feat: ( redirect ) add redirect url based on the domain name

This commit is contained in:
Abubeker Yasin
2026-07-14 14:16:48 +03:00
parent 1ace808b4a
commit ef5d643ba4
8 changed files with 179 additions and 11 deletions

View File

@@ -2,6 +2,7 @@ import {
Body,
Controller,
Get,
Headers,
HttpCode,
HttpStatus,
Post,
@@ -19,6 +20,7 @@ import type { TCurrentUser } from "@tria-plc/api-common/modules/auth/types/curre
import { IsPublic } from "@tria-plc/api-common/modules/auth/decorators/public.decorator";
import { JwtGuard } from "../../common/jwt.guard";
import { OptionalJwtGuard } from "./optional-jwt.guard";
import { resolveAllowedOrigin } from "../../common/utils/redirect-origin.util";
import {
CompleteVerificationResultDto,
StartVerificationDto,
@@ -68,12 +70,20 @@ export class VerifaydaController {
async start(
@Body() dto: StartVerificationDto,
@Req() req: RequestWithOptionalUser,
@Headers("origin") origin?: string,
@Headers("referer") referer?: string,
@Headers("x-frontend-base-url") frontendBaseUrl?: string,
): Promise<{ authorizationUrl: string }> {
const authorizationUrl = await this.service.startVerification({
purpose: dto.purpose ?? "VERIFY",
platform: dto.platform ?? "WEB",
userId: req.user?.id,
wantsPasswordSetup: dto.wantsPasswordSetup ?? false,
// WEB redirect_uri host follows the domain the user is on — Origin, then
// Referer, then the X-Frontend-Base-URL header — validated against the
// allowlist. Both domain variants must be registered with eSignet or the
// login is rejected.
requestOrigin: resolveAllowedOrigin(origin, referer, frontendBaseUrl),
});
return { authorizationUrl };
}

View File

@@ -18,6 +18,7 @@ import {
generateState,
} from './utils/pkce.util';
import { generateClientAssertion } from './utils/client-assertion.util';
import { rebaseUrlOrigin } from '../../common/utils/redirect-origin.util';
import { VerifaydaCallbackDto, VerificationStatusDto } from './verifayda.dto';
import {
FaydaTokenExchangeException,
@@ -49,6 +50,13 @@ export interface StartVerificationInput {
platform?: FaydaPlatform;
userId?: string; // iamUserId of the authenticated user, if any
wantsPasswordSetup?: boolean;
/**
* Allowlisted browser origin the request came from (e.g. https://bookingedr.et
* or https://passenger.edrsc.com), already validated by the controller. For
* WEB sessions the eSignet redirect_uri host is swapped to this so the user
* lands back on the same domain. Null/undefined keeps the configured default.
*/
requestOrigin?: string | null;
}
export interface FaydaUserSummary {
@@ -145,12 +153,26 @@ export class VerifaydaService {
Date.now() + this.faydaConfig.sessionTtlMinutes * 60_000,
);
const platform = input.platform ?? 'WEB';
// WEB sessions follow the domain the user came in on (bookingedr.et vs
// passenger.edrsc.com); MOBILE always uses the native base redirect_uri.
// Persist the exact URI so token exchange at /complete reuses it verbatim —
// eSignet requires the redirect_uri to match between authorize and token.
const redirectUri =
platform === 'MOBILE'
? this.redirectUriForPlatform('MOBILE')
: rebaseUrlOrigin(
this.faydaConfig.webRedirectUri,
input.requestOrigin ?? null,
) ?? this.faydaConfig.webRedirectUri;
await this.prisma.faydaVerificationSession.create({
data: {
state,
codeVerifier,
purpose: input.purpose,
platform: input.platform ?? 'WEB',
platform,
redirectUri,
saveToAccount: input.wantsPasswordSetup ?? false,
iamUserId: input.userId ?? null,
expiresAt,
@@ -158,13 +180,13 @@ export class VerifaydaService {
});
this.logger.log(
`Fayda verification started: purpose=${input.purpose} platform=${input.platform ?? 'WEB'} userId=${input.userId ?? 'none'}`,
`Fayda verification started: purpose=${input.purpose} platform=${platform} userId=${input.userId ?? 'none'} redirectUri=${redirectUri}`,
);
return this.buildAuthorizationUrl({
state,
codeChallenge,
redirectUri: this.redirectUriForPlatform(input.platform ?? 'WEB'),
redirectUri,
});
}
@@ -224,7 +246,11 @@ export class VerifaydaService {
const tokens = await this.exchangeCodeForTokens(
query.code,
session.codeVerifier,
this.redirectUriForPlatform(session.platform as FaydaPlatform),
// Reuse the exact redirect_uri sent at authorize (persisted on the
// session). Fall back to the platform default for sessions created
// before this column existed.
session.redirectUri ??
this.redirectUriForPlatform(session.platform as FaydaPlatform),
);
const userInfo = await this.fetchUserInfo(tokens.access_token);
const normalized = this.normalizeUserInfo(userInfo);