fix and update

This commit is contained in:
lendry
2026-06-29 16:23:40 +03:00
parent aebce54bd7
commit 10253fc76b
8 changed files with 797 additions and 106 deletions

View File

@@ -3,11 +3,8 @@ import { firstValueFrom } from 'rxjs';
import { CoreGrpcService } from '../core-grpc.service';
import { resolveFrontendUrl, resolveOAuthIssuer } from './oauth-issuer';
import {
hostsMatch,
normalizePublicBaseUrl,
preferBrowserReachableBase,
readRequestHost,
readRequestProto,
preferCanonicalBase,
resolvePublicApiBaseFromRequest,
resolvePublicFrontendBaseFromRequest
} from './public-url';
@@ -47,76 +44,17 @@ 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);
const frontendUrl = resolveSameOriginFrontend(storedFrontend, req);
const issuer = resolveSameOriginIssuer(storedIssuer, storedFrontend, req);
// ВАЖНО для FedCM: configUrl должен быть стабильным независимо от того, с какого
// хоста пришёл запрос. well-known запрашивается браузером всегда с eTLD+1
// (например https://idpmvk.lpr/.well-known/web-identity), а discover.json — с
// поддомена (https://sso.idpmvk.lpr/idp-api/...). Оба обязаны вернуть ОДИН и тот
// же configUrl, иначе браузер отвергнет провайдера. Поэтому берём канонический
// публичный URL из настроек, а не из текущего хоста запроса.
const issuer = preferCanonicalBase(storedIssuer, resolvePublicApiBaseFromRequest(req));
const frontendUrl = preferCanonicalBase(storedFrontend, resolvePublicFrontendBaseFromRequest(req));
let projectName = 'MVK ID';
try {

View File

@@ -94,6 +94,25 @@ export function resolvePublicFrontendBaseFromRequest(req?: Request): string | nu
return resolvePublicOriginFromRequest(req);
}
/**
* Возвращает канонический публичный base-URL для FedCM.
*
* FedCM требует, чтобы well-known файл (всегда запрашивается с eTLD+1, например
* idpmvk.lpr) и discover.json (запрашивается с поддомена sso.idpmvk.lpr)
* возвращали ОДИН и тот же configUrl. Поэтому здесь приоритет всегда у значения
* из настроек (PUBLIC_API_URL / PUBLIC_FRONTEND_URL), а на хост запроса
* переключаемся только если в настройках указан внутренний Docker-хост.
*/
export function preferCanonicalBase(stored: string, fromRequest: string | null) {
if (stored && isBrowserReachableBaseUrl(stored)) {
return normalizePublicBaseUrl(stored);
}
if (fromRequest && isBrowserReachableBaseUrl(fromRequest)) {
return normalizePublicBaseUrl(fromRequest);
}
return normalizePublicBaseUrl(stored);
}
export function preferBrowserReachableBase(stored: string, fromRequest: string | null) {
if (fromRequest && isBrowserReachableBaseUrl(fromRequest)) {
try {