fix and update
This commit is contained in:
@@ -1,13 +1,18 @@
|
||||
import type { Request } from 'express';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { CoreGrpcService } from '../core-grpc.service';
|
||||
import { resolveFrontendUrl, resolveOAuthIssuer } from './oauth-issuer';
|
||||
import {
|
||||
resolveFrontendUrl,
|
||||
resolveOAuthIssuer,
|
||||
resolveProjectDomainApiUrl,
|
||||
resolveProjectDomainFrontendUrl
|
||||
} from './oauth-issuer';
|
||||
import {
|
||||
isLocalDevBaseUrl,
|
||||
isValidFedcmEndpointUrl,
|
||||
normalizePublicBaseUrl,
|
||||
preferCanonicalBase,
|
||||
resolveFedcmWebIdentityOrigin,
|
||||
resolvePublicApiBaseFromRequest,
|
||||
resolvePublicFrontendBaseFromRequest
|
||||
pickBestPublicBase,
|
||||
resolveFedcmWebIdentityOrigin
|
||||
} from './public-url';
|
||||
|
||||
export interface FedcmEndpoints {
|
||||
@@ -39,17 +44,44 @@ function buildFedcmEndpointUrls(issuer: string, frontendUrl: string) {
|
||||
};
|
||||
}
|
||||
|
||||
export async function resolveFedcmEndpoints(core: CoreGrpcService, req?: Request): Promise<FedcmEndpoints> {
|
||||
const storedIssuer = await resolveOAuthIssuer(core);
|
||||
const storedFrontend = await resolveFrontendUrl(core);
|
||||
// ВАЖНО для 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));
|
||||
async function resolveCanonicalFedcmIssuer(core: CoreGrpcService): Promise<string> {
|
||||
const stored = await resolveOAuthIssuer(core);
|
||||
const fromProject = await resolveProjectDomainApiUrl(core);
|
||||
const issuer = pickBestPublicBase(
|
||||
[stored, fromProject],
|
||||
stored
|
||||
);
|
||||
if (isValidFedcmEndpointUrl(issuer)) {
|
||||
return issuer;
|
||||
}
|
||||
if (fromProject && isValidFedcmEndpointUrl(fromProject)) {
|
||||
return fromProject;
|
||||
}
|
||||
return issuer;
|
||||
}
|
||||
|
||||
async function resolveCanonicalFedcmFrontend(core: CoreGrpcService): Promise<string> {
|
||||
const stored = await resolveFrontendUrl(core);
|
||||
const fromProject = await resolveProjectDomainFrontendUrl(core);
|
||||
const frontend = pickBestPublicBase(
|
||||
[stored, fromProject],
|
||||
stored
|
||||
);
|
||||
if (isValidFedcmEndpointUrl(frontend) && !isLocalDevBaseUrl(frontend)) {
|
||||
return frontend;
|
||||
}
|
||||
if (fromProject && isValidFedcmEndpointUrl(fromProject)) {
|
||||
return fromProject;
|
||||
}
|
||||
return frontend;
|
||||
}
|
||||
|
||||
export async function resolveFedcmEndpoints(core: CoreGrpcService, _req?: Request): Promise<FedcmEndpoints> {
|
||||
// FedCM: web-identity (idpmvk.lpr) и config.json (api.idpmvk.lpr) ОБЯЗАНЫ
|
||||
// возвращать одинаковые login_url / accounts_endpoint / configUrl.
|
||||
// Хост запроса разный — никогда не выводим URL из req.headers.host.
|
||||
const issuer = await resolveCanonicalFedcmIssuer(core);
|
||||
const frontendUrl = await resolveCanonicalFedcmFrontend(core);
|
||||
let projectName = 'MVK ID';
|
||||
|
||||
try {
|
||||
|
||||
@@ -51,7 +51,8 @@ export async function resolveOAuthIssuer(core: CoreGrpcService, fallback = 'http
|
||||
}
|
||||
|
||||
const fromDomain = await resolveProjectDomainUrl(core, true);
|
||||
return pickBestPublicBase([fromDb, envIssuer, fromDomain], fallback);
|
||||
// env первым: в Docker .env перекрывает seed localhost в БД после первого деплоя.
|
||||
return pickBestPublicBase([envIssuer, fromDb, fromDomain], fallback);
|
||||
}
|
||||
|
||||
export async function resolveFrontendUrl(core: CoreGrpcService, fallback = 'http://localhost:3002'): Promise<string> {
|
||||
@@ -65,7 +66,15 @@ export async function resolveFrontendUrl(core: CoreGrpcService, fallback = 'http
|
||||
}
|
||||
|
||||
const fromDomain = await resolveProjectDomainUrl(core, false);
|
||||
return pickBestPublicBase([fromDb, envFrontend, fromDomain], fallback);
|
||||
return pickBestPublicBase([envFrontend, fromDb, fromDomain], fallback);
|
||||
}
|
||||
|
||||
export async function resolveProjectDomainFrontendUrl(core: CoreGrpcService): Promise<string | undefined> {
|
||||
return resolveProjectDomainUrl(core, false);
|
||||
}
|
||||
|
||||
export async function resolveProjectDomainApiUrl(core: CoreGrpcService): Promise<string | undefined> {
|
||||
return resolveProjectDomainUrl(core, true);
|
||||
}
|
||||
|
||||
export function buildOpenIdConfiguration(issuer: string) {
|
||||
|
||||
@@ -47,6 +47,25 @@ export function isBrowserReachableBaseUrl(url: string) {
|
||||
}
|
||||
}
|
||||
|
||||
/** Абсолютный HTTPS (или localhost HTTP) URL для FedCM login_url / configUrl. */
|
||||
export function isValidFedcmEndpointUrl(url: string) {
|
||||
try {
|
||||
const parsed = new URL(url);
|
||||
if (!['http:', 'https:'].includes(parsed.protocol)) {
|
||||
return false;
|
||||
}
|
||||
if (isInternalHostname(parsed.hostname)) {
|
||||
return false;
|
||||
}
|
||||
if (isLocalDevBaseUrl(url)) {
|
||||
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
|
||||
}
|
||||
return parsed.protocol === 'https:';
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function readRequestHost(req?: Request): string | undefined {
|
||||
if (!req) {
|
||||
return undefined;
|
||||
|
||||
Reference in New Issue
Block a user