diff --git a/.env.example b/.env.example index 701e90b..6af26bb 100644 --- a/.env.example +++ b/.env.example @@ -19,7 +19,9 @@ SSL_CERT_DIR= SSL_CERT_FULLCHAIN= SSL_CERT_KEY= -# Домены без протокола (install.sh подставит PUBLIC_* URL) +# Доп. DNS-имена в self-signed сертификат (сайты приложений в LAN через запятую) +# EXTRA_SSL_SANS=myapp.lan,portal.company.lpr + DOMAIN_API=api.idpmvk.lpr DOMAIN_FRONTEND=sso.idpmvk.lpr DOMAIN_DOCS= diff --git a/apps/api-gateway/src/lib/fedcm-config.ts b/apps/api-gateway/src/lib/fedcm-config.ts index c0cb5d6..510275f 100644 --- a/apps/api-gateway/src/lib/fedcm-config.ts +++ b/apps/api-gateway/src/lib/fedcm-config.ts @@ -5,6 +5,7 @@ import { resolveFrontendUrl, resolveOAuthIssuer } from './oauth-issuer'; import { normalizePublicBaseUrl, preferCanonicalBase, + resolveFedcmWebIdentityOrigin, resolvePublicApiBaseFromRequest, resolvePublicFrontendBaseFromRequest } from './public-url'; @@ -25,13 +26,7 @@ export interface FedcmEndpoints { function buildFedcmEndpointUrls(issuer: string, frontendUrl: string) { const base = normalizePublicBaseUrl(issuer); const front = normalizePublicBaseUrl(frontendUrl); - - let webIdentityOrigin = front; - try { - webIdentityOrigin = new URL(base).origin; - } catch { - // keep frontend origin - } + const webIdentityOrigin = resolveFedcmWebIdentityOrigin(base, front); return { configUrl: `${base}/fedcm/config.json`, diff --git a/apps/api-gateway/src/lib/public-url.ts b/apps/api-gateway/src/lib/public-url.ts index f04da24..d55ae50 100644 --- a/apps/api-gateway/src/lib/public-url.ts +++ b/apps/api-gateway/src/lib/public-url.ts @@ -139,3 +139,26 @@ export function hostsMatch(a: string, b: string) { return a === 'localhost' && b === 'localhost'; } + +/** eTLD+1 для FedCM (sso.idpmvk.lpr → idpmvk.lpr). Совпадает с registrable_domain в install.sh */ +export function resolveRegistrableDomain(hostname: string): string { + const host = hostname.trim().toLowerCase(); + if (!host) return host; + const labels = host.split('.').filter(Boolean); + if (labels.length <= 2) return host; + return `${labels[labels.length - 2]}.${labels[labels.length - 1]}`; +} + +export function resolveFedcmWebIdentityOrigin(issuer: string, frontendUrl: string): string { + for (const candidate of [issuer, frontendUrl]) { + try { + const hostname = new URL(candidate).hostname; + const apex = resolveRegistrableDomain(hostname); + const proto = new URL(candidate).protocol; + return `${proto}//${apex}`; + } catch { + // try next + } + } + return frontendUrl; +} diff --git a/install.sh b/install.sh index ce94017..96ae756 100644 --- a/install.sh +++ b/install.sh @@ -1626,6 +1626,19 @@ collect_self_signed_sans() { sans+=("DNS:${apex}") fi + # Дополнительные SAN для сторонних приложений в LAN (через запятую в .env). + local extra_sans="${EXTRA_SSL_SANS:-}" + if [[ -n "$extra_sans" ]]; then + local entry + IFS=',' read -ra _extra_sans <<< "$extra_sans" + for entry in "${_extra_sans[@]}"; do + entry="${entry#"${entry%%[![:space:]]*}"}" + entry="${entry%"${entry##*[![:space:]]}"}" + [[ -z "$entry" ]] && continue + sans+=("DNS:${entry}") + done + fi + (IFS=,; echo "${sans[*]}") } @@ -2760,9 +2773,15 @@ print_intranet_hint() { fi echo "" if [[ "$(env_get SSL_TYPE none)" == "selfsigned" ]]; then - echo " Сертификат: ${LOCAL_CERTS_DIR}/shared-intranet (общий для всех доменов)" - echo " Браузер покажет предупреждение — примите сертификат для ${DOMAIN_FRONTEND}." - echo " API вызывается через ${DOMAIN_FRONTEND}/idp-api — отдельно открывать ${DOMAIN_API} не нужно." + echo " Сертификат: ${LOCAL_CERTS_DIR}/shared-intranet (общий для всех доменов IdP)" + echo " FedCM/One Tap: браузер открывает ${apex_domain:-корневой домен}/.well-known/web-identity —" + echo " примите сертификат для ${DOMAIN_FRONTEND}, ${DOMAIN_API}${extra_apex} и сайта вашего приложения." + echo " Надёжно: импортируйте fullchain.pem в «Доверенные корневые центры» на каждом ПК (Windows/macOS)." + if [[ -n "$(env_get EXTRA_SSL_SANS "")" ]]; then + echo " EXTRA_SSL_SANS: $(env_get EXTRA_SSL_SANS "")" + else + echo " Для другого домена приложения добавьте в .env: EXTRA_SSL_SANS=myapp.lan и ./install.sh --fix-all" + fi echo "" fi if [[ "$(env_get LDAP_USE_HOST_NETWORK false)" == "true" ]]; then