71 lines
2.1 KiB
TypeScript
71 lines
2.1 KiB
TypeScript
import { BadRequestException } from '@nestjs/common';
|
|
import type { Request, Response } from 'express';
|
|
|
|
export function normalizeFedcmOrigin(origin?: string) {
|
|
return origin?.trim().replace(/\/+$/, '') || undefined;
|
|
}
|
|
|
|
export function applyFedcmCorsHeaders(res: Response, origin?: string) {
|
|
const normalized = normalizeFedcmOrigin(origin);
|
|
if (!normalized) {
|
|
return;
|
|
}
|
|
res.setHeader('Access-Control-Allow-Origin', normalized);
|
|
res.setHeader('Access-Control-Allow-Credentials', 'true');
|
|
res.setHeader('Vary', 'Origin');
|
|
}
|
|
|
|
export function requireFedcmCorsOrigin(origin?: string) {
|
|
const normalized = normalizeFedcmOrigin(origin);
|
|
if (!normalized) {
|
|
throw new BadRequestException('FedCM требует заголовок Origin');
|
|
}
|
|
return normalized;
|
|
}
|
|
|
|
export function applyFedcmPreflightHeaders(res: Response, origin?: string) {
|
|
applyFedcmCorsHeaders(res, origin);
|
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
res.setHeader(
|
|
'Access-Control-Allow-Headers',
|
|
'Content-Type, Authorization, Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site'
|
|
);
|
|
res.setHeader('Access-Control-Max-Age', '86400');
|
|
}
|
|
|
|
/**
|
|
* FedCM-запросы должны иметь Sec-Fetch-Dest: webidentity (или empty, если прокси
|
|
* не пробрасывает заголовок). Блокируем только явно «не-FedCM» dest (document,
|
|
* script, image…) — иначе well-known/config отдают 400 и One Tap ломается.
|
|
*/
|
|
const BLOCKED_FEDCM_FETCH_DEST = new Set([
|
|
'document',
|
|
'embed',
|
|
'frame',
|
|
'iframe',
|
|
'object',
|
|
'script',
|
|
'style',
|
|
'image',
|
|
'font',
|
|
'audio',
|
|
'video',
|
|
'track',
|
|
'worker',
|
|
'serviceworker',
|
|
'sharedworker',
|
|
'manifest',
|
|
'xslt'
|
|
]);
|
|
|
|
export function assertFedcmWebIdentityRequest(req: Request) {
|
|
const dest = String(req.headers['sec-fetch-dest'] ?? '').toLowerCase();
|
|
if (dest && BLOCKED_FEDCM_FETCH_DEST.has(dest)) {
|
|
throw new BadRequestException('Недопустимый Sec-Fetch-Dest для FedCM');
|
|
}
|
|
}
|
|
|
|
export function applyFedcmLoginStatus(res: Response, loggedIn: boolean) {
|
|
res.setHeader('Set-Login', loggedIn ? 'logged-in' : 'logged-out');
|
|
}
|