fix and update

This commit is contained in:
lendry
2026-06-29 15:02:01 +03:00
parent 0df7240dc8
commit 01e4917acf
10 changed files with 326 additions and 132 deletions

View File

@@ -2,6 +2,15 @@ import type { Request } from 'express';
import { firstValueFrom } from 'rxjs';
import { CoreGrpcService } from '../core-grpc.service';
import { resolveFrontendUrl, resolveOAuthIssuer } from './oauth-issuer';
import {
hostsMatch,
normalizePublicBaseUrl,
preferBrowserReachableBase,
readRequestHost,
readRequestProto,
resolvePublicApiBaseFromRequest,
resolvePublicFrontendBaseFromRequest
} from './public-url';
export interface FedcmEndpoints {
issuer: string;
@@ -16,94 +25,9 @@ export interface FedcmEndpoints {
webIdentityUrl: string;
}
function normalizeBaseUrl(url: string) {
return url.trim().replace(/\/+$/, '');
}
function readRequestHost(req?: Request): string | undefined {
if (!req) {
return undefined;
}
const forwarded = req.headers['x-forwarded-host'];
const host = (Array.isArray(forwarded) ? forwarded[0] : forwarded) ?? req.headers.host;
return host?.split(',')[0]?.trim() || undefined;
}
function readRequestProto(req?: Request): string {
if (!req) {
return 'https';
}
const forwarded = req.headers['x-forwarded-proto'];
const proto = (Array.isArray(forwarded) ? forwarded[0] : forwarded) ?? req.protocol;
return proto?.split(',')[0]?.trim() || 'https';
}
function hostsMatch(a: string, b: string) {
if (a === b) {
return true;
}
return a === 'localhost' && b === 'localhost';
}
function resolveSameOriginFrontend(storedFrontend: string, req?: Request): string {
const host = readRequestHost(req);
if (!host) {
return storedFrontend;
}
const requestOrigin = normalizeBaseUrl(`${readRequestProto(req)}://${host}`);
try {
const frontendUrl = new URL(storedFrontend);
const requestUrl = new URL(requestOrigin);
if (hostsMatch(frontendUrl.hostname, requestUrl.hostname)) {
return requestOrigin;
}
} catch {
return storedFrontend;
}
return storedFrontend;
}
export function resolveSameOriginIssuer(storedIssuer: string, storedFrontend: string, req?: Request): string {
const host = readRequestHost(req);
if (!host) {
return storedIssuer;
}
const requestOrigin = normalizeBaseUrl(`${readRequestProto(req)}://${host}`);
const sameOriginApi = `${requestOrigin}/idp-api`;
try {
const issuerUrl = new URL(storedIssuer);
const frontendUrl = new URL(storedFrontend);
const requestUrl = new URL(requestOrigin);
const hostMatches =
hostsMatch(issuerUrl.hostname, requestUrl.hostname) || hostsMatch(frontendUrl.hostname, requestUrl.hostname);
if (!hostMatches) {
return storedIssuer;
}
if (normalizeBaseUrl(storedIssuer) !== sameOriginApi) {
return sameOriginApi;
}
} catch {
return storedIssuer;
}
return storedIssuer;
}
function buildFedcmEndpointUrls(issuer: string, frontendUrl: string) {
const base = normalizeBaseUrl(issuer);
const front = normalizeBaseUrl(frontendUrl);
const base = normalizePublicBaseUrl(issuer);
const front = normalizePublicBaseUrl(frontendUrl);
let webIdentityOrigin = front;
try {
@@ -123,6 +47,71 @@ function buildFedcmEndpointUrls(issuer: string, frontendUrl: string) {
};
}
function resolveSameOriginFrontend(storedFrontend: string, req?: Request): string {
const fromRequest = resolvePublicFrontendBaseFromRequest(req);
const preferred = preferBrowserReachableBase(storedFrontend, fromRequest);
if (preferred !== storedFrontend) {
return preferred;
}
const host = readRequestHost(req);
if (!host) {
return storedFrontend;
}
const requestOrigin = normalizePublicBaseUrl(`${readRequestProto(req)}://${host}`);
try {
const frontendUrl = new URL(storedFrontend);
const requestUrl = new URL(requestOrigin);
if (hostsMatch(frontendUrl.hostname, requestUrl.hostname)) {
return requestOrigin;
}
} catch {
return storedFrontend;
}
return storedFrontend;
}
export function resolveSameOriginIssuer(storedIssuer: string, storedFrontend: string, req?: Request): string {
const fromRequest = resolvePublicApiBaseFromRequest(req);
const preferred = preferBrowserReachableBase(storedIssuer, fromRequest);
if (preferred !== storedIssuer) {
return preferred;
}
const host = readRequestHost(req);
if (!host) {
return storedIssuer;
}
const requestOrigin = normalizePublicBaseUrl(`${readRequestProto(req)}://${host}`);
const sameOriginApi = `${requestOrigin}/idp-api`;
try {
const issuerUrl = new URL(storedIssuer);
const frontendUrl = new URL(storedFrontend);
const requestUrl = new URL(requestOrigin);
const hostMatches =
hostsMatch(issuerUrl.hostname, requestUrl.hostname) || hostsMatch(frontendUrl.hostname, requestUrl.hostname);
if (!hostMatches) {
return storedIssuer;
}
if (normalizePublicBaseUrl(storedIssuer) !== sameOriginApi) {
return sameOriginApi;
}
} catch {
return fromRequest ?? storedIssuer;
}
return storedIssuer;
}
export async function resolveFedcmEndpoints(core: CoreGrpcService, req?: Request): Promise<FedcmEndpoints> {
const storedIssuer = await resolveOAuthIssuer(core);
const storedFrontend = await resolveFrontendUrl(core);