@vritti/api-sdk 0.3.11 → 0.3.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth.cjs +193 -78
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +84 -2
- package/dist/auth.d.ts +84 -2
- package/dist/auth.js +114 -6
- package/dist/auth.js.map +1 -1
- package/dist/catalog-resolver.cjs +22 -6
- package/dist/catalog-resolver.cjs.map +1 -1
- package/dist/catalog-resolver.d.cts +11 -3
- package/dist/catalog-resolver.d.ts +11 -3
- package/dist/catalog-resolver.js +22 -6
- package/dist/catalog-resolver.js.map +1 -1
- package/dist/index.d.cts +34 -0
- package/dist/index.d.ts +34 -0
- package/dist/license.d.cts +1 -1
- package/dist/license.d.ts +1 -1
- package/dist/request-CDllp7xb.d.cts +111 -0
- package/dist/request-CDllp7xb.d.ts +111 -0
- package/dist/signing.cjs +18 -2
- package/dist/signing.cjs.map +1 -1
- package/dist/signing.d.cts +2 -31
- package/dist/signing.d.ts +2 -31
- package/dist/signing.js +18 -2
- package/dist/signing.js.map +1 -1
- package/dist/{types-BBK6MG-U.d.cts → types-CG-Ynwyf.d.cts} +14 -1
- package/dist/{types-BBK6MG-U.d.ts → types-CG-Ynwyf.d.ts} +14 -1
- package/package.json +1 -1
package/dist/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/auth/auth.config.ts","../src/auth/auth-config.module.ts","../src/request/request.module.ts","../src/request/services/request.service.ts","../src/context/resolve-request.ts","../src/auth/guards/vritti-auth.guard.ts","../src/context/extractors/graphql.extractor.ts","../src/context/extractors/http.extractor.ts","../src/context/context.registry.ts","../src/context/get-request.ts","../src/context/get-response.ts","../src/auth/decorators/require-session.decorator.ts","../src/auth/decorators/skip-csrf.decorator.ts","../src/auth/services/token.service.ts","../src/utils/time.utils.ts","../src/auth/utils/token-hash.util.ts","../src/auth/decorators/access-token.decorator.ts","../src/auth/decorators/client-ip.decorator.ts","../src/auth/decorators/cookie-domain.decorator.ts","../src/auth/decorators/cookie-name.decorator.ts","../src/auth/decorators/hostname.decorator.ts","../src/auth/decorators/public.decorator.ts","../src/auth/decorators/refresh-cookie-options.decorator.ts","../src/exceptions/bad-gateway.exception.ts","../src/exceptions/base-field.exception.ts","../src/exceptions/bad-request.exception.ts","../src/exceptions/conflict.exception.ts","../src/exceptions/forbidden.exception.ts","../src/exceptions/gone.exception.ts","../src/exceptions/internal-server-error.exception.ts","../src/exceptions/method-not-allowed.exception.ts","../src/exceptions/not-acceptable.exception.ts","../src/exceptions/not-found.exception.ts","../src/exceptions/not-implemented.exception.ts","../src/exceptions/payload-too-large.exception.ts","../src/exceptions/request-timeout.exception.ts","../src/exceptions/service-unavailable.exception.ts","../src/exceptions/too-many-requests.exception.ts","../src/exceptions/unauthorized.exception.ts","../src/exceptions/unprocessable-entity.exception.ts","../src/exceptions/unsupported-media-type.exception.ts","../src/exceptions/validation.exception.ts","../src/auth/decorators/refresh-token-cookie.decorator.ts","../src/auth/decorators/session-data.decorator.ts","../src/auth/decorators/subdomain.decorator.ts","../src/auth/decorators/user-agent.decorator.ts","../src/auth/decorators/user-id.decorator.ts"],"sourcesContent":["import type { FastifyRequest } from 'fastify';\nimport type { RequestService } from '../request/services/request.service';\n\nexport const AUTH_CONFIG = Symbol('AUTH_CONFIG');\n\nexport type OnAuthenticatedCallback = (\n requestService: RequestService,\n sessionInfo: NonNullable<FastifyRequest['sessionInfo']>,\n) => void | Promise<void>;\n\nexport type TokenExpiryString = `${number}${'s' | 'm' | 'h' | 'd' | 'w' | 'y'}`;\n\nexport interface TokenExpiry {\n access: TokenExpiryString;\n refresh: TokenExpiryString;\n}\n\nexport interface CookieConfig {\n refreshCookieName: string;\n refreshCookieMaxAge: number;\n refreshCookiePath: string;\n refreshCookieSecure: boolean;\n refreshCookieSameSite: 'strict' | 'lax' | 'none';\n refreshCookieDomain?: string;\n}\n\nexport interface GuardConfig {\n authHeaderName: string;\n tokenPrefix: string;\n csrfExemptSessionTypes?: string[];\n csrfExemptTransports?: string[];\n refreshTokenBindingExemptSessionTypes?: string[];\n onAuthenticated?: OnAuthenticatedCallback;\n}\n\nexport interface AuthConfig {\n tokenExpiry: TokenExpiry;\n cookie: CookieConfig;\n guard: GuardConfig;\n}\n\nexport const AUTH_CONFIG_DEFAULTS = {\n cookie: {\n refreshCookieName: 'vritti_refresh',\n refreshCookieMaxAge: 30 * 24 * 60 * 60 * 1000, // 30 days\n refreshCookiePath: '/',\n refreshCookieSecure: process.env.NODE_ENV === 'production',\n refreshCookieSameSite: 'strict' as const,\n refreshCookieDomain: 'localhost',\n },\n guard: {\n authHeaderName: 'authorization',\n tokenPrefix: 'Bearer',\n csrfExemptSessionTypes: [],\n csrfExemptTransports: [],\n refreshTokenBindingExemptSessionTypes: [],\n },\n} satisfies Omit<AuthConfig, 'tokenExpiry'>;\n\nexport interface CookieSerializeOptions {\n httpOnly: boolean;\n secure: boolean;\n sameSite: 'strict' | 'lax' | 'none';\n path: string;\n maxAge: number;\n domain: string;\n}\n\nexport enum TokenType {\n ACCESS = 'access',\n REFRESH = 'refresh',\n}\n\ninterface JwtClaims {\n exp: number;\n iat: number;\n}\n\nexport interface AccessTokenPayload {\n sessionType: string;\n tokenType: TokenType.ACCESS;\n userId: string;\n sessionId: string;\n refreshTokenHash: string;\n}\n\nexport type DecodedAccessToken = AccessTokenPayload & JwtClaims;\n\nexport interface RefreshTokenPayload {\n sessionType: string;\n tokenType: TokenType.REFRESH;\n userId: string;\n sessionId: string;\n}\n\nexport type DecodedRefreshToken = RefreshTokenPayload & JwtClaims;\n","import { type DynamicModule, Global, type InjectionToken, Module } from '@nestjs/common';\nimport { ConfigModule, ConfigService } from '@nestjs/config';\nimport { APP_GUARD, Reflector } from '@nestjs/core';\nimport { JwtModule } from '@nestjs/jwt';\nimport { RequestModule } from '../request/request.module';\nimport {\n AUTH_CONFIG,\n AUTH_CONFIG_DEFAULTS,\n type AuthConfig,\n type CookieConfig,\n type GuardConfig,\n type TokenExpiry,\n} from './auth.config';\nimport { VrittiAuthGuard } from './guards/vritti-auth.guard';\nimport { TokenService } from './services/token.service';\n\n// Factory return type — tokenExpiry is required, cookie and guard are partial with defaults merged\ninterface AuthConfigInput {\n tokenExpiry: TokenExpiry;\n cookie?: Partial<CookieConfig>;\n guard?: Partial<GuardConfig>;\n}\n\ninterface AuthConfigModuleOptions<T extends unknown[] = unknown[]> {\n useFactory: (...args: [...T]) => AuthConfigInput | Promise<AuthConfigInput>;\n inject?: InjectionToken[];\n}\n\n// Merges user-provided partial config with defaults to produce a complete AuthConfig\nfunction mergeWithDefaults(input: AuthConfigInput): AuthConfig {\n return {\n tokenExpiry: input.tokenExpiry,\n cookie: {\n ...AUTH_CONFIG_DEFAULTS.cookie,\n ...(input.cookie ?? {}),\n },\n guard: {\n ...AUTH_CONFIG_DEFAULTS.guard,\n ...(input.guard ?? {}),\n },\n };\n}\n\n@Global()\n@Module({})\nexport class AuthConfigModule {\n // Registers JWT, TokenService, and global VrittiAuthGuard\n static forRootAsync<T extends unknown[] = unknown[]>(options: AuthConfigModuleOptions<T>): DynamicModule {\n return {\n module: AuthConfigModule,\n imports: [\n ConfigModule,\n RequestModule,\n JwtModule.registerAsync({\n imports: [ConfigModule],\n inject: [ConfigService],\n useFactory: (config: ConfigService) => ({\n secret: config.getOrThrow<string>('JWT_SECRET'),\n signOptions: { algorithm: 'HS256' as const },\n }),\n }),\n ],\n providers: [\n {\n provide: Reflector,\n useClass: Reflector,\n },\n {\n provide: APP_GUARD,\n useClass: VrittiAuthGuard,\n },\n {\n provide: AUTH_CONFIG,\n useFactory: async (...args: unknown[]) => {\n const input = await options.useFactory(...(args as [...T]));\n return mergeWithDefaults(input);\n },\n inject: options.inject || [],\n },\n TokenService,\n ],\n exports: [JwtModule, TokenService, AUTH_CONFIG],\n };\n }\n}\n","import { Global, Module } from '@nestjs/common';\nimport { RequestService } from './services/request.service';\n\n@Global()\n@Module({\n providers: [RequestService],\n exports: [RequestService],\n})\nexport class RequestModule {}\n","import { Inject, Injectable, Scope } from '@nestjs/common';\nimport { REQUEST } from '@nestjs/core';\nimport type { FastifyRequest } from 'fastify';\nimport { AUTH_CONFIG, type AuthConfig } from '../../auth/auth.config';\nimport { resolveInjectedRequest } from '../../context/resolve-request';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class RequestService {\n constructor(\n @Inject(REQUEST) private readonly injectedRequest: FastifyRequest,\n @Inject(AUTH_CONFIG) private readonly config: AuthConfig,\n ) {}\n\n // Unwraps the GraphQL { req, reply } context wrapper so every accessor below works across both transports\n private get request(): FastifyRequest {\n return resolveInjectedRequest(this.injectedRequest);\n }\n\n // Extracts the bearer access token from the Authorization header\n getAccessToken(): string | null {\n const authHeader = this.request.headers?.[this.config.guard.authHeaderName];\n if (!authHeader || typeof authHeader !== 'string') {\n return null;\n }\n const [type, token] = authHeader.split(' ') ?? [];\n return type === this.config.guard.tokenPrefix && token ? token : null;\n }\n\n // Extracts the refresh token from the configured httpOnly cookie\n getRefreshToken(): string | null {\n try {\n const cookies = (this.request as unknown as { cookies?: Record<string, string> }).cookies;\n if (cookies && typeof cookies === 'object') {\n const refreshToken = cookies[this.config.cookie.refreshCookieName];\n if (refreshToken) {\n return refreshToken;\n }\n }\n return null;\n } catch (_error: unknown) {\n return null;\n }\n }\n\n // Returns the value of a specific request header by key\n getHeader(key: string): string | string[] | undefined {\n return this.request.headers?.[key];\n }\n\n // Returns the request hostname (without port)\n getHostname(): string {\n return this.request.hostname ?? '';\n }\n\n // Returns all request headers\n getAllHeaders(): FastifyRequest['headers'] {\n return this.request.headers || {};\n }\n}\n","import type { FastifyRequest } from 'fastify';\n\n// Unwraps the @Inject(REQUEST) value to the real Fastify request (GraphQL injects a { req, reply } context).\nexport function resolveInjectedRequest(injected: FastifyRequest): FastifyRequest {\n const candidate = injected as unknown as { headers?: unknown; req?: FastifyRequest };\n if (candidate && candidate.headers === undefined && candidate.req) {\n return candidate.req;\n }\n return injected;\n}\n","import {\n type CanActivate,\n type ExecutionContext,\n ForbiddenException,\n Inject,\n Injectable,\n Logger,\n Scope,\n UnauthorizedException,\n} from '@nestjs/common';\nimport { SSE_METADATA } from '@nestjs/common/constants';\nimport { Reflector } from '@nestjs/core';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport '../../types/fastify-augmentation';\nimport { getRequestFromContext, getResponseFromContext } from '../../context';\nimport { RequestService } from '../../request/services/request.service';\nimport { AUTH_CONFIG, type AuthConfig } from '../auth.config';\nimport { REQUIRE_SESSION_KEY } from '../decorators/require-session.decorator';\nimport { SKIP_CSRF_KEY } from '../decorators/skip-csrf.decorator';\nimport { TokenService } from '../services/token.service';\n\ninterface FastifyInstanceWithCsrf {\n csrfProtection?: (req: FastifyRequest, reply: FastifyReply, next: (err?: Error) => void) => void;\n}\n\ntype PatchableReply = { send: (...args: unknown[]) => unknown };\n\n@Injectable({ scope: Scope.REQUEST })\nexport class VrittiAuthGuard implements CanActivate {\n private readonly logger = new Logger(VrittiAuthGuard.name);\n\n constructor(\n private readonly reflector: Reflector,\n private readonly requestService: RequestService,\n private readonly tokenService: TokenService,\n @Inject(AUTH_CONFIG) private readonly config: AuthConfig,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = getRequestFromContext(context);\n const reply = getResponseFromContext(context);\n const route = `${request.method} ${request.url}`;\n\n // Attach auth config to request so decorators can access it without injection\n request.authConfig = this.config;\n\n // CSRF is skipped via @SkipCsrf() or when the request transport is CSRF-exempt (e.g. 'graphql')\n const csrfExemptTransports = this.config.guard.csrfExemptTransports ?? [];\n const skipCsrf =\n this.reflector.getAllAndOverride<boolean>(SKIP_CSRF_KEY, [context.getHandler(), context.getClass()]) ||\n csrfExemptTransports.includes(context.getType<string>());\n\n // @Public() endpoints skip auth, while preserving their current CSRF behavior\n const isPublic = this.reflector.getAllAndOverride<boolean>('isPublic', [context.getHandler(), context.getClass()]);\n if (isPublic) {\n if (!skipCsrf) {\n await this.validateCsrf(request, reply);\n }\n this.logger.debug(`${route} — public endpoint, skipping auth`);\n return true;\n }\n\n // @RequireSession() restricts access to specific session types\n const requiredSessionTypes = this.reflector.getAllAndOverride<string[]>(REQUIRE_SESSION_KEY, [\n context.getHandler(),\n context.getClass(),\n ]);\n\n // SSE endpoints authenticate via refresh token cookie (EventSource cannot send Authorization headers)\n const isSseEndpoint = this.reflector.get<boolean>(SSE_METADATA, context.getHandler());\n if (isSseEndpoint) {\n this.logger.debug(`${route} — SSE endpoint, authenticating via refresh cookie`);\n return this.handleSseAuth(request, requiredSessionTypes);\n }\n\n const sessionType = await this.handleHttpAuth(request, requiredSessionTypes);\n\n const csrfExemptSessionTypes = this.config.guard.csrfExemptSessionTypes ?? [];\n if (!skipCsrf && !csrfExemptSessionTypes.includes(sessionType)) {\n await this.validateCsrf(request, reply);\n }\n\n return true;\n }\n\n // Authenticates standard HTTP requests using the access token from Authorization header\n private async handleHttpAuth(request: FastifyRequest, requiredSessionTypes?: string[]): Promise<string> {\n const route = `${request.method} ${request.url}`;\n\n const accessToken = this.requestService.getAccessToken();\n if (!accessToken) {\n this.logger.warn(`${route} — no access token found`);\n throw new UnauthorizedException('Access token not found');\n }\n\n const decoded = this.tokenService.validateAccessToken(accessToken);\n\n const refreshTokenBindingExemptSessionTypes = this.config.guard.refreshTokenBindingExemptSessionTypes ?? [];\n\n if (!refreshTokenBindingExemptSessionTypes.includes(decoded.sessionType)) {\n const refreshToken = this.requestService.getRefreshToken();\n if (!refreshToken) {\n throw new UnauthorizedException('Session validation failed');\n }\n this.tokenService.validateTokenBinding(decoded, refreshToken);\n }\n\n // Validate session type access (only if @RequireSession specifies types)\n if (requiredSessionTypes?.length && !requiredSessionTypes.includes(decoded.sessionType)) {\n this.logger.warn(\n `${route} — session type ${decoded.sessionType} not in allowed: [${requiredSessionTypes.join(', ')}]`,\n );\n throw new UnauthorizedException(`${decoded.sessionType} sessions cannot access this endpoint`);\n }\n\n // Attach session info to request — spread full decoded token (includes metadata fields)\n const { tokenType: _tokenType, refreshTokenHash: _hash, exp: _exp, iat: _iat, ...sessionInfo } = decoded;\n request.sessionInfo = sessionInfo;\n\n // Call onAuthenticated callback if configured\n const onAuthenticated = this.config.guard.onAuthenticated;\n if (onAuthenticated) {\n await onAuthenticated(this.requestService, request.sessionInfo);\n }\n\n this.logger.debug(`${route} — authenticated user: ${decoded.userId} (${decoded.sessionType})`);\n return decoded.sessionType;\n }\n\n // Authenticates SSE connections using the refresh token httpOnly cookie\n private handleSseAuth(request: FastifyRequest, requiredSessionTypes?: string[]): boolean {\n const refreshToken = this.requestService.getRefreshToken();\n if (!refreshToken) {\n this.logger.warn(`SSE ${request.url} — no refresh token cookie`);\n throw new UnauthorizedException('Authentication required');\n }\n\n const decoded = this.tokenService.validateRefreshToken(refreshToken);\n\n if (requiredSessionTypes?.length && !requiredSessionTypes.includes(decoded.sessionType)) {\n this.logger.warn(`SSE ${request.url} — session type ${decoded.sessionType} not allowed`);\n throw new UnauthorizedException(`${decoded.sessionType} sessions cannot access this endpoint`);\n }\n\n const { tokenType: _tokenType, exp: _exp, iat: _iat, ...sessionInfo } = decoded;\n request.sessionInfo = sessionInfo;\n\n this.logger.debug(`SSE ${request.url} — authenticated user: ${decoded.userId} (${decoded.sessionType})`);\n return true;\n }\n\n // Validates CSRF token for state-changing requests\n private async validateCsrf(request: FastifyRequest, reply: FastifyReply): Promise<void> {\n const safeMethods = ['GET', 'HEAD', 'OPTIONS'];\n if (safeMethods.includes(request.method)) return;\n\n try {\n const fastifyInstance = request.server as unknown as FastifyInstanceWithCsrf;\n const csrfProtection = fastifyInstance.csrfProtection;\n if (!csrfProtection) {\n throw new ForbiddenException('CSRF protection not configured');\n }\n\n await new Promise<void>((resolve, reject) => {\n const originalSend = reply.send.bind(reply);\n (reply as PatchableReply).send = () => {\n (reply as PatchableReply).send = originalSend as PatchableReply['send'];\n reject(new Error('CSRF validation failed'));\n return reply;\n };\n\n csrfProtection(request, reply, (err?: Error) => {\n (reply as PatchableReply).send = originalSend as PatchableReply['send'];\n if (err) reject(err);\n else resolve();\n });\n });\n } catch (_error: unknown) {\n this.logger.warn(`${request.method} ${request.url} — CSRF validation failed`);\n throw new ForbiddenException({\n errors: [{ field: 'csrf', message: 'Invalid or missing CSRF token' }],\n message: 'CSRF validation failed',\n });\n }\n }\n}\n","import type { ArgumentsHost } from '@nestjs/common';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { RequestExtractor } from '../context.types';\n\ninterface GqlContext {\n req: FastifyRequest;\n reply?: FastifyReply;\n}\n\nexport const graphqlExtractor: RequestExtractor = {\n getRequest: (host: ArgumentsHost) => host.getArgByIndex<GqlContext>(2).req,\n getResponse: (host: ArgumentsHost) => host.getArgByIndex<GqlContext>(2).reply as FastifyReply,\n};\n","import type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { RequestExtractor } from '../context.types';\n\n// Default transport: standard HTTP via the Fastify adapter.\nexport const httpExtractor: RequestExtractor = {\n getRequest: (host) => host.switchToHttp().getRequest<FastifyRequest>(),\n getResponse: (host) => host.switchToHttp().getResponse<FastifyReply>(),\n};\n","import type { RequestExtractor, TransportType } from './context.types';\nimport { graphqlExtractor } from './extractors/graphql.extractor';\nimport { httpExtractor } from './extractors/http.extractor';\n\nconst registry = new Map<TransportType, RequestExtractor>([\n ['http', httpExtractor],\n ['graphql', graphqlExtractor],\n]);\n\n// Registers (or overrides) the extractor for a transport type without modifying the SDK\nexport function registerTransport(type: TransportType, extractor: RequestExtractor): void {\n registry.set(type, extractor);\n}\n\n// Resolves the extractor for the host's reported transport, falling back to HTTP.\nexport function resolveExtractor(type: string): RequestExtractor {\n return registry.get(type as TransportType) ?? httpExtractor;\n}\n","import type { ArgumentsHost } from '@nestjs/common';\nimport type { FastifyRequest } from 'fastify';\nimport { resolveExtractor } from './context.registry';\n\n// Returns the underlying Fastify request for any registered transport, via the transport registry.\nexport function getRequestFromContext(host: ArgumentsHost): FastifyRequest {\n return resolveExtractor(host.getType()).getRequest(host);\n}\n","import type { ArgumentsHost } from '@nestjs/common';\nimport type { FastifyReply } from 'fastify';\nimport { resolveExtractor } from './context.registry';\n\n// Returns the underlying Fastify reply for any registered transport (HTTP, GraphQL, ...).\nexport function getResponseFromContext(host: ArgumentsHost): FastifyReply {\n return resolveExtractor(host.getType()).getResponse(host);\n}\n","import { SetMetadata } from '@nestjs/common';\n\n// Restricts endpoint access to specific session types\nexport const REQUIRE_SESSION_KEY = 'requiredSessionTypes';\nexport const RequireSession = (...types: string[]) => SetMetadata(REQUIRE_SESSION_KEY, types);\n","import { SetMetadata } from '@nestjs/common';\n\nexport const SKIP_CSRF_KEY = 'skipCsrf';\n\nexport const SkipCsrf = () => SetMetadata(SKIP_CSRF_KEY, true);\n","import { Inject, Injectable, Logger, UnauthorizedException } from '@nestjs/common';\nimport { JwtService, type JwtSignOptions } from '@nestjs/jwt';\nimport type { FastifyRequest } from 'fastify';\nimport '../../types/fastify-augmentation';\nimport { parseExpiryToMs } from '../../utils/time.utils';\nimport {\n AUTH_CONFIG,\n type AuthConfig,\n type DecodedAccessToken,\n type DecodedRefreshToken,\n TokenType,\n} from '../auth.config';\nimport { hashToken, verifyTokenHash } from '../utils/token-hash.util';\n\nexport type { DecodedAccessToken, DecodedRefreshToken };\n\n// Session info type from Fastify augmentation\ntype SessionInfo = NonNullable<FastifyRequest['sessionInfo']>;\n\ninterface TokenError extends Error {\n name: 'TokenExpiredError' | 'JsonWebTokenError' | 'NotBeforeError';\n}\n\n// Handles all token operations — generation, validation, and binding verification\n@Injectable()\nexport class TokenService {\n private readonly logger = new Logger(TokenService.name);\n\n constructor(\n private readonly jwtService: JwtService,\n @Inject(AUTH_CONFIG) private readonly config: AuthConfig,\n ) {}\n\n // ---- Generation ----\n\n // Generates an access token bound to the given refresh token\n generateAccessToken(sessionInfo: SessionInfo, refreshToken: string): string {\n const { userId, sessionId, sessionType, ...metadata } = sessionInfo;\n return this.jwtService.sign(\n {\n sessionType,\n tokenType: TokenType.ACCESS,\n userId,\n sessionId,\n refreshTokenHash: hashToken(refreshToken),\n ...metadata,\n },\n { expiresIn: this.config.tokenExpiry.access },\n );\n }\n\n // Generates a refresh token for session persistence\n generateRefreshToken(sessionInfo: SessionInfo): string {\n const { userId, sessionId, sessionType, ...metadata } = sessionInfo;\n return this.jwtService.sign(\n { sessionType, tokenType: TokenType.REFRESH, userId, sessionId, ...metadata },\n { expiresIn: this.config.tokenExpiry.refresh },\n );\n }\n\n // Signs an arbitrary payload with optional JWT options\n sign(payload: object, options?: JwtSignOptions): string {\n return this.jwtService.sign(payload, options);\n }\n\n // Verifies a token and ensures it matches the expected token type\n verify(\n token: string,\n expectedType: TokenType,\n ): { userId: string; sessionId: string; sessionType: string; tokenType: TokenType } {\n try {\n const payload = this.jwtService.verify(token);\n\n if (payload.tokenType !== expectedType) {\n throw new Error(`Expected ${expectedType} token, got ${payload.tokenType}`);\n }\n\n return payload;\n } catch (error) {\n this.logger.error(`Failed to verify ${expectedType} token`, error);\n throw error;\n }\n }\n\n // Returns the expiry as a Date for the given token type\n getExpiryTime(type: TokenType): Date {\n return new Date(Date.now() + parseExpiryToMs(this.config.tokenExpiry[type]));\n }\n\n // Returns the token lifetime in seconds for the given type\n getExpiryInSeconds(type: TokenType): number {\n return Math.floor(parseExpiryToMs(this.config.tokenExpiry[type]) / 1000);\n }\n\n // ---- Validation ----\n\n // Decodes and validates an access token JWT\n validateAccessToken(token: string): DecodedAccessToken {\n try {\n const decoded = this.jwtService.verify<DecodedAccessToken>(token);\n\n if (decoded.tokenType !== TokenType.ACCESS) {\n throw new UnauthorizedException('Invalid token type');\n }\n\n return decoded;\n } catch (error) {\n if (error instanceof UnauthorizedException) throw error;\n\n const jwtError = error as TokenError;\n switch (jwtError.name) {\n case 'TokenExpiredError':\n throw new UnauthorizedException('Access token has expired');\n case 'JsonWebTokenError':\n throw new UnauthorizedException('Invalid access token');\n case 'NotBeforeError':\n throw new UnauthorizedException('Access token not yet valid');\n default:\n throw new UnauthorizedException('Access token validation failed');\n }\n }\n }\n\n // Decodes and validates a refresh token JWT\n validateRefreshToken(token: string): DecodedRefreshToken {\n try {\n const decoded = this.jwtService.verify<DecodedRefreshToken>(token);\n\n if (decoded.tokenType !== TokenType.REFRESH) {\n throw new UnauthorizedException('Invalid token type');\n }\n\n return decoded;\n } catch (error) {\n if (error instanceof UnauthorizedException) throw error;\n throw new UnauthorizedException('Invalid or expired session');\n }\n }\n\n // Validates that the access token is bound to the refresh token\n validateTokenBinding(accessToken: DecodedAccessToken, refreshToken: string): void {\n if (!verifyTokenHash(refreshToken, accessToken.refreshTokenHash)) {\n throw new UnauthorizedException('Session validation failed');\n }\n }\n}\n","// Parses a duration string (e.g. '10m', '1h', '30s', '7d') to milliseconds\nexport function parseExpiryToMs(expiry: string): number {\n const match = expiry.match(/^(\\d+)([smhdwy])$/);\n if (!match) throw new Error(`Invalid expiry format: ${expiry}`);\n\n const [, digits = '', unit = ''] = match;\n const value = Number.parseInt(digits, 10);\n const multipliers: Record<string, number> = {\n s: 1000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n w: 604_800_000,\n y: 31_536_000_000,\n };\n\n const multiplier = multipliers[unit];\n if (multiplier === undefined) throw new Error(`Invalid expiry format: ${expiry}`);\n\n return value * multiplier;\n}\n","import * as crypto from 'node:crypto';\n\n// Hashes a token using SHA-256\nexport function hashToken(token: string): string {\n return crypto.createHash('sha256').update(token).digest('hex');\n}\n\n// Verifies a token against its stored hash\nexport function verifyTokenHash(token: string, expectedHash: string): boolean {\n const computedHash = hashToken(token);\n if (computedHash.length !== expectedHash.length) return false;\n return crypto.timingSafeEqual(Buffer.from(computedHash, 'hex'), Buffer.from(expectedHash, 'hex'));\n}\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the bearer token from the Authorization header\nexport const AccessToken = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n const authHeader = request.headers.authorization;\n return authHeader?.replace('Bearer ', '') || '';\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the client IP from the request, working across HTTP and GraphQL transports\nexport const ClientIp = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n return getRequestFromContext(ctx).ip;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { AUTH_CONFIG_DEFAULTS } from '../auth.config';\n\n// Extracts the cookie domain from x-forwarded-host (injected by proxy), validated against baseDomain, falls back to baseDomain if invalid\nexport const CookieDomain = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n const domain = hostStr.split(':')[0] ?? hostStr;\n const baseDomain =\n request.authConfig?.cookie.refreshCookieDomain ?? AUTH_CONFIG_DEFAULTS.cookie.refreshCookieDomain ?? '';\n return domain.endsWith(`.${baseDomain}`) ? domain : baseDomain;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { AUTH_CONFIG_DEFAULTS } from '../auth.config';\n\n// Returns the configured refresh cookie name from request.authConfig\nexport const CookieName = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n return request.authConfig?.cookie.refreshCookieName ?? AUTH_CONFIG_DEFAULTS.cookie.refreshCookieName;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the request hostname from x-forwarded-host (set by reverse proxies and dev proxy) with fallback to request.hostname\nexport const Hostname = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n return hostStr.split(':')[0] ?? hostStr;\n});\n","import { SetMetadata } from '@nestjs/common';\n\nexport const Public = () => SetMetadata('isPublic', true);\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { UnauthorizedException } from '../../exceptions';\nimport { AUTH_CONFIG_DEFAULTS, type CookieConfig, type CookieSerializeOptions } from '../auth.config';\n\n// Builds cookie serialize options from the given cookie config\nfunction buildCookieOptionsForHost(cookieConfig: CookieConfig, hostname: string): CookieSerializeOptions {\n const baseDomain = cookieConfig.refreshCookieDomain;\n\n if (!baseDomain) {\n throw new Error('refreshCookieDomain must be configured before using @RefreshCookieOptions()');\n }\n\n if (!hostname.endsWith(`.${baseDomain}`)) {\n throw new UnauthorizedException('Invalid request host.');\n }\n\n return {\n httpOnly: true,\n secure: cookieConfig.refreshCookieSecure,\n sameSite: cookieConfig.refreshCookieSameSite,\n path: cookieConfig.refreshCookiePath,\n maxAge: cookieConfig.refreshCookieMaxAge,\n domain: hostname,\n };\n}\n\n// Returns refresh cookie options with domain scoped to the request subdomain (reads x-forwarded-host injected by proxy)\nexport const RefreshCookieOptions = createParamDecorator(\n (_data: unknown, ctx: ExecutionContext): CookieSerializeOptions => {\n const request = getRequestFromContext(ctx);\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n const domain = hostStr.split(':')[0] ?? hostStr;\n const cookieConfig: CookieConfig = request.authConfig?.cookie ?? AUTH_CONFIG_DEFAULTS.cookie;\n return buildCookieOptionsForHost(cookieConfig, domain);\n },\n);\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadGatewayException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Gateway', HttpStatus.BAD_GATEWAY);\n }\n}\n","import { HttpException, HttpStatus } from '@nestjs/common';\nimport type { FieldError } from '../types/error-response.types';\n\n// Re-export FieldError for backwards compatibility\nexport type { FieldError } from '../types/error-response.types';\n\nexport interface ProblemOptions {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\nexport abstract class HttpProblemException extends HttpException {\n constructor(detailOrOptions: string | ProblemOptions, httpStatus: HttpStatus) {\n const options = typeof detailOrOptions === 'string' ? { detail: detailOrOptions } : detailOrOptions;\n\n super(\n {\n type: options.type ?? 'about:blank',\n label: options.label,\n detail: options.detail,\n errors: options.errors ?? [],\n },\n httpStatus,\n );\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadRequestException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Request', HttpStatus.BAD_REQUEST);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ConflictException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Conflict', HttpStatus.CONFLICT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ForbiddenException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Forbidden', HttpStatus.FORBIDDEN);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class GoneException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Gone', HttpStatus.GONE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class InternalServerErrorException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Internal Server Error', HttpStatus.INTERNAL_SERVER_ERROR);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class MethodNotAllowedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Method Not Allowed', HttpStatus.METHOD_NOT_ALLOWED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotAcceptableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Acceptable', HttpStatus.NOT_ACCEPTABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotFoundException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Found', HttpStatus.NOT_FOUND);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotImplementedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Implemented', HttpStatus.NOT_IMPLEMENTED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class PayloadTooLargeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Payload Too Large', HttpStatus.PAYLOAD_TOO_LARGE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class RequestTimeoutException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Request Timeout', HttpStatus.REQUEST_TIMEOUT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ServiceUnavailableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Service Unavailable', HttpStatus.SERVICE_UNAVAILABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class TooManyRequestsException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Too Many Requests', HttpStatus.TOO_MANY_REQUESTS);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnauthorizedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unauthorized', HttpStatus.UNAUTHORIZED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnprocessableEntityException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unprocessable Entity', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnsupportedMediaTypeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unsupported Media Type', HttpStatus.UNSUPPORTED_MEDIA_TYPE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ValidationException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Validation Failed', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { AUTH_CONFIG_DEFAULTS } from '../auth.config';\n\nexport const RefreshTokenCookie = createParamDecorator((_data: unknown, ctx: ExecutionContext): string | undefined => {\n const request = getRequestFromContext(ctx);\n const cookies = request.cookies ?? {};\n const cookieName = request.authConfig?.cookie.refreshCookieName ?? AUTH_CONFIG_DEFAULTS.cookie.refreshCookieName;\n return cookies[cookieName] as string | undefined;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport '../../types/fastify-augmentation';\n\nexport interface SessionInfo {\n userId: string;\n sessionId: string;\n sessionType: string;\n}\n\n// Returns full decoded session info from request.sessionInfo (set by VrittiAuthGuard)\nexport const SessionData = createParamDecorator((_data: unknown, ctx: ExecutionContext): SessionInfo => {\n const request = getRequestFromContext(ctx);\n const sessionInfo = request.sessionInfo;\n\n if (!sessionInfo?.sessionId) {\n throw new Error('Session info not found on request. Ensure route is protected by auth guard.');\n }\n\n return {\n userId: sessionInfo.userId,\n sessionId: sessionInfo.sessionId,\n sessionType: sessionInfo.sessionType,\n };\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the subdomain from the request host, resolved the same way as @Hostname() — x-forwarded-host\n// (set by dev and reverse proxies) with a fallback to request.hostname. Origin is deliberately not used:\n// it is client-controlled and can disagree with the Host header, which would mint a session for one\n// subdomain that the auth guard's host check then rejects against another.\nexport const Subdomain = createParamDecorator((_data: unknown, ctx: ExecutionContext): string | undefined => {\n const request = getRequestFromContext(ctx);\n\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n if (!hostStr) return undefined;\n\n const host = hostStr.split(':')[0] ?? hostStr;\n return host.split('.')[0] || undefined;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the User-Agent header from the request, working across HTTP and GraphQL transports\nexport const UserAgent = createParamDecorator((_data: unknown, ctx: ExecutionContext): string | undefined => {\n const userAgent = getRequestFromContext(ctx).headers['user-agent'];\n return Array.isArray(userAgent) ? userAgent[0] : userAgent;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport '../../types/fastify-augmentation';\n\n// Extracts userId from request.sessionInfo (set by VrittiAuthGuard)\nexport const UserId = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n const sessionInfo = request.sessionInfo;\n\n if (!sessionInfo?.userId) {\n throw new Error('User ID not found on request. Ensure route is protected by auth guard.');\n }\n\n return sessionInfo.userId;\n});\n"],"mappings":"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","names":["AUTH_CONFIG","Symbol","AUTH_CONFIG_DEFAULTS","cookie","refreshCookieName","refreshCookieMaxAge","refreshCookiePath","refreshCookieSecure","process","env","NODE_ENV","refreshCookieSameSite","refreshCookieDomain","guard","authHeaderName","tokenPrefix","csrfExemptSessionTypes","csrfExemptTransports","refreshTokenBindingExemptSessionTypes","TokenType","Global","Module","ConfigModule","ConfigService","APP_GUARD","Reflector","JwtModule","Global","Module","Inject","Injectable","Scope","REQUEST","resolveInjectedRequest","injected","candidate","headers","undefined","req","RequestService","injectedRequest","config","request","resolveInjectedRequest","getAccessToken","authHeader","headers","guard","authHeaderName","type","token","split","tokenPrefix","getRefreshToken","cookies","refreshToken","cookie","refreshCookieName","_error","getHeader","key","getHostname","hostname","getAllHeaders","scope","Scope","REQUEST","RequestModule","providers","RequestService","exports","ForbiddenException","Inject","Injectable","Logger","Scope","UnauthorizedException","SSE_METADATA","Reflector","graphqlExtractor","getRequest","host","getArgByIndex","req","getResponse","reply","httpExtractor","getRequest","host","switchToHttp","getResponse","registry","Map","httpExtractor","graphqlExtractor","resolveExtractor","type","registry","get","httpExtractor","getRequestFromContext","host","resolveExtractor","getType","getRequest","getResponseFromContext","host","resolveExtractor","getType","getResponse","SetMetadata","REQUIRE_SESSION_KEY","RequireSession","types","SetMetadata","SetMetadata","SKIP_CSRF_KEY","SkipCsrf","SetMetadata","Inject","Injectable","Logger","UnauthorizedException","JwtService","parseExpiryToMs","expiry","match","Error","digits","unit","value","Number","parseInt","multipliers","s","m","h","d","w","y","multiplier","undefined","crypto","hashToken","token","createHash","update","digest","verifyTokenHash","expectedHash","computedHash","length","timingSafeEqual","Buffer","from","TokenService","logger","Logger","name","jwtService","config","generateAccessToken","sessionInfo","refreshToken","userId","sessionId","sessionType","metadata","sign","tokenType","TokenType","ACCESS","refreshTokenHash","hashToken","expiresIn","tokenExpiry","access","generateRefreshToken","REFRESH","refresh","payload","options","verify","token","expectedType","Error","error","getExpiryTime","type","Date","now","parseExpiryToMs","getExpiryInSeconds","Math","floor","validateAccessToken","decoded","UnauthorizedException","jwtError","validateRefreshToken","validateTokenBinding","accessToken","verifyTokenHash","VrittiAuthGuard","logger","Logger","name","reflector","requestService","tokenService","config","canActivate","context","request","getRequestFromContext","reply","getResponseFromContext","route","method","url","authConfig","csrfExemptTransports","guard","skipCsrf","getAllAndOverride","SKIP_CSRF_KEY","getHandler","getClass","includes","getType","isPublic","validateCsrf","debug","requiredSessionTypes","REQUIRE_SESSION_KEY","isSseEndpoint","get","SSE_METADATA","handleSseAuth","sessionType","handleHttpAuth","csrfExemptSessionTypes","accessToken","getAccessToken","warn","UnauthorizedException","decoded","validateAccessToken","refreshTokenBindingExemptSessionTypes","refreshToken","getRefreshToken","validateTokenBinding","length","join","tokenType","_tokenType","refreshTokenHash","_hash","exp","_exp","iat","_iat","sessionInfo","onAuthenticated","userId","validateRefreshToken","safeMethods","fastifyInstance","server","csrfProtection","ForbiddenException","Promise","resolve","reject","originalSend","send","bind","Error","err","_error","errors","field","message","scope","Scope","REQUEST","mergeWithDefaults","input","tokenExpiry","cookie","AUTH_CONFIG_DEFAULTS","guard","AuthConfigModule","forRootAsync","options","module","imports","ConfigModule","RequestModule","JwtModule","registerAsync","inject","ConfigService","useFactory","config","secret","getOrThrow","signOptions","algorithm","providers","provide","Reflector","useClass","APP_GUARD","VrittiAuthGuard","AUTH_CONFIG","args","TokenService","exports","createParamDecorator","AccessToken","createParamDecorator","_data","ctx","request","getRequestFromContext","authHeader","headers","authorization","replace","createParamDecorator","ClientIp","createParamDecorator","_data","ctx","getRequestFromContext","ip","createParamDecorator","CookieDomain","createParamDecorator","_data","ctx","request","getRequestFromContext","forwarded","headers","raw","Array","isArray","hostStr","hostname","domain","split","baseDomain","authConfig","cookie","refreshCookieDomain","AUTH_CONFIG_DEFAULTS","endsWith","createParamDecorator","CookieName","createParamDecorator","_data","ctx","request","getRequestFromContext","authConfig","cookie","refreshCookieName","AUTH_CONFIG_DEFAULTS","createParamDecorator","Hostname","createParamDecorator","_data","ctx","request","getRequestFromContext","forwarded","headers","raw","Array","isArray","hostStr","hostname","split","SetMetadata","Public","SetMetadata","createParamDecorator","HttpStatus","HttpException","HttpProblemException","HttpException","detailOrOptions","httpStatus","options","detail","type","label","errors","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","UnauthorizedException","HttpProblemException","detailOrOptions","HttpStatus","UNAUTHORIZED","HttpStatus","HttpStatus","HttpStatus","buildCookieOptionsForHost","cookieConfig","hostname","baseDomain","refreshCookieDomain","Error","endsWith","UnauthorizedException","httpOnly","secure","refreshCookieSecure","sameSite","refreshCookieSameSite","path","refreshCookiePath","maxAge","refreshCookieMaxAge","domain","RefreshCookieOptions","createParamDecorator","_data","ctx","request","getRequestFromContext","forwarded","headers","raw","Array","isArray","hostStr","split","authConfig","cookie","AUTH_CONFIG_DEFAULTS","createParamDecorator","RefreshTokenCookie","createParamDecorator","_data","ctx","request","getRequestFromContext","cookies","cookieName","authConfig","cookie","refreshCookieName","AUTH_CONFIG_DEFAULTS","createParamDecorator","SessionData","createParamDecorator","_data","ctx","request","getRequestFromContext","sessionInfo","sessionId","Error","userId","sessionType","createParamDecorator","Subdomain","createParamDecorator","_data","ctx","request","getRequestFromContext","forwarded","headers","raw","Array","isArray","hostStr","hostname","undefined","host","split","createParamDecorator","UserAgent","createParamDecorator","_data","ctx","userAgent","getRequestFromContext","headers","Array","isArray","createParamDecorator","UserId","createParamDecorator","_data","ctx","request","getRequestFromContext","sessionInfo","userId","Error"]}
|
|
1
|
+
{"version":3,"sources":["../src/signing/request.ts","../src/auth/app-request.ts","../src/auth/auth.config.ts","../src/auth/auth-config.module.ts","../src/request/request.module.ts","../src/request/services/request.service.ts","../src/context/resolve-request.ts","../src/auth/guards/vritti-auth.guard.ts","../src/context/extractors/graphql.extractor.ts","../src/context/extractors/http.extractor.ts","../src/context/context.registry.ts","../src/context/get-request.ts","../src/context/get-response.ts","../src/auth/decorators/require-app.decorator.ts","../src/auth/decorators/require-session.decorator.ts","../src/auth/decorators/skip-csrf.decorator.ts","../src/auth/services/token.service.ts","../src/utils/time.utils.ts","../src/auth/utils/token-hash.util.ts","../src/auth/decorators/access-token.decorator.ts","../src/auth/decorators/client-ip.decorator.ts","../src/auth/decorators/cookie-domain.decorator.ts","../src/auth/decorators/cookie-name.decorator.ts","../src/auth/decorators/hostname.decorator.ts","../src/auth/decorators/public.decorator.ts","../src/auth/decorators/refresh-cookie-options.decorator.ts","../src/exceptions/bad-gateway.exception.ts","../src/exceptions/base-field.exception.ts","../src/exceptions/bad-request.exception.ts","../src/exceptions/conflict.exception.ts","../src/exceptions/forbidden.exception.ts","../src/exceptions/gone.exception.ts","../src/exceptions/internal-server-error.exception.ts","../src/exceptions/method-not-allowed.exception.ts","../src/exceptions/not-acceptable.exception.ts","../src/exceptions/not-found.exception.ts","../src/exceptions/not-implemented.exception.ts","../src/exceptions/payload-too-large.exception.ts","../src/exceptions/request-timeout.exception.ts","../src/exceptions/service-unavailable.exception.ts","../src/exceptions/too-many-requests.exception.ts","../src/exceptions/unauthorized.exception.ts","../src/exceptions/unprocessable-entity.exception.ts","../src/exceptions/unsupported-media-type.exception.ts","../src/exceptions/validation.exception.ts","../src/auth/decorators/refresh-token-cookie.decorator.ts","../src/auth/decorators/session-data.decorator.ts","../src/auth/decorators/subdomain.decorator.ts","../src/auth/decorators/user-agent.decorator.ts","../src/auth/decorators/user-id.decorator.ts"],"sourcesContent":["import { createHash, createPrivateKey, createPublicKey, sign, verify } from 'node:crypto';\n\n/**\n * The workspace-scope headers, in the order the canonical appends them.\n *\n * Exactly one is expected — the kind of scope *is* which header is sent, the same\n * convention core-web and core-app already use. Signing the header **name** rather\n * than a separate `kind` field means a verifier needs no scope vocabulary: it scans\n * this same list in this same order and rebuilds an identical string.\n *\n * Fixed and shared so a signer and a verifier can never disagree about it, and so the\n * discouraged multi-header case is deterministic rather than ambiguous.\n */\nexport const WORKSPACE_HEADER_ORDER = ['x-site-id', 'x-sg-id', 'x-le-id', 'x-org-id'] as const;\n\nexport interface RequestCanonicalInput {\n method: string;\n path: string;\n orgId?: string;\n body?: string | Buffer;\n timestamp: number;\n /**\n * The raw query string, without the leading `?`.\n *\n * Signed byte for byte, with no sorting or re-encoding, so the signer and the\n * verifier cannot disagree about normalization. The consequence is that a proxy\n * which reorders or re-encodes parameters invalidates the signature.\n */\n query?: string;\n /**\n * The party (person) the request acts for, when there is one.\n *\n * Signed so a caller cannot be swapped for another shopper in transit.\n */\n partyId?: string;\n /**\n * The workspace-scope headers actually present on the request, by header name.\n *\n * Only names in `WORKSPACE_HEADER_ORDER` are considered, and they are appended in\n * that order — so signer and verifier agree without either needing to know what a\n * \"site\" or a \"legal entity\" is.\n */\n workspaceHeaders?: Record<string, string | undefined>;\n}\n\nexport interface SignRequestInput {\n method: string;\n path: string;\n orgId?: string;\n body?: string | Buffer;\n /** Raw query string, without the leading `?`. See `RequestCanonicalInput`. */\n query?: string;\n /**\n * The party (person) the request acts for, when there is one.\n *\n * Signed so a caller cannot be swapped for another shopper in transit.\n */\n partyId?: string;\n /**\n * The workspace-scope headers actually present on the request, by header name.\n *\n * Only names in `WORKSPACE_HEADER_ORDER` are considered, and they are appended in\n * that order — so signer and verifier agree without either needing to know what a\n * \"site\" or a \"legal entity\" is.\n */\n workspaceHeaders?: Record<string, string | undefined>;\n}\n\nexport interface VerifySignedRequestInput {\n method: string;\n path: string;\n orgId?: string;\n /** Raw query string, without the leading `?`. See `RequestCanonicalInput`. */\n query?: string;\n /**\n * The party (person) the request acts for, when there is one.\n *\n * Signed so a caller cannot be swapped for another shopper in transit.\n */\n partyId?: string;\n /**\n * The workspace-scope headers actually present on the request, by header name.\n *\n * Only names in `WORKSPACE_HEADER_ORDER` are considered, and they are appended in\n * that order — so signer and verifier agree without either needing to know what a\n * \"site\" or a \"legal entity\" is.\n */\n workspaceHeaders?: Record<string, string | undefined>;\n rawBody?: string | Buffer;\n timestamp: string | number;\n signature: string;\n publicKey: string;\n maxSkewSeconds?: number;\n}\n\n/**\n * Builds the canonical string a request's signature is made over:\n * `METHOD\\npath\\norgId\\nsha256hex(body)\\ntimestamp`, then a labelled line per\n * optional field that is present.\n *\n * Optional fields are appended **only when set**, so a caller that supplies none\n * produces exactly the string this function has always produced — which is what\n * lets new fields be added without invalidating existing signers. They are\n * **labelled** so that \"query present, party absent\" can never be confused with\n * \"party present, query absent\".\n *\n * Absence is still tamper-evident: strip a signed field in transit and the\n * verifier rebuilds the canonical without it, which no longer matches.\n */\nexport function buildRequestCanonical(input: RequestCanonicalInput): string {\n const bodyHash = createHash('sha256')\n .update(input.body ?? '')\n .digest('hex');\n\n const lines = [input.method.toUpperCase(), input.path, input.orgId ?? '', bodyHash, String(input.timestamp)];\n if (input.query) lines.push(`query:${input.query}`);\n if (input.partyId) lines.push(`party:${input.partyId}`);\n\n // Header name and value both go in, so swapping x-le-id for x-site-id on the same\n // value breaks the signature. Fixed order, so multiple headers stay deterministic.\n for (const header of WORKSPACE_HEADER_ORDER) {\n const value = input.workspaceHeaders?.[header];\n if (value) lines.push(`${header}:${value}`);\n }\n\n return lines.join('\\n');\n}\n\n// Signs a request with an Ed25519 private key (base64 pkcs8 DER), stamping the current unix time\nexport function signRequestHeaders(\n input: SignRequestInput,\n privateKeyBase64: string,\n): { 'x-timestamp': string; 'x-signature': string } {\n const timestamp = Math.floor(Date.now() / 1000);\n const canonical = buildRequestCanonical({ ...input, timestamp });\n const key = createPrivateKey({ key: Buffer.from(privateKeyBase64, 'base64'), format: 'der', type: 'pkcs8' });\n const signature = sign(null, Buffer.from(canonical, 'utf8'), key).toString('base64');\n return { 'x-timestamp': String(timestamp), 'x-signature': signature };\n}\n\n// Verifies a signed request (signature + timestamp skew) against an Ed25519 public key; malformed input ⇒ false\nexport function verifySignedRequest(input: VerifySignedRequestInput): boolean {\n try {\n const timestamp = Number(input.timestamp);\n if (!Number.isFinite(timestamp)) return false;\n const maxSkew = input.maxSkewSeconds ?? 300;\n if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > maxSkew) return false;\n const canonical = buildRequestCanonical({\n method: input.method,\n path: input.path,\n orgId: input.orgId,\n query: input.query,\n partyId: input.partyId,\n workspaceHeaders: input.workspaceHeaders,\n body: input.rawBody,\n timestamp,\n });\n const key = createPublicKey({ key: Buffer.from(input.publicKey, 'base64'), format: 'der', type: 'spki' });\n return verify(null, Buffer.from(canonical, 'utf8'), key, Buffer.from(input.signature, 'base64'));\n } catch {\n return false;\n }\n}\n","/**\n * The shared contract between this guard and a server that authenticates apps.\n *\n * The guard recognises `@RequireApp()` and enforces the type filter; the server\n * resolves the credential in `guard.onAuthenticated`, because only it has a\n * database. These are the values both sides have to agree on.\n */\n\n/** The client id an app sends alongside the standard signature headers. */\nexport const CLIENT_ID_HEADER = 'x-vritti-client-id';\n\n/** The party (person) a signed request is acting for, when there is one. */\nexport const PARTY_ID_HEADER = 'x-party-id';\n\n// Re-exported from the signing module, which owns it because the canonical is what\n// fixes the order. Kept visible here so an auth-side consumer has one import.\nexport { WORKSPACE_HEADER_ORDER } from '../signing/request';\n\n/**\n * How far a signed request's timestamp may drift before it is refused.\n *\n * Five minutes, matching the deployment-signing path. Wide enough for ordinary\n * clock drift between two servers; a captured request is replayable inside the\n * window, which is why app operations should stay idempotent.\n */\nexport const MAX_CLOCK_SKEW_SECONDS = 300;\n\n/**\n * `sessionInfo.sessionType` for an app request.\n *\n * Not a value of any server's `session_type` enum — no session row exists. It is\n * here so anything reading `sessionType` can tell an app apart from a person\n * without a second field.\n */\nexport const APP_SESSION_TYPE = 'APP';\n","import type { FastifyRequest } from 'fastify';\nimport type { RequestService } from '../request/services/request.service';\n\nexport const AUTH_CONFIG = Symbol('AUTH_CONFIG');\n\nexport type OnAuthenticatedCallback = (\n requestService: RequestService,\n sessionInfo: NonNullable<FastifyRequest['sessionInfo']>,\n) => void | Promise<void>;\n\nexport type TokenExpiryString = `${number}${'s' | 'm' | 'h' | 'd' | 'w' | 'y'}`;\n\nexport interface TokenExpiry {\n access: TokenExpiryString;\n refresh: TokenExpiryString;\n}\n\nexport interface CookieConfig {\n refreshCookieName: string;\n refreshCookieMaxAge: number;\n refreshCookiePath: string;\n refreshCookieSecure: boolean;\n refreshCookieSameSite: 'strict' | 'lax' | 'none';\n refreshCookieDomain?: string;\n}\n\nexport interface GuardConfig {\n authHeaderName: string;\n tokenPrefix: string;\n csrfExemptSessionTypes?: string[];\n csrfExemptTransports?: string[];\n refreshTokenBindingExemptSessionTypes?: string[];\n onAuthenticated?: OnAuthenticatedCallback;\n}\n\nexport interface AuthConfig {\n tokenExpiry: TokenExpiry;\n cookie: CookieConfig;\n guard: GuardConfig;\n}\n\nexport const AUTH_CONFIG_DEFAULTS = {\n cookie: {\n refreshCookieName: 'vritti_refresh',\n refreshCookieMaxAge: 30 * 24 * 60 * 60 * 1000, // 30 days\n refreshCookiePath: '/',\n refreshCookieSecure: process.env.NODE_ENV === 'production',\n refreshCookieSameSite: 'strict' as const,\n refreshCookieDomain: 'localhost',\n },\n guard: {\n authHeaderName: 'authorization',\n tokenPrefix: 'Bearer',\n csrfExemptSessionTypes: [],\n csrfExemptTransports: [],\n refreshTokenBindingExemptSessionTypes: [],\n },\n} satisfies Omit<AuthConfig, 'tokenExpiry'>;\n\nexport interface CookieSerializeOptions {\n httpOnly: boolean;\n secure: boolean;\n sameSite: 'strict' | 'lax' | 'none';\n path: string;\n maxAge: number;\n domain: string;\n}\n\nexport enum TokenType {\n ACCESS = 'access',\n REFRESH = 'refresh',\n}\n\ninterface JwtClaims {\n exp: number;\n iat: number;\n}\n\nexport interface AccessTokenPayload {\n sessionType: string;\n tokenType: TokenType.ACCESS;\n userId: string;\n sessionId: string;\n refreshTokenHash: string;\n}\n\nexport type DecodedAccessToken = AccessTokenPayload & JwtClaims;\n\nexport interface RefreshTokenPayload {\n sessionType: string;\n tokenType: TokenType.REFRESH;\n userId: string;\n sessionId: string;\n}\n\nexport type DecodedRefreshToken = RefreshTokenPayload & JwtClaims;\n","import { type DynamicModule, Global, type InjectionToken, Module, type ModuleMetadata } from '@nestjs/common';\nimport { ConfigModule, ConfigService } from '@nestjs/config';\nimport { APP_GUARD, Reflector } from '@nestjs/core';\nimport { JwtModule } from '@nestjs/jwt';\nimport { RequestModule } from '../request/request.module';\nimport {\n AUTH_CONFIG,\n AUTH_CONFIG_DEFAULTS,\n type AuthConfig,\n type CookieConfig,\n type GuardConfig,\n type TokenExpiry,\n} from './auth.config';\nimport { VrittiAuthGuard } from './guards/vritti-auth.guard';\nimport { TokenService } from './services/token.service';\n\n// Factory return type — tokenExpiry is required, cookie and guard are partial with defaults merged\ninterface AuthConfigInput {\n tokenExpiry: TokenExpiry;\n cookie?: Partial<CookieConfig>;\n guard?: Partial<GuardConfig>;\n}\n\ninterface AuthConfigModuleOptions<T extends unknown[] = unknown[]> {\n useFactory: (...args: [...T]) => AuthConfigInput | Promise<AuthConfigInput>;\n inject?: InjectionToken[];\n /**\n * Extra modules the factory's injected providers come from.\n *\n * Needed because `guard.onAuthenticated` is where a server resolves an app\n * credential, so the factory has to be able to inject the service that owns that\n * lookup. Mirrors `NatsClientModule.forRoot`.\n */\n imports?: ModuleMetadata['imports'];\n}\n\n// Merges user-provided partial config with defaults to produce a complete AuthConfig\nfunction mergeWithDefaults(input: AuthConfigInput): AuthConfig {\n return {\n tokenExpiry: input.tokenExpiry,\n cookie: {\n ...AUTH_CONFIG_DEFAULTS.cookie,\n ...(input.cookie ?? {}),\n },\n guard: {\n ...AUTH_CONFIG_DEFAULTS.guard,\n ...(input.guard ?? {}),\n },\n };\n}\n\n@Global()\n@Module({})\nexport class AuthConfigModule {\n // Registers JWT, TokenService, and global VrittiAuthGuard\n static forRootAsync<T extends unknown[] = unknown[]>(options: AuthConfigModuleOptions<T>): DynamicModule {\n return {\n module: AuthConfigModule,\n imports: [\n ConfigModule,\n RequestModule,\n ...(options.imports ?? []),\n JwtModule.registerAsync({\n imports: [ConfigModule],\n inject: [ConfigService],\n useFactory: (config: ConfigService) => ({\n secret: config.getOrThrow<string>('JWT_SECRET'),\n signOptions: { algorithm: 'HS256' as const },\n }),\n }),\n ],\n providers: [\n {\n provide: Reflector,\n useClass: Reflector,\n },\n {\n provide: APP_GUARD,\n useClass: VrittiAuthGuard,\n },\n {\n provide: AUTH_CONFIG,\n useFactory: async (...args: unknown[]) => {\n const input = await options.useFactory(...(args as [...T]));\n return mergeWithDefaults(input);\n },\n inject: options.inject || [],\n },\n TokenService,\n ],\n exports: [JwtModule, TokenService, AUTH_CONFIG],\n };\n }\n}\n","import { Global, Module } from '@nestjs/common';\nimport { RequestService } from './services/request.service';\n\n@Global()\n@Module({\n providers: [RequestService],\n exports: [RequestService],\n})\nexport class RequestModule {}\n","import { Inject, Injectable, Scope } from '@nestjs/common';\nimport { REQUEST } from '@nestjs/core';\nimport type { FastifyRequest } from 'fastify';\nimport { AUTH_CONFIG, type AuthConfig } from '../../auth/auth.config';\nimport { resolveInjectedRequest } from '../../context/resolve-request';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class RequestService {\n constructor(\n @Inject(REQUEST) private readonly injectedRequest: FastifyRequest,\n @Inject(AUTH_CONFIG) private readonly config: AuthConfig,\n ) {}\n\n // Unwraps the GraphQL { req, reply } context wrapper so every accessor below works across both transports\n private get request(): FastifyRequest {\n return resolveInjectedRequest(this.injectedRequest);\n }\n\n // Extracts the bearer access token from the Authorization header\n getAccessToken(): string | null {\n const authHeader = this.request.headers?.[this.config.guard.authHeaderName];\n if (!authHeader || typeof authHeader !== 'string') {\n return null;\n }\n const [type, token] = authHeader.split(' ') ?? [];\n return type === this.config.guard.tokenPrefix && token ? token : null;\n }\n\n // Extracts the refresh token from the configured httpOnly cookie\n getRefreshToken(): string | null {\n try {\n const cookies = (this.request as unknown as { cookies?: Record<string, string> }).cookies;\n if (cookies && typeof cookies === 'object') {\n const refreshToken = cookies[this.config.cookie.refreshCookieName];\n if (refreshToken) {\n return refreshToken;\n }\n }\n return null;\n } catch (_error: unknown) {\n return null;\n }\n }\n\n // Returns the value of a specific request header by key\n getHeader(key: string): string | string[] | undefined {\n return this.request.headers?.[key];\n }\n\n // Returns the request hostname (without port)\n getHostname(): string {\n return this.request.hostname ?? '';\n }\n\n // Returns all request headers\n getAllHeaders(): FastifyRequest['headers'] {\n return this.request.headers || {};\n }\n\n // Returns the HTTP method\n getMethod(): string {\n return this.request.method ?? '';\n }\n\n /**\n * Returns the path with any query string stripped.\n *\n * Signature canonicals cover the path only, so a query string must not be part\n * of what gets signed or verified.\n */\n getPath(): string {\n const url = this.request.url ?? '';\n return url.split('?')[0] ?? url;\n }\n\n /**\n * Returns the raw query string, without the leading `?`.\n *\n * Companion to `getPath()`, which strips it. Signed separately so a REST request's\n * filters are covered — `getPath()` alone would sign `GET /people?search=salt` as\n * though the filter were not there.\n */\n getQuery(): string {\n const url = this.request.url ?? '';\n const index = url.indexOf('?');\n return index === -1 ? '' : url.slice(index + 1);\n }\n\n /**\n * Returns the raw request body, as `fastify-raw-body` leaves it.\n *\n * Read structurally rather than through a module augmentation: that plugin is the\n * consuming server's dependency, not this SDK's, and declaring `rawBody` here\n * would collide with the plugin's own declaration the moment the two drift.\n *\n * A server that has not registered it yields `undefined`, which hashes as an empty\n * body and therefore fails any signature made over real bytes — refused, never\n * waved through.\n */\n getRawBody(): string | Buffer | undefined {\n return (this.request as unknown as { rawBody?: string | Buffer }).rawBody;\n }\n}\n","import type { FastifyRequest } from 'fastify';\n\n// Unwraps the @Inject(REQUEST) value to the real Fastify request (GraphQL injects a { req, reply } context).\nexport function resolveInjectedRequest(injected: FastifyRequest): FastifyRequest {\n const candidate = injected as unknown as { headers?: unknown; req?: FastifyRequest };\n if (candidate && candidate.headers === undefined && candidate.req) {\n return candidate.req;\n }\n return injected;\n}\n","import {\n type CanActivate,\n type ExecutionContext,\n ForbiddenException,\n Inject,\n Injectable,\n Logger,\n Scope,\n UnauthorizedException,\n} from '@nestjs/common';\nimport { SSE_METADATA } from '@nestjs/common/constants';\nimport { Reflector } from '@nestjs/core';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport '../../types/fastify-augmentation';\nimport { getRequestFromContext, getResponseFromContext } from '../../context';\nimport { RequestService } from '../../request/services/request.service';\nimport { APP_SESSION_TYPE } from '../app-request';\nimport { AUTH_CONFIG, type AuthConfig } from '../auth.config';\nimport { REQUIRE_APP_KEY } from '../decorators/require-app.decorator';\nimport { REQUIRE_SESSION_KEY } from '../decorators/require-session.decorator';\nimport { SKIP_CSRF_KEY } from '../decorators/skip-csrf.decorator';\nimport { TokenService } from '../services/token.service';\n\ninterface FastifyInstanceWithCsrf {\n csrfProtection?: (req: FastifyRequest, reply: FastifyReply, next: (err?: Error) => void) => void;\n}\n\ntype PatchableReply = { send: (...args: unknown[]) => unknown };\n\n@Injectable({ scope: Scope.REQUEST })\nexport class VrittiAuthGuard implements CanActivate {\n private readonly logger = new Logger(VrittiAuthGuard.name);\n\n constructor(\n private readonly reflector: Reflector,\n private readonly requestService: RequestService,\n private readonly tokenService: TokenService,\n @Inject(AUTH_CONFIG) private readonly config: AuthConfig,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = getRequestFromContext(context);\n const reply = getResponseFromContext(context);\n const route = `${request.method} ${request.url}`;\n\n // Attach auth config to request so decorators can access it without injection\n request.authConfig = this.config;\n\n // CSRF is skipped via @SkipCsrf() or when the request transport is CSRF-exempt (e.g. 'graphql')\n const csrfExemptTransports = this.config.guard.csrfExemptTransports ?? [];\n const skipCsrf =\n this.reflector.getAllAndOverride<boolean>(SKIP_CSRF_KEY, [context.getHandler(), context.getClass()]) ||\n csrfExemptTransports.includes(context.getType<string>());\n\n // @RequireApp() authenticates an external app by its request signature.\n //\n // Placed before the public check on purpose: an app endpoint is not anonymous,\n // so it must never fall through to the @Public() branch. Returning from here\n // also means CSRF never runs on an app request — a signed server-to-server call\n // carries no cookie for CSRF to protect, and that holds on REST too, which a\n // transport-level exemption would not cover.\n const requiredAppTypes = this.reflector.getAllAndOverride<string[]>(REQUIRE_APP_KEY, [\n context.getHandler(),\n context.getClass(),\n ]);\n if (requiredAppTypes) {\n return this.handleAppAuth(request, requiredAppTypes, route);\n }\n\n // @Public() endpoints skip auth, while preserving their current CSRF behavior\n const isPublic = this.reflector.getAllAndOverride<boolean>('isPublic', [context.getHandler(), context.getClass()]);\n if (isPublic) {\n if (!skipCsrf) {\n await this.validateCsrf(request, reply);\n }\n this.logger.debug(`${route} — public endpoint, skipping auth`);\n return true;\n }\n\n // @RequireSession() restricts access to specific session types\n const requiredSessionTypes = this.reflector.getAllAndOverride<string[]>(REQUIRE_SESSION_KEY, [\n context.getHandler(),\n context.getClass(),\n ]);\n\n // SSE endpoints authenticate via refresh token cookie (EventSource cannot send Authorization headers)\n const isSseEndpoint = this.reflector.get<boolean>(SSE_METADATA, context.getHandler());\n if (isSseEndpoint) {\n this.logger.debug(`${route} — SSE endpoint, authenticating via refresh cookie`);\n return this.handleSseAuth(request, requiredSessionTypes);\n }\n\n const sessionType = await this.handleHttpAuth(request, requiredSessionTypes);\n\n const csrfExemptSessionTypes = this.config.guard.csrfExemptSessionTypes ?? [];\n if (!skipCsrf && !csrfExemptSessionTypes.includes(sessionType)) {\n await this.validateCsrf(request, reply);\n }\n\n return true;\n }\n\n /**\n * Authenticates a signed request from an external app.\n *\n * The credential lookup and the signature check are the consuming server's job —\n * it is the only side with a database — so this delegates to\n * `guard.onAuthenticated`, the same hook the session path already uses to resolve\n * organization and workspace context. That server fills in `organizationId`,\n * `appType` and whatever else it knows.\n *\n * What stays here is the part only this side can do: reading the decorator's\n * metadata and enforcing the app-type filter against what the server resolved.\n *\n * An empty list means \"any type\" — the caller is still authenticated. That makes\n * `@RequireApp()` with no arguments mean what it reads like.\n */\n private async handleAppAuth(request: FastifyRequest, requiredAppTypes: string[], route: string): Promise<boolean> {\n const onAuthenticated = this.config.guard.onAuthenticated;\n if (!onAuthenticated) {\n // Fail closed. Without the hook nothing can verify the signature, and\n // treating the request as authenticated would leave the endpoint wide open.\n this.logger.error(`${route} — @RequireApp() requires guard.onAuthenticated to be configured`);\n throw new UnauthorizedException('App authentication is not configured');\n }\n\n // Seeded so the hook has something to populate, matching how the session path\n // hands it an object to mutate.\n const sessionInfo = { sessionType: APP_SESSION_TYPE } as NonNullable<FastifyRequest['sessionInfo']>;\n request.sessionInfo = sessionInfo;\n\n await onAuthenticated(this.requestService, sessionInfo);\n\n if (requiredAppTypes.length && !requiredAppTypes.includes(sessionInfo.appType ?? '')) {\n this.logger.warn(\n `${route} — app type ${sessionInfo.appType ?? 'unknown'} not in allowed: [${requiredAppTypes.join(', ')}]`,\n );\n // Deliberately the same error the server raises for an unknown client, a\n // revoked one or a bad signature. Distinguishing them would tell whoever is\n // probing which of the four they hit.\n throw new UnauthorizedException('This client is not recognised.');\n }\n\n // No organization in this message on purpose: which field holds the tenant is\n // the consuming server's augmentation, not something this side knows about.\n this.logger.debug(`${route} — authenticated app (${sessionInfo.appType})`);\n return true;\n }\n\n // Authenticates standard HTTP requests using the access token from Authorization header\n private async handleHttpAuth(request: FastifyRequest, requiredSessionTypes?: string[]): Promise<string> {\n const route = `${request.method} ${request.url}`;\n\n const accessToken = this.requestService.getAccessToken();\n if (!accessToken) {\n this.logger.warn(`${route} — no access token found`);\n throw new UnauthorizedException('Access token not found');\n }\n\n const decoded = this.tokenService.validateAccessToken(accessToken);\n\n const refreshTokenBindingExemptSessionTypes = this.config.guard.refreshTokenBindingExemptSessionTypes ?? [];\n\n if (!refreshTokenBindingExemptSessionTypes.includes(decoded.sessionType)) {\n const refreshToken = this.requestService.getRefreshToken();\n if (!refreshToken) {\n throw new UnauthorizedException('Session validation failed');\n }\n this.tokenService.validateTokenBinding(decoded, refreshToken);\n }\n\n // Validate session type access (only if @RequireSession specifies types)\n if (requiredSessionTypes?.length && !requiredSessionTypes.includes(decoded.sessionType)) {\n this.logger.warn(\n `${route} — session type ${decoded.sessionType} not in allowed: [${requiredSessionTypes.join(', ')}]`,\n );\n throw new UnauthorizedException(`${decoded.sessionType} sessions cannot access this endpoint`);\n }\n\n // Attach session info to request — spread full decoded token (includes metadata fields)\n const { tokenType: _tokenType, refreshTokenHash: _hash, exp: _exp, iat: _iat, ...sessionInfo } = decoded;\n request.sessionInfo = sessionInfo;\n\n // Call onAuthenticated callback if configured\n const onAuthenticated = this.config.guard.onAuthenticated;\n if (onAuthenticated) {\n await onAuthenticated(this.requestService, request.sessionInfo);\n }\n\n this.logger.debug(`${route} — authenticated user: ${decoded.userId} (${decoded.sessionType})`);\n return decoded.sessionType;\n }\n\n // Authenticates SSE connections using the refresh token httpOnly cookie\n private handleSseAuth(request: FastifyRequest, requiredSessionTypes?: string[]): boolean {\n const refreshToken = this.requestService.getRefreshToken();\n if (!refreshToken) {\n this.logger.warn(`SSE ${request.url} — no refresh token cookie`);\n throw new UnauthorizedException('Authentication required');\n }\n\n const decoded = this.tokenService.validateRefreshToken(refreshToken);\n\n if (requiredSessionTypes?.length && !requiredSessionTypes.includes(decoded.sessionType)) {\n this.logger.warn(`SSE ${request.url} — session type ${decoded.sessionType} not allowed`);\n throw new UnauthorizedException(`${decoded.sessionType} sessions cannot access this endpoint`);\n }\n\n const { tokenType: _tokenType, exp: _exp, iat: _iat, ...sessionInfo } = decoded;\n request.sessionInfo = sessionInfo;\n\n this.logger.debug(`SSE ${request.url} — authenticated user: ${decoded.userId} (${decoded.sessionType})`);\n return true;\n }\n\n // Validates CSRF token for state-changing requests\n private async validateCsrf(request: FastifyRequest, reply: FastifyReply): Promise<void> {\n const safeMethods = ['GET', 'HEAD', 'OPTIONS'];\n if (safeMethods.includes(request.method)) return;\n\n try {\n const fastifyInstance = request.server as unknown as FastifyInstanceWithCsrf;\n const csrfProtection = fastifyInstance.csrfProtection;\n if (!csrfProtection) {\n throw new ForbiddenException('CSRF protection not configured');\n }\n\n await new Promise<void>((resolve, reject) => {\n const originalSend = reply.send.bind(reply);\n (reply as PatchableReply).send = () => {\n (reply as PatchableReply).send = originalSend as PatchableReply['send'];\n reject(new Error('CSRF validation failed'));\n return reply;\n };\n\n csrfProtection(request, reply, (err?: Error) => {\n (reply as PatchableReply).send = originalSend as PatchableReply['send'];\n if (err) reject(err);\n else resolve();\n });\n });\n } catch (_error: unknown) {\n this.logger.warn(`${request.method} ${request.url} — CSRF validation failed`);\n throw new ForbiddenException({\n errors: [{ field: 'csrf', message: 'Invalid or missing CSRF token' }],\n message: 'CSRF validation failed',\n });\n }\n }\n}\n","import type { ArgumentsHost } from '@nestjs/common';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { RequestExtractor } from '../context.types';\n\ninterface GqlContext {\n req: FastifyRequest;\n reply?: FastifyReply;\n}\n\nexport const graphqlExtractor: RequestExtractor = {\n getRequest: (host: ArgumentsHost) => host.getArgByIndex<GqlContext>(2).req,\n getResponse: (host: ArgumentsHost) => host.getArgByIndex<GqlContext>(2).reply as FastifyReply,\n};\n","import type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { RequestExtractor } from '../context.types';\n\n// Default transport: standard HTTP via the Fastify adapter.\nexport const httpExtractor: RequestExtractor = {\n getRequest: (host) => host.switchToHttp().getRequest<FastifyRequest>(),\n getResponse: (host) => host.switchToHttp().getResponse<FastifyReply>(),\n};\n","import type { RequestExtractor, TransportType } from './context.types';\nimport { graphqlExtractor } from './extractors/graphql.extractor';\nimport { httpExtractor } from './extractors/http.extractor';\n\nconst registry = new Map<TransportType, RequestExtractor>([\n ['http', httpExtractor],\n ['graphql', graphqlExtractor],\n]);\n\n// Registers (or overrides) the extractor for a transport type without modifying the SDK\nexport function registerTransport(type: TransportType, extractor: RequestExtractor): void {\n registry.set(type, extractor);\n}\n\n// Resolves the extractor for the host's reported transport, falling back to HTTP.\nexport function resolveExtractor(type: string): RequestExtractor {\n return registry.get(type as TransportType) ?? httpExtractor;\n}\n","import type { ArgumentsHost } from '@nestjs/common';\nimport type { FastifyRequest } from 'fastify';\nimport { resolveExtractor } from './context.registry';\n\n// Returns the underlying Fastify request for any registered transport, via the transport registry.\nexport function getRequestFromContext(host: ArgumentsHost): FastifyRequest {\n return resolveExtractor(host.getType()).getRequest(host);\n}\n","import type { ArgumentsHost } from '@nestjs/common';\nimport type { FastifyReply } from 'fastify';\nimport { resolveExtractor } from './context.registry';\n\n// Returns the underlying Fastify reply for any registered transport (HTTP, GraphQL, ...).\nexport function getResponseFromContext(host: ArgumentsHost): FastifyReply {\n return resolveExtractor(host.getType()).getResponse(host);\n}\n","import { SetMetadata } from '@nestjs/common';\n\n// Restricts endpoint access to signed requests from specific app types\nexport const REQUIRE_APP_KEY = 'requiredAppTypes';\n\n/**\n * Authenticates an external app by its request signature, and restricts the\n * endpoint to the given app types.\n *\n * ```ts\n * @RequireApp(AppTypeValues.GRAPHQL)\n * @Mutation(() => Person, { name: 'createPerson' })\n * async createPerson(@Args('input') input: CreatePersonInput) {}\n * ```\n *\n * Metadata only, exactly like `@RequireSession` — `VrittiAuthGuard` reads it and\n * branches. Nothing else is needed at the call site:\n *\n * - no `@Public()`, because the guard's app branch runs *before* the public check\n * - no `@UseGuards()`, because the global guard owns this\n * - no `@SkipCsrf()`, because that branch returns before CSRF is ever reached —\n * which also covers REST, where a transport-level exemption would not\n *\n * Types are compared as strings: the enum belongs to the consuming server's\n * schema, and this only ever compares. Passing none authenticates the caller\n * without restricting which kind it is.\n *\n * The consuming server resolves the credential in its `onAuthenticated` callback,\n * which is the only side with a database. See `GuardConfig.onAuthenticated`.\n */\nexport const RequireApp = (...types: string[]) => SetMetadata(REQUIRE_APP_KEY, types);\n","import { SetMetadata } from '@nestjs/common';\n\n// Restricts endpoint access to specific session types\nexport const REQUIRE_SESSION_KEY = 'requiredSessionTypes';\nexport const RequireSession = (...types: string[]) => SetMetadata(REQUIRE_SESSION_KEY, types);\n","import { SetMetadata } from '@nestjs/common';\n\nexport const SKIP_CSRF_KEY = 'skipCsrf';\n\nexport const SkipCsrf = () => SetMetadata(SKIP_CSRF_KEY, true);\n","import { Inject, Injectable, Logger, UnauthorizedException } from '@nestjs/common';\nimport { JwtService, type JwtSignOptions } from '@nestjs/jwt';\nimport type { FastifyRequest } from 'fastify';\nimport '../../types/fastify-augmentation';\nimport { parseExpiryToMs } from '../../utils/time.utils';\nimport {\n AUTH_CONFIG,\n type AuthConfig,\n type DecodedAccessToken,\n type DecodedRefreshToken,\n TokenType,\n} from '../auth.config';\nimport { hashToken, verifyTokenHash } from '../utils/token-hash.util';\n\nexport type { DecodedAccessToken, DecodedRefreshToken };\n\n// Session info type from Fastify augmentation\ntype SessionInfo = NonNullable<FastifyRequest['sessionInfo']>;\n\ninterface TokenError extends Error {\n name: 'TokenExpiredError' | 'JsonWebTokenError' | 'NotBeforeError';\n}\n\n// Handles all token operations — generation, validation, and binding verification\n@Injectable()\nexport class TokenService {\n private readonly logger = new Logger(TokenService.name);\n\n constructor(\n private readonly jwtService: JwtService,\n @Inject(AUTH_CONFIG) private readonly config: AuthConfig,\n ) {}\n\n // ---- Generation ----\n\n // Generates an access token bound to the given refresh token\n generateAccessToken(sessionInfo: SessionInfo, refreshToken: string): string {\n const { userId, sessionId, sessionType, ...metadata } = sessionInfo;\n return this.jwtService.sign(\n {\n sessionType,\n tokenType: TokenType.ACCESS,\n userId,\n sessionId,\n refreshTokenHash: hashToken(refreshToken),\n ...metadata,\n },\n { expiresIn: this.config.tokenExpiry.access },\n );\n }\n\n // Generates a refresh token for session persistence\n generateRefreshToken(sessionInfo: SessionInfo): string {\n const { userId, sessionId, sessionType, ...metadata } = sessionInfo;\n return this.jwtService.sign(\n { sessionType, tokenType: TokenType.REFRESH, userId, sessionId, ...metadata },\n { expiresIn: this.config.tokenExpiry.refresh },\n );\n }\n\n // Signs an arbitrary payload with optional JWT options\n sign(payload: object, options?: JwtSignOptions): string {\n return this.jwtService.sign(payload, options);\n }\n\n // Verifies a token and ensures it matches the expected token type\n verify(\n token: string,\n expectedType: TokenType,\n ): { userId: string; sessionId: string; sessionType: string; tokenType: TokenType } {\n try {\n const payload = this.jwtService.verify(token);\n\n if (payload.tokenType !== expectedType) {\n throw new Error(`Expected ${expectedType} token, got ${payload.tokenType}`);\n }\n\n return payload;\n } catch (error) {\n this.logger.error(`Failed to verify ${expectedType} token`, error);\n throw error;\n }\n }\n\n // Returns the expiry as a Date for the given token type\n getExpiryTime(type: TokenType): Date {\n return new Date(Date.now() + parseExpiryToMs(this.config.tokenExpiry[type]));\n }\n\n // Returns the token lifetime in seconds for the given type\n getExpiryInSeconds(type: TokenType): number {\n return Math.floor(parseExpiryToMs(this.config.tokenExpiry[type]) / 1000);\n }\n\n // ---- Validation ----\n\n // Decodes and validates an access token JWT\n validateAccessToken(token: string): DecodedAccessToken {\n try {\n const decoded = this.jwtService.verify<DecodedAccessToken>(token);\n\n if (decoded.tokenType !== TokenType.ACCESS) {\n throw new UnauthorizedException('Invalid token type');\n }\n\n return decoded;\n } catch (error) {\n if (error instanceof UnauthorizedException) throw error;\n\n const jwtError = error as TokenError;\n switch (jwtError.name) {\n case 'TokenExpiredError':\n throw new UnauthorizedException('Access token has expired');\n case 'JsonWebTokenError':\n throw new UnauthorizedException('Invalid access token');\n case 'NotBeforeError':\n throw new UnauthorizedException('Access token not yet valid');\n default:\n throw new UnauthorizedException('Access token validation failed');\n }\n }\n }\n\n // Decodes and validates a refresh token JWT\n validateRefreshToken(token: string): DecodedRefreshToken {\n try {\n const decoded = this.jwtService.verify<DecodedRefreshToken>(token);\n\n if (decoded.tokenType !== TokenType.REFRESH) {\n throw new UnauthorizedException('Invalid token type');\n }\n\n return decoded;\n } catch (error) {\n if (error instanceof UnauthorizedException) throw error;\n throw new UnauthorizedException('Invalid or expired session');\n }\n }\n\n // Validates that the access token is bound to the refresh token\n validateTokenBinding(accessToken: DecodedAccessToken, refreshToken: string): void {\n if (!verifyTokenHash(refreshToken, accessToken.refreshTokenHash)) {\n throw new UnauthorizedException('Session validation failed');\n }\n }\n}\n","// Parses a duration string (e.g. '10m', '1h', '30s', '7d') to milliseconds\nexport function parseExpiryToMs(expiry: string): number {\n const match = expiry.match(/^(\\d+)([smhdwy])$/);\n if (!match) throw new Error(`Invalid expiry format: ${expiry}`);\n\n const [, digits = '', unit = ''] = match;\n const value = Number.parseInt(digits, 10);\n const multipliers: Record<string, number> = {\n s: 1000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n w: 604_800_000,\n y: 31_536_000_000,\n };\n\n const multiplier = multipliers[unit];\n if (multiplier === undefined) throw new Error(`Invalid expiry format: ${expiry}`);\n\n return value * multiplier;\n}\n","import * as crypto from 'node:crypto';\n\n// Hashes a token using SHA-256\nexport function hashToken(token: string): string {\n return crypto.createHash('sha256').update(token).digest('hex');\n}\n\n// Verifies a token against its stored hash\nexport function verifyTokenHash(token: string, expectedHash: string): boolean {\n const computedHash = hashToken(token);\n if (computedHash.length !== expectedHash.length) return false;\n return crypto.timingSafeEqual(Buffer.from(computedHash, 'hex'), Buffer.from(expectedHash, 'hex'));\n}\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the bearer token from the Authorization header\nexport const AccessToken = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n const authHeader = request.headers.authorization;\n return authHeader?.replace('Bearer ', '') || '';\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the client IP from the request, working across HTTP and GraphQL transports\nexport const ClientIp = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n return getRequestFromContext(ctx).ip;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { AUTH_CONFIG_DEFAULTS } from '../auth.config';\n\n// Extracts the cookie domain from x-forwarded-host (injected by proxy), validated against baseDomain, falls back to baseDomain if invalid\nexport const CookieDomain = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n const domain = hostStr.split(':')[0] ?? hostStr;\n const baseDomain =\n request.authConfig?.cookie.refreshCookieDomain ?? AUTH_CONFIG_DEFAULTS.cookie.refreshCookieDomain ?? '';\n return domain.endsWith(`.${baseDomain}`) ? domain : baseDomain;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { AUTH_CONFIG_DEFAULTS } from '../auth.config';\n\n// Returns the configured refresh cookie name from request.authConfig\nexport const CookieName = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n return request.authConfig?.cookie.refreshCookieName ?? AUTH_CONFIG_DEFAULTS.cookie.refreshCookieName;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the request hostname from x-forwarded-host (set by reverse proxies and dev proxy) with fallback to request.hostname\nexport const Hostname = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n return hostStr.split(':')[0] ?? hostStr;\n});\n","import { SetMetadata } from '@nestjs/common';\n\nexport const Public = () => SetMetadata('isPublic', true);\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { UnauthorizedException } from '../../exceptions';\nimport { AUTH_CONFIG_DEFAULTS, type CookieConfig, type CookieSerializeOptions } from '../auth.config';\n\n// Builds cookie serialize options from the given cookie config\nfunction buildCookieOptionsForHost(cookieConfig: CookieConfig, hostname: string): CookieSerializeOptions {\n const baseDomain = cookieConfig.refreshCookieDomain;\n\n if (!baseDomain) {\n throw new Error('refreshCookieDomain must be configured before using @RefreshCookieOptions()');\n }\n\n if (!hostname.endsWith(`.${baseDomain}`)) {\n throw new UnauthorizedException('Invalid request host.');\n }\n\n return {\n httpOnly: true,\n secure: cookieConfig.refreshCookieSecure,\n sameSite: cookieConfig.refreshCookieSameSite,\n path: cookieConfig.refreshCookiePath,\n maxAge: cookieConfig.refreshCookieMaxAge,\n domain: hostname,\n };\n}\n\n// Returns refresh cookie options with domain scoped to the request subdomain (reads x-forwarded-host injected by proxy)\nexport const RefreshCookieOptions = createParamDecorator(\n (_data: unknown, ctx: ExecutionContext): CookieSerializeOptions => {\n const request = getRequestFromContext(ctx);\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n const domain = hostStr.split(':')[0] ?? hostStr;\n const cookieConfig: CookieConfig = request.authConfig?.cookie ?? AUTH_CONFIG_DEFAULTS.cookie;\n return buildCookieOptionsForHost(cookieConfig, domain);\n },\n);\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadGatewayException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Gateway', HttpStatus.BAD_GATEWAY);\n }\n}\n","import { HttpException, HttpStatus } from '@nestjs/common';\nimport type { FieldError } from '../types/error-response.types';\n\n// Re-export FieldError for backwards compatibility\nexport type { FieldError } from '../types/error-response.types';\n\nexport interface ProblemOptions {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\nexport abstract class HttpProblemException extends HttpException {\n constructor(detailOrOptions: string | ProblemOptions, httpStatus: HttpStatus) {\n const options = typeof detailOrOptions === 'string' ? { detail: detailOrOptions } : detailOrOptions;\n\n super(\n {\n type: options.type ?? 'about:blank',\n label: options.label,\n detail: options.detail,\n errors: options.errors ?? [],\n },\n httpStatus,\n );\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadRequestException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Request', HttpStatus.BAD_REQUEST);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ConflictException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Conflict', HttpStatus.CONFLICT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ForbiddenException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Forbidden', HttpStatus.FORBIDDEN);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class GoneException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Gone', HttpStatus.GONE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class InternalServerErrorException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Internal Server Error', HttpStatus.INTERNAL_SERVER_ERROR);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class MethodNotAllowedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Method Not Allowed', HttpStatus.METHOD_NOT_ALLOWED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotAcceptableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Acceptable', HttpStatus.NOT_ACCEPTABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotFoundException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Found', HttpStatus.NOT_FOUND);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotImplementedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Implemented', HttpStatus.NOT_IMPLEMENTED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class PayloadTooLargeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Payload Too Large', HttpStatus.PAYLOAD_TOO_LARGE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class RequestTimeoutException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Request Timeout', HttpStatus.REQUEST_TIMEOUT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ServiceUnavailableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Service Unavailable', HttpStatus.SERVICE_UNAVAILABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class TooManyRequestsException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Too Many Requests', HttpStatus.TOO_MANY_REQUESTS);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnauthorizedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unauthorized', HttpStatus.UNAUTHORIZED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnprocessableEntityException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unprocessable Entity', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnsupportedMediaTypeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unsupported Media Type', HttpStatus.UNSUPPORTED_MEDIA_TYPE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ValidationException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Validation Failed', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { AUTH_CONFIG_DEFAULTS } from '../auth.config';\n\nexport const RefreshTokenCookie = createParamDecorator((_data: unknown, ctx: ExecutionContext): string | undefined => {\n const request = getRequestFromContext(ctx);\n const cookies = request.cookies ?? {};\n const cookieName = request.authConfig?.cookie.refreshCookieName ?? AUTH_CONFIG_DEFAULTS.cookie.refreshCookieName;\n return cookies[cookieName] as string | undefined;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport '../../types/fastify-augmentation';\n\nexport interface SessionInfo {\n userId: string;\n sessionId: string;\n sessionType: string;\n}\n\n// Returns full decoded session info from request.sessionInfo (set by VrittiAuthGuard)\nexport const SessionData = createParamDecorator((_data: unknown, ctx: ExecutionContext): SessionInfo => {\n const request = getRequestFromContext(ctx);\n const sessionInfo = request.sessionInfo;\n\n if (!sessionInfo?.sessionId) {\n throw new Error('Session info not found on request. Ensure route is protected by auth guard.');\n }\n\n return {\n userId: sessionInfo.userId,\n sessionId: sessionInfo.sessionId,\n sessionType: sessionInfo.sessionType,\n };\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the subdomain from the request host, resolved the same way as @Hostname() — x-forwarded-host\n// (set by dev and reverse proxies) with a fallback to request.hostname. Origin is deliberately not used:\n// it is client-controlled and can disagree with the Host header, which would mint a session for one\n// subdomain that the auth guard's host check then rejects against another.\nexport const Subdomain = createParamDecorator((_data: unknown, ctx: ExecutionContext): string | undefined => {\n const request = getRequestFromContext(ctx);\n\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n if (!hostStr) return undefined;\n\n const host = hostStr.split(':')[0] ?? hostStr;\n return host.split('.')[0] || undefined;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the User-Agent header from the request, working across HTTP and GraphQL transports\nexport const UserAgent = createParamDecorator((_data: unknown, ctx: ExecutionContext): string | undefined => {\n const userAgent = getRequestFromContext(ctx).headers['user-agent'];\n return Array.isArray(userAgent) ? userAgent[0] : userAgent;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport '../../types/fastify-augmentation';\n\n// Extracts userId from request.sessionInfo (set by VrittiAuthGuard)\nexport const UserId = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n const sessionInfo = request.sessionInfo;\n\n if (!sessionInfo?.userId) {\n throw new Error('User ID not found on request. Ensure route is protected by auth guard.');\n }\n\n return sessionInfo.userId;\n});\n"],"mappings":"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","names":["createHash","createPrivateKey","createPublicKey","sign","verify","WORKSPACE_HEADER_ORDER","CLIENT_ID_HEADER","PARTY_ID_HEADER","MAX_CLOCK_SKEW_SECONDS","APP_SESSION_TYPE","AUTH_CONFIG","Symbol","AUTH_CONFIG_DEFAULTS","cookie","refreshCookieName","refreshCookieMaxAge","refreshCookiePath","refreshCookieSecure","process","env","NODE_ENV","refreshCookieSameSite","refreshCookieDomain","guard","authHeaderName","tokenPrefix","csrfExemptSessionTypes","csrfExemptTransports","refreshTokenBindingExemptSessionTypes","TokenType","Global","Module","ConfigModule","ConfigService","APP_GUARD","Reflector","JwtModule","Global","Module","Inject","Injectable","Scope","REQUEST","resolveInjectedRequest","injected","candidate","headers","undefined","req","RequestService","injectedRequest","config","request","resolveInjectedRequest","getAccessToken","authHeader","headers","guard","authHeaderName","type","token","split","tokenPrefix","getRefreshToken","cookies","refreshToken","cookie","refreshCookieName","_error","getHeader","key","getHostname","hostname","getAllHeaders","getMethod","method","getPath","url","getQuery","index","indexOf","slice","getRawBody","rawBody","scope","Scope","REQUEST","RequestModule","providers","RequestService","exports","ForbiddenException","Inject","Injectable","Logger","Scope","UnauthorizedException","SSE_METADATA","Reflector","graphqlExtractor","getRequest","host","getArgByIndex","req","getResponse","reply","httpExtractor","getRequest","host","switchToHttp","getResponse","registry","Map","httpExtractor","graphqlExtractor","resolveExtractor","type","registry","get","httpExtractor","getRequestFromContext","host","resolveExtractor","getType","getRequest","getResponseFromContext","host","resolveExtractor","getType","getResponse","SetMetadata","REQUIRE_APP_KEY","RequireApp","types","SetMetadata","SetMetadata","REQUIRE_SESSION_KEY","RequireSession","types","SetMetadata","SetMetadata","SKIP_CSRF_KEY","SkipCsrf","SetMetadata","Inject","Injectable","Logger","UnauthorizedException","JwtService","parseExpiryToMs","expiry","match","Error","digits","unit","value","Number","parseInt","multipliers","s","m","h","d","w","y","multiplier","undefined","crypto","hashToken","token","createHash","update","digest","verifyTokenHash","expectedHash","computedHash","length","timingSafeEqual","Buffer","from","TokenService","logger","Logger","name","jwtService","config","generateAccessToken","sessionInfo","refreshToken","userId","sessionId","sessionType","metadata","sign","tokenType","TokenType","ACCESS","refreshTokenHash","hashToken","expiresIn","tokenExpiry","access","generateRefreshToken","REFRESH","refresh","payload","options","verify","token","expectedType","Error","error","getExpiryTime","type","Date","now","parseExpiryToMs","getExpiryInSeconds","Math","floor","validateAccessToken","decoded","UnauthorizedException","jwtError","validateRefreshToken","validateTokenBinding","accessToken","verifyTokenHash","VrittiAuthGuard","logger","Logger","name","reflector","requestService","tokenService","config","canActivate","context","request","getRequestFromContext","reply","getResponseFromContext","route","method","url","authConfig","csrfExemptTransports","guard","skipCsrf","getAllAndOverride","SKIP_CSRF_KEY","getHandler","getClass","includes","getType","requiredAppTypes","REQUIRE_APP_KEY","handleAppAuth","isPublic","validateCsrf","debug","requiredSessionTypes","REQUIRE_SESSION_KEY","isSseEndpoint","get","SSE_METADATA","handleSseAuth","sessionType","handleHttpAuth","csrfExemptSessionTypes","onAuthenticated","error","UnauthorizedException","sessionInfo","APP_SESSION_TYPE","length","appType","warn","join","accessToken","getAccessToken","decoded","validateAccessToken","refreshTokenBindingExemptSessionTypes","refreshToken","getRefreshToken","validateTokenBinding","tokenType","_tokenType","refreshTokenHash","_hash","exp","_exp","iat","_iat","userId","validateRefreshToken","safeMethods","fastifyInstance","server","csrfProtection","ForbiddenException","Promise","resolve","reject","originalSend","send","bind","Error","err","_error","errors","field","message","scope","Scope","REQUEST","mergeWithDefaults","input","tokenExpiry","cookie","AUTH_CONFIG_DEFAULTS","guard","AuthConfigModule","forRootAsync","options","module","imports","ConfigModule","RequestModule","JwtModule","registerAsync","inject","ConfigService","useFactory","config","secret","getOrThrow","signOptions","algorithm","providers","provide","Reflector","useClass","APP_GUARD","VrittiAuthGuard","AUTH_CONFIG","args","TokenService","exports","createParamDecorator","AccessToken","createParamDecorator","_data","ctx","request","getRequestFromContext","authHeader","headers","authorization","replace","createParamDecorator","ClientIp","createParamDecorator","_data","ctx","getRequestFromContext","ip","createParamDecorator","CookieDomain","createParamDecorator","_data","ctx","request","getRequestFromContext","forwarded","headers","raw","Array","isArray","hostStr","hostname","domain","split","baseDomain","authConfig","cookie","refreshCookieDomain","AUTH_CONFIG_DEFAULTS","endsWith","createParamDecorator","CookieName","createParamDecorator","_data","ctx","request","getRequestFromContext","authConfig","cookie","refreshCookieName","AUTH_CONFIG_DEFAULTS","createParamDecorator","Hostname","createParamDecorator","_data","ctx","request","getRequestFromContext","forwarded","headers","raw","Array","isArray","hostStr","hostname","split","SetMetadata","Public","SetMetadata","createParamDecorator","HttpStatus","HttpException","HttpProblemException","HttpException","detailOrOptions","httpStatus","options","detail","type","label","errors","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","UnauthorizedException","HttpProblemException","detailOrOptions","HttpStatus","UNAUTHORIZED","HttpStatus","HttpStatus","HttpStatus","buildCookieOptionsForHost","cookieConfig","hostname","baseDomain","refreshCookieDomain","Error","endsWith","UnauthorizedException","httpOnly","secure","refreshCookieSecure","sameSite","refreshCookieSameSite","path","refreshCookiePath","maxAge","refreshCookieMaxAge","domain","RefreshCookieOptions","createParamDecorator","_data","ctx","request","getRequestFromContext","forwarded","headers","raw","Array","isArray","hostStr","split","authConfig","cookie","AUTH_CONFIG_DEFAULTS","createParamDecorator","RefreshTokenCookie","createParamDecorator","_data","ctx","request","getRequestFromContext","cookies","cookieName","authConfig","cookie","refreshCookieName","AUTH_CONFIG_DEFAULTS","createParamDecorator","SessionData","createParamDecorator","_data","ctx","request","getRequestFromContext","sessionInfo","sessionId","Error","userId","sessionType","createParamDecorator","Subdomain","createParamDecorator","_data","ctx","request","getRequestFromContext","forwarded","headers","raw","Array","isArray","hostStr","hostname","undefined","host","split","createParamDecorator","UserAgent","createParamDecorator","_data","ctx","userAgent","getRequestFromContext","headers","Array","isArray","createParamDecorator","UserId","createParamDecorator","_data","ctx","request","getRequestFromContext","sessionInfo","userId","Error"]}
|
|
@@ -116,6 +116,11 @@ __name(filterGrantedByDeps, "filterGrantedByDeps");
|
|
|
116
116
|
|
|
117
117
|
// src/catalog-resolver/types.ts
|
|
118
118
|
var PLATFORMS = [
|
|
119
|
+
"web",
|
|
120
|
+
"mobile",
|
|
121
|
+
"app"
|
|
122
|
+
];
|
|
123
|
+
var UI_PLATFORMS = [
|
|
119
124
|
"web",
|
|
120
125
|
"mobile"
|
|
121
126
|
];
|
|
@@ -156,7 +161,10 @@ function buildSiteCatalog(snapshot, businessCode, planCode, siteLocks, bucket, s
|
|
|
156
161
|
...business.apps
|
|
157
162
|
].sort((a, b) => a.name.localeCompare(b.name));
|
|
158
163
|
for (const app of sortedApps) {
|
|
159
|
-
const businessAppFeatures = app.features.filter((ref) => scope === void 0 || ref.scope === scope).map((ref) => snapshot.features?.[snapshotFeatureKey(ref.code, ref.scope)]).filter((f) => !!f &&
|
|
164
|
+
const businessAppFeatures = app.features.filter((ref) => scope === void 0 || ref.scope === scope).map((ref) => snapshot.features?.[snapshotFeatureKey(ref.code, ref.scope)]).filter((f) => !!f && // A UI bucket needs something to render, so a feature shipping no microfrontend is dropped.
|
|
165
|
+
// The `app` bucket renders nothing — dropping headless features there would make them
|
|
166
|
+
// permanently ungrantable to an API client.
|
|
167
|
+
(bucket === "app" || !!(f.microfrontends?.web || f.microfrontends?.mobile)) && (siteType === void 0 || featureAppliesAtNode(f.applicableSiteTypes, siteType)));
|
|
160
168
|
if (businessAppFeatures.length === 0) continue;
|
|
161
169
|
for (const feature of businessAppFeatures) {
|
|
162
170
|
const membership = plan?.unlockedPermissions?.[feature.code];
|
|
@@ -322,9 +330,14 @@ function composeRoleGrants(params) {
|
|
|
322
330
|
__name(composeRoleGrants, "composeRoleGrants");
|
|
323
331
|
|
|
324
332
|
// src/catalog-resolver/resolve-user-features.ts
|
|
333
|
+
var EMPTY_ROUTE = {
|
|
334
|
+
remoteEntry: "",
|
|
335
|
+
exposedModule: "",
|
|
336
|
+
routePrefix: ""
|
|
337
|
+
};
|
|
325
338
|
function resolveUserFeatures(params) {
|
|
326
339
|
const { snapshot, businessCode, planCode, siteLocks, roleFeatures, platform, siteType, scope, availableServices } = params;
|
|
327
|
-
const bucket = platform === "web" ? "web" : "mobile";
|
|
340
|
+
const bucket = platform === "web" ? "web" : platform === "app" ? "app" : "mobile";
|
|
328
341
|
const featureByCode = /* @__PURE__ */ __name((code) => scope ? snapshot.features?.[snapshotFeatureKey(code, scope)] : findFeatureByCode(snapshot, code), "featureByCode");
|
|
329
342
|
const catalog = buildSiteCatalog(snapshot, businessCode, planCode, siteLocks, bucket, siteType, scope, availableServices);
|
|
330
343
|
const catalogMap = new Map(catalog.map((f) => [
|
|
@@ -363,7 +376,7 @@ function resolveUserFeatures(params) {
|
|
|
363
376
|
for (const [code, permsSet] of grantedFeatures) {
|
|
364
377
|
const catalogEntry = catalogMap.get(code);
|
|
365
378
|
if (!catalogEntry) continue;
|
|
366
|
-
const route = pickRouteForPlatform(catalogEntry, platform);
|
|
379
|
+
const route = bucket === "app" ? EMPTY_ROUTE : pickRouteForPlatform(catalogEntry, platform);
|
|
367
380
|
if (!route) continue;
|
|
368
381
|
const featureDeps = buildDependsMap(featureByCode(code)?.permissions ?? []);
|
|
369
382
|
const permByCode = new Map((catalogEntry.permissions ?? []).map((p) => [
|
|
@@ -459,8 +472,10 @@ function buildMatrix(snapshot, businessCode, planCode, siteLocks, allScopes, sit
|
|
|
459
472
|
const feature = snapshot.features?.[snapshotFeatureKey(code, ref.scope)];
|
|
460
473
|
if (!feature) continue;
|
|
461
474
|
if (siteType !== void 0 && !featureAppliesAtNode(feature.applicableSiteTypes, siteType)) continue;
|
|
462
|
-
const platforms =
|
|
463
|
-
|
|
475
|
+
const platforms = [
|
|
476
|
+
...UI_PLATFORMS.filter((p) => !!feature.microfrontends?.[p]),
|
|
477
|
+
"app"
|
|
478
|
+
];
|
|
464
479
|
const membership = plan.unlockedPermissions?.[code];
|
|
465
480
|
const featureInPlan = isPlanMember(membership);
|
|
466
481
|
const siteEntry = siteLocks?.[code];
|
|
@@ -484,7 +499,8 @@ function buildMatrix(snapshot, businessCode, planCode, siteLocks, allScopes, sit
|
|
|
484
499
|
label: p.label,
|
|
485
500
|
dependsOn: p.dependsOn ?? [],
|
|
486
501
|
web: cell("web"),
|
|
487
|
-
mobile: cell("mobile")
|
|
502
|
+
mobile: cell("mobile"),
|
|
503
|
+
app: cell("app")
|
|
488
504
|
};
|
|
489
505
|
});
|
|
490
506
|
features.push({
|