@yuva-devlab/auth-server 0.3.0 → 1.0.0

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/index.cjs CHANGED
@@ -40,7 +40,7 @@ var JwksValidator = class {
40
40
  keyCache = /* @__PURE__ */ new Map();
41
41
  jwksSetCache = null;
42
42
  constructor(config = {}) {
43
- this.jwksUri = config.jwksUri ?? (config.issuer ? `${config.issuer}/.well-known/jwks.json` : void 0) ?? (typeof process !== "undefined" ? process.env?.DEVLAB_JWKS_URI ?? (process.env?.DEVLAB_AUTH_URL ? `${process.env.DEVLAB_AUTH_URL}/.well-known/jwks.json` : void 0) : void 0) ?? "";
43
+ this.jwksUri = config.jwksUri ?? (config.issuer ? `${config.issuer}/.well-known/jwks.json` : void 0) ?? (typeof process !== "undefined" ? process.env?.DEVLAB_PORTAL_API_URL ? `${process.env.DEVLAB_PORTAL_API_URL}/.well-known/jwks.json` : void 0 : void 0) ?? "";
44
44
  this.audience = config.audience;
45
45
  this.issuer = config.issuer;
46
46
  this.cacheTtlMs = config.cacheTtlMs ?? DEFAULT_JWKS_CACHE_TTL_MS;
@@ -73,7 +73,7 @@ var JwksValidator = class {
73
73
  async fetchJwks() {
74
74
  if (!this.jwksUri) {
75
75
  throw new errors.UnauthorizedError(
76
- "JwksValidator: jwksUri, issuer, or DEVLAB_AUTH_URL must be configured"
76
+ "JwksValidator: jwksUri, issuer, or DEVLAB_PORTAL_API_URL must be configured"
77
77
  );
78
78
  }
79
79
  const now = Date.now();
@@ -193,7 +193,7 @@ var M2mApiKeyValidator = class {
193
193
  requiredScopes;
194
194
  keyCache = /* @__PURE__ */ new Map();
195
195
  constructor(config = {}) {
196
- this.authUrl = config.authUrl ?? config.portalUrl ?? process.env.DEVLAB_AUTH_URL ?? process.env.DEVLAB_PORTAL_URL;
196
+ this.authUrl = config.authUrl ?? config.portalUrl ?? process.env.DEVLAB_PORTAL_API_URL;
197
197
  this.cacheTtlMs = config.cacheTtlMs ?? DEFAULT_API_KEY_CACHE_TTL_MS;
198
198
  this.requiredClientId = config.requiredClientId ?? config.requiredAppId;
199
199
  this.requiredScopes = config.requiredScopes;
@@ -205,7 +205,7 @@ var M2mApiKeyValidator = class {
205
205
  }
206
206
  if (!this.authUrl) {
207
207
  throw new errors.UnauthorizedError(
208
- "M2mApiKeyValidator: authUrl or DEVLAB_AUTH_URL must be configured for validation"
208
+ "M2mApiKeyValidator: authUrl or DEVLAB_PORTAL_API_URL must be configured for validation"
209
209
  );
210
210
  }
211
211
  const now = Date.now();
@@ -250,9 +250,13 @@ var M2mApiKeyValidator = class {
250
250
  }
251
251
  enforceKeyConstraints(result) {
252
252
  if (this.requiredClientId && result.appId && result.appId !== this.requiredClientId) {
253
- throw new errors.ForbiddenError(
254
- "API key assigned to " + result.appId + ", required " + this.requiredClientId
255
- );
253
+ const keyScopes = result.scopes ?? [];
254
+ const hasWildcard = keyScopes.includes("*") || keyScopes.includes(`${this.requiredClientId}:*`) || keyScopes.includes(this.requiredClientId);
255
+ if (!hasWildcard) {
256
+ throw new errors.ForbiddenError(
257
+ "API key assigned to " + result.appId + ", required " + this.requiredClientId
258
+ );
259
+ }
256
260
  }
257
261
  if (this.requiredScopes && this.requiredScopes.length > 0) {
258
262
  const keyScopes = result.scopes ?? [];
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants/auth.constants.ts","../src/types/auth.types.ts","../src/token/jwt-validator.ts","../src/token/m2m-validator.ts","../src/guards/auth-guards.ts","../src/middleware/auth-middleware.ts"],"names":["AppStatus","loggerWithConfig","Logger","UnauthorizedError","crypto","logger","ForbiddenError"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @file packages/auth-server/src/constants/auth.constants.ts\n * @description Centralized constants and header keys for server-side authentication.\n * @module @yuva-devlab/auth-server\n */\n\n/** Standard Authorization HTTP header name (lowercase for case-insensitive lookup) */\nexport const DEFAULT_AUTH_HEADER = \"authorization\";\n\n/** Standard machine-to-machine API key HTTP header name */\nexport const DEFAULT_API_KEY_HEADER = \"x-api-key\";\n\n/** Standard Bearer token prefix */\nexport const DEFAULT_BEARER_PREFIX = \"Bearer \";\n\n/** Default cache time-to-live for fetched RS256 JWKS public keys (1 hour) */\nexport const DEFAULT_JWKS_CACHE_TTL_MS = 3_600_000;\n\n/** Default cache time-to-live for validated M2M API keys (5 minutes) */\nexport const DEFAULT_API_KEY_CACHE_TTL_MS = 300_000;\n\n/** Default public endpoints bypassing mandatory authentication */\nexport const DEFAULT_PUBLIC_PATHS = Object.freeze([\n \"/health\",\n \"/ready\",\n \"/live\",\n \"/metrics\",\n]);\n\n/** HTTP Status Code for unauthenticated requests */\nexport const HTTP_STATUS_UNAUTHORIZED = 401;\n\n/** HTTP Status Code for unauthorized / forbidden role requests */\nexport const HTTP_STATUS_FORBIDDEN = 403;\n\n/** HTTP Status Code returned when an application is deactivated or killed */\nexport const HTTP_STATUS_SERVICE_UNAVAILABLE = 503;\n","/**\n * @file packages/auth-server/src/types/auth.types.ts\n * @description Type definitions and contracts for server authentication, JWKS, and app lifecycle.\n * @module @yuva-devlab/auth-server\n */\n\n/**\n * Operational state of an application on the DevLab platform.\n */\nexport enum AppStatus {\n /** Application is fully operational and serving traffic */\n ACTIVE = \"ACTIVE\",\n /** Application has been administratively disabled / deactivated (Kill-Switch triggered) */\n INACTIVE = \"INACTIVE\",\n /** Application is undergoing scheduled maintenance */\n MAINTENANCE = \"MAINTENANCE\",\n}\n\n/**\n * Authenticated user profile representation.\n */\nexport interface AuthUser {\n /** Canonical user UUID */\n readonly id: string;\n /** Primary user email address */\n readonly email?: string;\n /** Granted role slugs */\n readonly roles: readonly string[];\n /** Tenant isolation partition ID */\n readonly tenantId?: string;\n /** Arbitrary profile metadata */\n readonly metadata?: Record<string, unknown>;\n}\n\n/**\n * Machine-to-machine API key credential.\n */\nexport interface ApiKey {\n /** Primary API key UUID */\n readonly id: string;\n /** Secure key string */\n readonly key: string;\n /** Human-readable key name or description */\n readonly name: string;\n /** Scoped roles granted to this key */\n readonly roles: readonly string[];\n /** Tenant ID partition */\n readonly tenantId?: string;\n /** Active status flag */\n readonly isEnabled: boolean;\n}\n\n/**\n * Evaluated authentication context attached to inbound requests.\n */\nexport interface AuthContext {\n /** Whether the request carries valid credentials */\n readonly authenticated: boolean;\n /** Authenticated user if authenticated via session/JWT */\n readonly user?: AuthUser;\n /** Authenticated API key if authenticated via machine credential */\n readonly apiKey?: ApiKey;\n /** Consolidated roles granted across user or API key */\n readonly roles: readonly string[];\n /** Whether the host application is currently active on the platform */\n readonly isAppActive: boolean;\n /** Detailed app status */\n readonly appStatus?: AppStatus;\n /** Tenant partition ID */\n readonly tenantId?: string;\n /** Distributed trace request identifier */\n readonly requestId?: string;\n}\n\n/**\n * Configuration for verifying RS256 JWTs against a remote JWKS endpoint.\n */\nexport interface JwksConfig {\n /** JWKS endpoint URI exposing JSON Web Keys */\n readonly jwksUri: string;\n /** Expected audience claim */\n readonly audience?: string;\n /** Expected issuer claim */\n readonly issuer?: string;\n /** In-memory cache duration for public keys in milliseconds */\n readonly cacheTtlMs?: number;\n}\n\n/**\n * Decoded JWT claims payload.\n */\n\n/**\n * Result of validating a Machine-to-Machine API key against the DevLab Portal.\n */\nexport interface ApiKeyValidationResult {\n /** Whether the API key is active, valid, and unexpired */\n readonly valid: boolean;\n /** Primary API key UUID */\n readonly keyId?: string;\n /** Name assigned to this machine key */\n readonly name?: string;\n /** Target application slug this key belongs to */\n readonly appId?: string;\n /** Target application display name */\n readonly appName?: string;\n /** Scopes and permissions granted to this key */\n readonly scopes?: readonly string[];\n /** Allowed requests per minute limit */\n readonly rateLimitRpm?: number;\n /** Human-readable rejection reason if invalid */\n readonly reason?: string;\n}\n\n/**\n * Configuration options for Machine-to-Machine API key validation.\n */\nexport interface M2mConfig {\n /** Base URL of the authentication server / identity provider */\n readonly authUrl?: string;\n /** Legacy alias for authUrl */\n readonly portalUrl?: string;\n /** In-memory cache duration for validated API keys in milliseconds */\n readonly cacheTtlMs?: number;\n /** Expected client or application identifier */\n readonly requiredClientId?: string;\n /** Legacy alias for requiredClientId */\n readonly requiredAppId?: string;\n /** Required operational scopes that must be granted to the API key */\n readonly requiredScopes?: readonly string[];\n}\n\nexport interface JwtPayload {\n readonly sub: string;\n readonly iss?: string;\n readonly aud?: string | readonly string[];\n readonly exp?: number;\n readonly nbf?: number;\n readonly iat?: number;\n readonly email?: string;\n readonly name?: string;\n readonly role?: string;\n readonly roles?: readonly string[];\n readonly tenantId?: string;\n readonly tenant_id?: string;\n readonly tenant_slug?: string;\n readonly tenant_name?: string;\n readonly apps?: readonly string[];\n readonly appId?: string;\n readonly [key: string]: unknown;\n}\n","/**\n * @file packages/auth-server/src/token/jwt-validator.ts\n * @description RS256 JWKS validator providing cryptographic signature verification and claims checking.\n * @module @yuva-devlab/auth-server\n */\n\nimport crypto from \"node:crypto\";\nimport { UnauthorizedError } from \"@yuva-devlab/errors\";\nimport { Logger, loggerWithConfig } from \"@yuva-devlab/logger\";\nimport { DEFAULT_JWKS_CACHE_TTL_MS } from \"../constants\";\nimport type { JwksConfig, JwtPayload } from \"../types\";\n\nconst logger = loggerWithConfig(new Logger(\"JwksValidator\"));\n\ninterface CachedKey {\n readonly keyObject: crypto.KeyObject;\n readonly expiresAt: number;\n}\n\ninterface JwkRecord {\n readonly kid?: string;\n readonly kty: string;\n readonly alg?: string;\n readonly use?: string;\n readonly [key: string]: unknown;\n}\n\n/**\n * Validates JWT access tokens against an in-memory cached JWKS public key set.\n */\nexport class JwksValidator {\n private readonly jwksUri: string;\n private readonly audience?: string;\n private readonly issuer?: string;\n private readonly cacheTtlMs: number;\n private keyCache = new Map<string, CachedKey>();\n private jwksSetCache: {\n keys: readonly JwkRecord[];\n expiresAt: number;\n } | null = null;\n\n public constructor(config: Partial<JwksConfig> = {}) {\n this.jwksUri =\n config.jwksUri ??\n (config.issuer ? `${config.issuer}/.well-known/jwks.json` : undefined) ??\n (typeof process !== \"undefined\"\n ? (process.env?.DEVLAB_JWKS_URI ??\n (process.env?.DEVLAB_AUTH_URL\n ? `${process.env.DEVLAB_AUTH_URL}/.well-known/jwks.json`\n : undefined))\n : undefined) ??\n \"\";\n this.audience = config.audience;\n this.issuer = config.issuer;\n this.cacheTtlMs = config.cacheTtlMs ?? DEFAULT_JWKS_CACHE_TTL_MS;\n }\n\n public parseUnverified(token: string): {\n header: Record<string, unknown>;\n payload: JwtPayload;\n rawHeader: string;\n rawPayload: string;\n signature: string;\n } {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new UnauthorizedError(\n \"Malformed JWT token format: expected 3 segments\",\n );\n }\n\n try {\n const header = JSON.parse(\n Buffer.from(parts[0]!, \"base64url\").toString(\"utf-8\"),\n ) as Record<string, unknown>;\n const payload = JSON.parse(\n Buffer.from(parts[1]!, \"base64url\").toString(\"utf-8\"),\n ) as JwtPayload;\n return {\n header,\n payload,\n rawHeader: parts[0]!,\n rawPayload: parts[1]!,\n signature: parts[2]!,\n };\n } catch {\n throw new UnauthorizedError(\"Unable to decode JWT header or payload\");\n }\n }\n\n private async fetchJwks(): Promise<readonly JwkRecord[]> {\n if (!this.jwksUri) {\n throw new UnauthorizedError(\n \"JwksValidator: jwksUri, issuer, or DEVLAB_AUTH_URL must be configured\",\n );\n }\n const now = Date.now();\n if (this.jwksSetCache && this.jwksSetCache.expiresAt > now) {\n return this.jwksSetCache.keys;\n }\n\n try {\n const res = await fetch(this.jwksUri, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(5000),\n });\n\n if (!res.ok) {\n throw new Error(`JWKS endpoint returned HTTP ${res.status}`);\n }\n\n const body = (await res.json()) as { keys?: readonly JwkRecord[] };\n const keys = body.keys ?? [];\n this.jwksSetCache = { keys, expiresAt: now + this.cacheTtlMs };\n return keys;\n } catch (err) {\n logger.error(\"[fetchJwks] Failed to fetch JWKS public keys\", {\n uri: this.jwksUri,\n err,\n });\n if (this.jwksSetCache?.keys) return this.jwksSetCache.keys;\n throw new UnauthorizedError(\"Authentication service JWKS unreachable\");\n }\n }\n\n private async resolveKeyObject(kid?: string): Promise<crypto.KeyObject> {\n const now = Date.now();\n const cacheKey = kid ?? \"__default__\";\n const cached = this.keyCache.get(cacheKey);\n\n if (cached && cached.expiresAt > now) {\n return cached.keyObject;\n }\n\n const jwks = await this.fetchJwks();\n const matched = kid ? jwks.find((k) => k.kid === kid) : jwks[0];\n\n if (!matched) {\n throw new UnauthorizedError(\n `No matching JWK public key found for kid: ${kid ?? \"default\"}`,\n );\n }\n\n try {\n const keyObject = crypto.createPublicKey({\n key: matched as crypto.JsonWebKey,\n format: \"jwk\",\n });\n this.keyCache.set(cacheKey, {\n keyObject,\n expiresAt: now + this.cacheTtlMs,\n });\n return keyObject;\n } catch (err) {\n logger.error(\"[resolveKeyObject] Failed to parse JWK public key\", {\n kid,\n err,\n });\n throw new UnauthorizedError(\"Invalid JWK public key format\");\n }\n }\n\n public async verifyToken(token: string): Promise<JwtPayload> {\n const { header, payload, rawHeader, rawPayload, signature } =\n this.parseUnverified(token);\n\n if (header.alg && header.alg !== \"RS256\") {\n throw new UnauthorizedError(\n `Unsupported signing algorithm: ${header.alg}. Expected RS256`,\n );\n }\n\n const kid = typeof header.kid === \"string\" ? header.kid : undefined;\n const keyObject = await this.resolveKeyObject(kid);\n\n const data = Buffer.from(`${rawHeader}.${rawPayload}`);\n const sigBuffer = Buffer.from(signature, \"base64url\");\n const isSignatureValid = crypto.verify(\n \"RSA-SHA256\",\n data,\n keyObject,\n sigBuffer,\n );\n\n if (!isSignatureValid) {\n logger.warn(\"[verifyToken] Cryptographic signature check failed\", {\n sub: payload.sub,\n kid,\n });\n throw new UnauthorizedError(\"Invalid token signature\");\n }\n\n const nowSeconds = Math.floor(Date.now() / 1000);\n if (typeof payload.exp === \"number\" && nowSeconds >= payload.exp) {\n throw new UnauthorizedError(\"Authentication token has expired\");\n }\n\n if (typeof payload.nbf === \"number\" && nowSeconds < payload.nbf) {\n throw new UnauthorizedError(\"Authentication token is not yet active\");\n }\n\n if (this.issuer && payload.iss && payload.iss !== this.issuer) {\n throw new UnauthorizedError(\n `Token issuer mismatch: expected ${this.issuer}, got ${payload.iss}`,\n );\n }\n\n if (this.audience && payload.aud) {\n const audList = Array.isArray(payload.aud) ? payload.aud : [payload.aud];\n if (!audList.includes(this.audience)) {\n throw new UnauthorizedError(\n `Token audience mismatch: expected ${this.audience}`,\n );\n }\n }\n\n return payload;\n }\n\n public clearKeyCache(): void {\n this.keyCache.clear();\n this.jwksSetCache = null;\n logger.info(\"[clearKeyCache] JWKS public key cache cleared\");\n }\n}\n","/**\n * @file packages/auth-server/src/token/m2m-validator.ts\n * @description In-memory cached Machine-to-Machine API key validator introspecting the auth provider.\n * @module @yuva-devlab/auth-server\n */\n\nimport { ForbiddenError, UnauthorizedError } from \"@yuva-devlab/errors\";\nimport { Logger, loggerWithConfig } from \"@yuva-devlab/logger\";\nimport { DEFAULT_API_KEY_CACHE_TTL_MS } from \"../constants\";\nimport type { ApiKeyValidationResult, M2mConfig } from \"../types\";\n\nconst logger = loggerWithConfig(new Logger(\"M2mApiKeyValidator\"));\n\ninterface CachedApiKey {\n readonly result: ApiKeyValidationResult;\n readonly expiresAt: number;\n}\n\n/**\n * Validates machine-to-machine API keys against central authentication server with in-memory caching.\n */\nexport class M2mApiKeyValidator {\n private readonly authUrl?: string;\n private readonly cacheTtlMs: number;\n private readonly requiredClientId?: string;\n private readonly requiredScopes?: readonly string[];\n private readonly keyCache = new Map<string, CachedApiKey>();\n\n public constructor(config: Partial<M2mConfig> = {}) {\n this.authUrl =\n config.authUrl ??\n config.portalUrl ??\n process.env.DEVLAB_AUTH_URL ??\n process.env.DEVLAB_PORTAL_URL;\n this.cacheTtlMs = config.cacheTtlMs ?? DEFAULT_API_KEY_CACHE_TTL_MS;\n this.requiredClientId = config.requiredClientId ?? config.requiredAppId;\n this.requiredScopes = config.requiredScopes;\n }\n\n public async validateKey(rawKey: string): Promise<ApiKeyValidationResult> {\n const trimmed = rawKey.trim();\n if (!trimmed.startsWith(\"dlk_\")) {\n throw new UnauthorizedError(\"Malformed API key: expected 'dlk_' prefix\");\n }\n\n if (!this.authUrl) {\n throw new UnauthorizedError(\n \"M2mApiKeyValidator: authUrl or DEVLAB_AUTH_URL must be configured for validation\",\n );\n }\n\n const now = Date.now();\n const cached = this.keyCache.get(trimmed);\n if (cached && cached.expiresAt > now) {\n return this.enforceKeyConstraints(cached.result);\n }\n\n try {\n const url = `${this.authUrl}/api/v1/api-keys/validate`;\n const res = await fetch(url, {\n headers: {\n Accept: \"application/json\",\n Authorization: `Bearer ${trimmed}`,\n },\n signal: AbortSignal.timeout(5000),\n });\n\n if (!res.ok) {\n throw new Error(\n `Authentication validation endpoint returned HTTP ${res.status}`,\n );\n }\n\n const body = (await res.json()) as ApiKeyValidationResult;\n if (!body.valid) {\n throw new UnauthorizedError(body.reason ?? \"Invalid machine API key\");\n }\n\n this.keyCache.set(trimmed, {\n result: body,\n expiresAt: now + this.cacheTtlMs,\n });\n\n return this.enforceKeyConstraints(body);\n } catch (err) {\n if (err instanceof UnauthorizedError || err instanceof ForbiddenError) {\n throw err;\n }\n logger.error(\"[validateKey] Network error during API key introspection\", {\n err,\n });\n throw new UnauthorizedError(\n \"Authentication service unreachable for API key verification\",\n );\n }\n }\n\n private enforceKeyConstraints(\n result: ApiKeyValidationResult,\n ): ApiKeyValidationResult {\n if (\n this.requiredClientId &&\n result.appId &&\n result.appId !== this.requiredClientId\n ) {\n throw new ForbiddenError(\n \"API key assigned to \" +\n result.appId +\n \", required \" +\n this.requiredClientId,\n );\n }\n\n if (this.requiredScopes && this.requiredScopes.length > 0) {\n const keyScopes = result.scopes ?? [];\n const hasWildcard = keyScopes.includes(\"*\");\n if (!hasWildcard) {\n const hasAll = this.requiredScopes.every((scope) =>\n keyScopes.includes(scope),\n );\n if (!hasAll) {\n throw new ForbiddenError(\n \"API key lacks required scopes: [\" +\n this.requiredScopes.join(\", \") +\n \"]\",\n );\n }\n }\n }\n\n return result;\n }\n\n public clearCache(): void {\n this.keyCache.clear();\n logger.info(\"[clearCache] API key validation cache cleared\");\n }\n}\n","/**\n * @file packages/auth-server/src/guards/auth-guards.ts\n * @description Authorization assertions and scope/role enforcement guards.\n * @module @yuva-devlab/auth-server\n */\n\nimport { ForbiddenError, UnauthorizedError } from \"@yuva-devlab/errors\";\nimport { Logger, loggerWithConfig } from \"@yuva-devlab/logger\";\nimport type { ApiKey, AuthContext, AuthUser } from \"../types\";\n\nconst logger = loggerWithConfig(new Logger(\"AuthGuards\"));\n\n/**\n * Asserts that the request is authenticated with an active user session and optional role requirements.\n *\n * @param ctx - Inbound request auth context\n * @param requiredRoles - Optional list of required roles (user must possess at least one)\n * @returns Authenticated AuthUser\n * @throws UnauthorizedError if not authenticated as a user\n * @throws ForbiddenError if user lacks the required roles\n *\n * @example\n * ```typescript\n * const user = requireUser(ctx, [\"admin\", \"operator\"]);\n * ```\n */\nexport function requireUser(\n ctx: AuthContext,\n requiredRoles?: readonly string[],\n): AuthUser {\n if (!ctx.authenticated || !ctx.user) {\n logger.warn(\n \"[requireUser] Request rejected: missing authenticated user context\",\n );\n throw new UnauthorizedError(\n \"Authentication required: valid user session required\",\n );\n }\n\n if (requiredRoles && requiredRoles.length > 0) {\n const hasRole = requiredRoles.some((role) => ctx.roles.includes(role));\n if (!hasRole) {\n logger.warn(\"[requireUser] User lacks required roles\", {\n userId: ctx.user.id,\n userRoles: ctx.roles,\n requiredRoles,\n });\n throw new ForbiddenError(\n `Insufficient privileges: requires one of [${requiredRoles.join(\", \")}]`,\n );\n }\n }\n\n return ctx.user;\n}\n\n/**\n * Asserts that the request is authenticated via a machine-to-machine API key.\n *\n * @param ctx - Inbound request auth context\n * @returns Authenticated ApiKey\n * @throws UnauthorizedError if not authenticated via an API key\n */\nexport function requireApiKey(ctx: AuthContext): ApiKey {\n if (!ctx.authenticated || !ctx.apiKey) {\n logger.warn(\"[requireApiKey] Request rejected: missing machine API key\");\n throw new UnauthorizedError(\n \"Authentication required: valid API key required\",\n );\n }\n\n return ctx.apiKey;\n}\n\n/**\n * Asserts that the authenticated context (user or API key) possesses a specific operational scope.\n *\n * @param ctx - Inbound request auth context\n * @param scope - Required scope or permission string\n * @throws ForbiddenError if scope is missing\n */\nexport function requireScope(ctx: AuthContext, scope: string): void {\n if (!ctx.roles.includes(scope)) {\n logger.warn(\"[requireScope] Missing required scope\", {\n roles: ctx.roles,\n requiredScope: scope,\n });\n throw new ForbiddenError(`Missing required scope: '${scope}'`);\n }\n}\n","/**\n * @file packages/auth-server/src/middleware/auth-middleware.ts\n * @description Universal HTTP middleware providing platform kill-switch enforcement and credential validation.\n * @module @yuva-devlab/auth-server\n */\n\nimport { Logger, loggerWithConfig } from \"@yuva-devlab/logger\";\nimport {\n DEFAULT_API_KEY_HEADER,\n DEFAULT_AUTH_HEADER,\n DEFAULT_BEARER_PREFIX,\n DEFAULT_PUBLIC_PATHS,\n HTTP_STATUS_SERVICE_UNAVAILABLE,\n HTTP_STATUS_UNAUTHORIZED,\n} from \"../constants\";\nimport { AppStatus, type AuthContext } from \"../types\";\n\nconst logger = loggerWithConfig(new Logger(\"AuthMiddleware\"));\n\n/**\n * Minimal HTTP request and response interfaces for framework neutrality (Express, Fastify, Node http).\n */\nexport interface GenericHttpRequest {\n url?: string;\n method?: string;\n headers: Record<string, string | string[] | undefined>;\n authContext?: AuthContext;\n}\n\nexport interface GenericHttpResponse {\n statusCode?: number;\n setHeader(name: string, value: string): void;\n end(body?: string): void;\n}\n\n/**\n * Creates a kill-switch middleware that intercepts all traffic if an application is deactivated.\n *\n * @param getAppStatus - Callback returning current platform status of the application\n * @returns Middleware function returning true if allowed to proceed, false if blocked (503)\n *\n * @example\n * ```typescript\n * const killSwitch = createAppKillSwitchMiddleware(() => appState.status);\n * const allowed = await killSwitch(req, res);\n * if (!allowed) return;\n * ```\n */\nexport function createAppKillSwitchMiddleware(\n getAppStatus: () => Promise<AppStatus> | AppStatus,\n) {\n return async (\n req: GenericHttpRequest,\n res: GenericHttpResponse,\n ): Promise<boolean> => {\n const status = await getAppStatus();\n\n // If application has been administratively disabled or put into maintenance\n if (status === AppStatus.INACTIVE || status === AppStatus.MAINTENANCE) {\n logger.warn(\n \"[killSwitch] Application is inactive or under maintenance; request rejected\",\n {\n url: req.url,\n method: req.method,\n appStatus: status,\n },\n );\n\n res.statusCode = HTTP_STATUS_SERVICE_UNAVAILABLE;\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(\n JSON.stringify({\n error: {\n code: \"APP_DEACTIVATED\",\n message:\n status === AppStatus.MAINTENANCE\n ? \"Application is currently undergoing scheduled maintenance\"\n : \"Application has been deactivated by platform administrators\",\n status,\n },\n }),\n );\n return false;\n }\n\n return true;\n };\n}\n\n/**\n * Options configuring request authentication middleware.\n */\nexport interface CreateAuthMiddlewareOptions {\n /** Whitelisted public endpoints bypassing authentication */\n publicPaths?: readonly string[];\n /** Function resolving an API key against store */\n validateApiKey?: (key: string) => Promise<AuthContext | null>;\n /** Function resolving a Bearer session token against JWKS or session store */\n validateToken?: (token: string) => Promise<AuthContext | null>;\n}\n\n/**\n * Universal authentication middleware verifying API keys and Bearer tokens.\n *\n * @param options - Configuration options\n * @returns Middleware handler\n */\nexport function createAuthMiddleware(options: CreateAuthMiddlewareOptions) {\n const publicPaths = new Set([\n ...DEFAULT_PUBLIC_PATHS,\n ...(options.publicPaths ?? []),\n ]);\n\n return async (\n req: GenericHttpRequest,\n res: GenericHttpResponse,\n ): Promise<boolean> => {\n const urlPath = (req.url || \"/\").split(\"?\")[0] || \"/\";\n\n // 1. Bypass authentication for whitelisted public paths\n if (publicPaths.has(urlPath)) {\n req.authContext = {\n authenticated: false,\n roles: [],\n isAppActive: true,\n };\n return true;\n }\n\n // 2. Check for Machine-to-Machine API Key\n const rawApiKey = req.headers[DEFAULT_API_KEY_HEADER];\n const apiKey = typeof rawApiKey === \"string\" ? rawApiKey.trim() : undefined;\n\n if (apiKey && options.validateApiKey) {\n const apiKeyCtx = await options.validateApiKey(apiKey);\n if (apiKeyCtx?.authenticated) {\n logger.info(\"[authenticate] Authenticated via API Key\", {\n path: urlPath,\n });\n req.authContext = apiKeyCtx;\n return true;\n }\n }\n\n // 3. Check for Bearer Token\n const rawAuth = req.headers[DEFAULT_AUTH_HEADER];\n const authHeader = typeof rawAuth === \"string\" ? rawAuth.trim() : undefined;\n\n if (\n authHeader?.startsWith(DEFAULT_BEARER_PREFIX) &&\n options.validateToken\n ) {\n const token = authHeader.slice(DEFAULT_BEARER_PREFIX.length).trim();\n const tokenCtx = await options.validateToken(token);\n if (tokenCtx?.authenticated) {\n logger.info(\"[authenticate] Authenticated via Bearer token\", {\n path: urlPath,\n });\n req.authContext = tokenCtx;\n return true;\n }\n }\n\n // 4. Reject with 401 Unauthorized\n logger.warn(\"[authenticate] Unauthorized request rejected\", {\n path: urlPath,\n method: req.method,\n });\n\n res.statusCode = HTTP_STATUS_UNAUTHORIZED;\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(\n JSON.stringify({\n error: {\n code: \"UNAUTHORIZED\",\n message:\n \"Missing or invalid authentication credentials (Bearer token or API key required)\",\n },\n }),\n );\n return false;\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/constants/auth.constants.ts","../src/types/auth.types.ts","../src/token/jwt-validator.ts","../src/token/m2m-validator.ts","../src/guards/auth-guards.ts","../src/middleware/auth-middleware.ts"],"names":["AppStatus","loggerWithConfig","Logger","UnauthorizedError","crypto","logger","ForbiddenError"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @file packages/auth-server/src/constants/auth.constants.ts\n * @description Centralized constants and header keys for server-side authentication.\n * @module @yuva-devlab/auth-server\n */\n\n/** Standard Authorization HTTP header name (lowercase for case-insensitive lookup) */\nexport const DEFAULT_AUTH_HEADER = \"authorization\";\n\n/** Standard machine-to-machine API key HTTP header name */\nexport const DEFAULT_API_KEY_HEADER = \"x-api-key\";\n\n/** Standard Bearer token prefix */\nexport const DEFAULT_BEARER_PREFIX = \"Bearer \";\n\n/** Default cache time-to-live for fetched RS256 JWKS public keys (1 hour) */\nexport const DEFAULT_JWKS_CACHE_TTL_MS = 3_600_000;\n\n/** Default cache time-to-live for validated M2M API keys (5 minutes) */\nexport const DEFAULT_API_KEY_CACHE_TTL_MS = 300_000;\n\n/** Default public endpoints bypassing mandatory authentication */\nexport const DEFAULT_PUBLIC_PATHS = Object.freeze([\n \"/health\",\n \"/ready\",\n \"/live\",\n \"/metrics\",\n]);\n\n/** HTTP Status Code for unauthenticated requests */\nexport const HTTP_STATUS_UNAUTHORIZED = 401;\n\n/** HTTP Status Code for unauthorized / forbidden role requests */\nexport const HTTP_STATUS_FORBIDDEN = 403;\n\n/** HTTP Status Code returned when an application is deactivated or killed */\nexport const HTTP_STATUS_SERVICE_UNAVAILABLE = 503;\n","/**\n * @file packages/auth-server/src/types/auth.types.ts\n * @description Type definitions and contracts for server authentication, JWKS, and app lifecycle.\n * @module @yuva-devlab/auth-server\n */\n\n/**\n * Operational state of an application on the DevLab platform.\n */\nexport enum AppStatus {\n /** Application is fully operational and serving traffic */\n ACTIVE = \"ACTIVE\",\n /** Application has been administratively disabled / deactivated (Kill-Switch triggered) */\n INACTIVE = \"INACTIVE\",\n /** Application is undergoing scheduled maintenance */\n MAINTENANCE = \"MAINTENANCE\",\n}\n\n/**\n * Authenticated user profile representation.\n */\nexport interface AuthUser {\n /** Canonical user UUID */\n readonly id: string;\n /** Primary user email address */\n readonly email?: string;\n /** Granted role slugs */\n readonly roles: readonly string[];\n /** Tenant isolation partition ID */\n readonly tenantId?: string;\n /** Arbitrary profile metadata */\n readonly metadata?: Record<string, unknown>;\n}\n\n/**\n * Machine-to-machine API key credential.\n */\nexport interface ApiKey {\n /** Primary API key UUID */\n readonly id: string;\n /** Secure key string */\n readonly key: string;\n /** Human-readable key name or description */\n readonly name: string;\n /** Scoped roles granted to this key */\n readonly roles: readonly string[];\n /** Tenant ID partition */\n readonly tenantId?: string;\n /** Active status flag */\n readonly isEnabled: boolean;\n}\n\n/**\n * Evaluated authentication context attached to inbound requests.\n */\nexport interface AuthContext {\n /** Whether the request carries valid credentials */\n readonly authenticated: boolean;\n /** Authenticated user if authenticated via session/JWT */\n readonly user?: AuthUser;\n /** Authenticated API key if authenticated via machine credential */\n readonly apiKey?: ApiKey;\n /** Consolidated roles granted across user or API key */\n readonly roles: readonly string[];\n /** Whether the host application is currently active on the platform */\n readonly isAppActive: boolean;\n /** Detailed app status */\n readonly appStatus?: AppStatus;\n /** Tenant partition ID */\n readonly tenantId?: string;\n /** Distributed trace request identifier */\n readonly requestId?: string;\n}\n\n/**\n * Configuration for verifying RS256 JWTs against a remote JWKS endpoint.\n */\nexport interface JwksConfig {\n /** JWKS endpoint URI exposing JSON Web Keys */\n readonly jwksUri: string;\n /** Expected audience claim */\n readonly audience?: string;\n /** Expected issuer claim */\n readonly issuer?: string;\n /** In-memory cache duration for public keys in milliseconds */\n readonly cacheTtlMs?: number;\n}\n\n/**\n * Decoded JWT claims payload.\n */\n\n/**\n * Result of validating a Machine-to-Machine API key against the DevLab Portal.\n */\nexport interface ApiKeyValidationResult {\n /** Whether the API key is active, valid, and unexpired */\n readonly valid: boolean;\n /** Primary API key UUID */\n readonly keyId?: string;\n /** Name assigned to this machine key */\n readonly name?: string;\n /** Target application slug this key belongs to */\n readonly appId?: string;\n /** Target application display name */\n readonly appName?: string;\n /** Scopes and permissions granted to this key */\n readonly scopes?: readonly string[];\n /** Allowed requests per minute limit */\n readonly rateLimitRpm?: number;\n /** Human-readable rejection reason if invalid */\n readonly reason?: string;\n}\n\n/**\n * Configuration options for Machine-to-Machine API key validation.\n */\nexport interface M2mConfig {\n /** Base URL of the authentication server / identity provider */\n readonly authUrl?: string;\n /** Legacy alias for authUrl */\n readonly portalUrl?: string;\n /** In-memory cache duration for validated API keys in milliseconds */\n readonly cacheTtlMs?: number;\n /** Expected client or application identifier */\n readonly requiredClientId?: string;\n /** Legacy alias for requiredClientId */\n readonly requiredAppId?: string;\n /** Required operational scopes that must be granted to the API key */\n readonly requiredScopes?: readonly string[];\n}\n\nexport interface JwtPayload {\n readonly sub: string;\n readonly iss?: string;\n readonly aud?: string | readonly string[];\n readonly exp?: number;\n readonly nbf?: number;\n readonly iat?: number;\n readonly email?: string;\n readonly name?: string;\n readonly role?: string;\n readonly roles?: readonly string[];\n readonly tenantId?: string;\n readonly tenant_id?: string;\n readonly tenant_slug?: string;\n readonly tenant_name?: string;\n readonly apps?: readonly string[];\n readonly appId?: string;\n readonly [key: string]: unknown;\n}\n","/**\n * @file packages/auth-server/src/token/jwt-validator.ts\n * @description RS256 JWKS validator providing cryptographic signature verification and claims checking.\n * @module @yuva-devlab/auth-server\n */\n\nimport crypto from \"node:crypto\";\nimport { UnauthorizedError } from \"@yuva-devlab/errors\";\nimport { Logger, loggerWithConfig } from \"@yuva-devlab/logger\";\nimport { DEFAULT_JWKS_CACHE_TTL_MS } from \"../constants\";\nimport type { JwksConfig, JwtPayload } from \"../types\";\n\nconst logger = loggerWithConfig(new Logger(\"JwksValidator\"));\n\ninterface CachedKey {\n readonly keyObject: crypto.KeyObject;\n readonly expiresAt: number;\n}\n\ninterface JwkRecord {\n readonly kid?: string;\n readonly kty: string;\n readonly alg?: string;\n readonly use?: string;\n readonly [key: string]: unknown;\n}\n\n/**\n * Validates JWT access tokens against an in-memory cached JWKS public key set.\n */\nexport class JwksValidator {\n private readonly jwksUri: string;\n private readonly audience?: string;\n private readonly issuer?: string;\n private readonly cacheTtlMs: number;\n private keyCache = new Map<string, CachedKey>();\n private jwksSetCache: {\n keys: readonly JwkRecord[];\n expiresAt: number;\n } | null = null;\n\n public constructor(config: Partial<JwksConfig> = {}) {\n this.jwksUri =\n config.jwksUri ??\n (config.issuer ? `${config.issuer}/.well-known/jwks.json` : undefined) ??\n (typeof process !== \"undefined\"\n ? process.env?.DEVLAB_PORTAL_API_URL\n ? `${process.env.DEVLAB_PORTAL_API_URL}/.well-known/jwks.json`\n : undefined\n : undefined) ??\n \"\";\n this.audience = config.audience;\n this.issuer = config.issuer;\n this.cacheTtlMs = config.cacheTtlMs ?? DEFAULT_JWKS_CACHE_TTL_MS;\n }\n\n public parseUnverified(token: string): {\n header: Record<string, unknown>;\n payload: JwtPayload;\n rawHeader: string;\n rawPayload: string;\n signature: string;\n } {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new UnauthorizedError(\n \"Malformed JWT token format: expected 3 segments\",\n );\n }\n\n try {\n const header = JSON.parse(\n Buffer.from(parts[0]!, \"base64url\").toString(\"utf-8\"),\n ) as Record<string, unknown>;\n const payload = JSON.parse(\n Buffer.from(parts[1]!, \"base64url\").toString(\"utf-8\"),\n ) as JwtPayload;\n return {\n header,\n payload,\n rawHeader: parts[0]!,\n rawPayload: parts[1]!,\n signature: parts[2]!,\n };\n } catch {\n throw new UnauthorizedError(\"Unable to decode JWT header or payload\");\n }\n }\n\n private async fetchJwks(): Promise<readonly JwkRecord[]> {\n if (!this.jwksUri) {\n throw new UnauthorizedError(\n \"JwksValidator: jwksUri, issuer, or DEVLAB_PORTAL_API_URL must be configured\",\n );\n }\n const now = Date.now();\n if (this.jwksSetCache && this.jwksSetCache.expiresAt > now) {\n return this.jwksSetCache.keys;\n }\n\n try {\n const res = await fetch(this.jwksUri, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(5000),\n });\n\n if (!res.ok) {\n throw new Error(`JWKS endpoint returned HTTP ${res.status}`);\n }\n\n const body = (await res.json()) as { keys?: readonly JwkRecord[] };\n const keys = body.keys ?? [];\n this.jwksSetCache = { keys, expiresAt: now + this.cacheTtlMs };\n return keys;\n } catch (err) {\n logger.error(\"[fetchJwks] Failed to fetch JWKS public keys\", {\n uri: this.jwksUri,\n err,\n });\n if (this.jwksSetCache?.keys) return this.jwksSetCache.keys;\n throw new UnauthorizedError(\"Authentication service JWKS unreachable\");\n }\n }\n\n private async resolveKeyObject(kid?: string): Promise<crypto.KeyObject> {\n const now = Date.now();\n const cacheKey = kid ?? \"__default__\";\n const cached = this.keyCache.get(cacheKey);\n\n if (cached && cached.expiresAt > now) {\n return cached.keyObject;\n }\n\n const jwks = await this.fetchJwks();\n const matched = kid ? jwks.find((k) => k.kid === kid) : jwks[0];\n\n if (!matched) {\n throw new UnauthorizedError(\n `No matching JWK public key found for kid: ${kid ?? \"default\"}`,\n );\n }\n\n try {\n const keyObject = crypto.createPublicKey({\n key: matched as crypto.JsonWebKey,\n format: \"jwk\",\n });\n this.keyCache.set(cacheKey, {\n keyObject,\n expiresAt: now + this.cacheTtlMs,\n });\n return keyObject;\n } catch (err) {\n logger.error(\"[resolveKeyObject] Failed to parse JWK public key\", {\n kid,\n err,\n });\n throw new UnauthorizedError(\"Invalid JWK public key format\");\n }\n }\n\n public async verifyToken(token: string): Promise<JwtPayload> {\n const { header, payload, rawHeader, rawPayload, signature } =\n this.parseUnverified(token);\n\n if (header.alg && header.alg !== \"RS256\") {\n throw new UnauthorizedError(\n `Unsupported signing algorithm: ${header.alg}. Expected RS256`,\n );\n }\n\n const kid = typeof header.kid === \"string\" ? header.kid : undefined;\n const keyObject = await this.resolveKeyObject(kid);\n\n const data = Buffer.from(`${rawHeader}.${rawPayload}`);\n const sigBuffer = Buffer.from(signature, \"base64url\");\n const isSignatureValid = crypto.verify(\n \"RSA-SHA256\",\n data,\n keyObject,\n sigBuffer,\n );\n\n if (!isSignatureValid) {\n logger.warn(\"[verifyToken] Cryptographic signature check failed\", {\n sub: payload.sub,\n kid,\n });\n throw new UnauthorizedError(\"Invalid token signature\");\n }\n\n const nowSeconds = Math.floor(Date.now() / 1000);\n if (typeof payload.exp === \"number\" && nowSeconds >= payload.exp) {\n throw new UnauthorizedError(\"Authentication token has expired\");\n }\n\n if (typeof payload.nbf === \"number\" && nowSeconds < payload.nbf) {\n throw new UnauthorizedError(\"Authentication token is not yet active\");\n }\n\n if (this.issuer && payload.iss && payload.iss !== this.issuer) {\n throw new UnauthorizedError(\n `Token issuer mismatch: expected ${this.issuer}, got ${payload.iss}`,\n );\n }\n\n if (this.audience && payload.aud) {\n const audList = Array.isArray(payload.aud) ? payload.aud : [payload.aud];\n if (!audList.includes(this.audience)) {\n throw new UnauthorizedError(\n `Token audience mismatch: expected ${this.audience}`,\n );\n }\n }\n\n return payload;\n }\n\n public clearKeyCache(): void {\n this.keyCache.clear();\n this.jwksSetCache = null;\n logger.info(\"[clearKeyCache] JWKS public key cache cleared\");\n }\n}\n","/**\n * @file packages/auth-server/src/token/m2m-validator.ts\n * @description In-memory cached Machine-to-Machine API key validator introspecting the auth provider.\n * @module @yuva-devlab/auth-server\n */\n\nimport { ForbiddenError, UnauthorizedError } from \"@yuva-devlab/errors\";\nimport { Logger, loggerWithConfig } from \"@yuva-devlab/logger\";\nimport { DEFAULT_API_KEY_CACHE_TTL_MS } from \"../constants\";\nimport type { ApiKeyValidationResult, M2mConfig } from \"../types\";\n\nconst logger = loggerWithConfig(new Logger(\"M2mApiKeyValidator\"));\n\ninterface CachedApiKey {\n readonly result: ApiKeyValidationResult;\n readonly expiresAt: number;\n}\n\n/**\n * Validates machine-to-machine API keys against central authentication server with in-memory caching.\n */\nexport class M2mApiKeyValidator {\n private readonly authUrl?: string;\n private readonly cacheTtlMs: number;\n private readonly requiredClientId?: string;\n private readonly requiredScopes?: readonly string[];\n private readonly keyCache = new Map<string, CachedApiKey>();\n\n public constructor(config: Partial<M2mConfig> = {}) {\n this.authUrl =\n config.authUrl ?? config.portalUrl ?? process.env.DEVLAB_PORTAL_API_URL;\n this.cacheTtlMs = config.cacheTtlMs ?? DEFAULT_API_KEY_CACHE_TTL_MS;\n this.requiredClientId = config.requiredClientId ?? config.requiredAppId;\n this.requiredScopes = config.requiredScopes;\n }\n\n public async validateKey(rawKey: string): Promise<ApiKeyValidationResult> {\n const trimmed = rawKey.trim();\n if (!trimmed.startsWith(\"dlk_\")) {\n throw new UnauthorizedError(\"Malformed API key: expected 'dlk_' prefix\");\n }\n\n if (!this.authUrl) {\n throw new UnauthorizedError(\n \"M2mApiKeyValidator: authUrl or DEVLAB_PORTAL_API_URL must be configured for validation\",\n );\n }\n\n const now = Date.now();\n const cached = this.keyCache.get(trimmed);\n if (cached && cached.expiresAt > now) {\n return this.enforceKeyConstraints(cached.result);\n }\n\n try {\n const url = `${this.authUrl}/api/v1/api-keys/validate`;\n const res = await fetch(url, {\n headers: {\n Accept: \"application/json\",\n Authorization: `Bearer ${trimmed}`,\n },\n signal: AbortSignal.timeout(5000),\n });\n\n if (!res.ok) {\n throw new Error(\n `Authentication validation endpoint returned HTTP ${res.status}`,\n );\n }\n\n const body = (await res.json()) as ApiKeyValidationResult;\n if (!body.valid) {\n throw new UnauthorizedError(body.reason ?? \"Invalid machine API key\");\n }\n\n this.keyCache.set(trimmed, {\n result: body,\n expiresAt: now + this.cacheTtlMs,\n });\n\n return this.enforceKeyConstraints(body);\n } catch (err) {\n if (err instanceof UnauthorizedError || err instanceof ForbiddenError) {\n throw err;\n }\n logger.error(\"[validateKey] Network error during API key introspection\", {\n err,\n });\n throw new UnauthorizedError(\n \"Authentication service unreachable for API key verification\",\n );\n }\n }\n\n private enforceKeyConstraints(\n result: ApiKeyValidationResult,\n ): ApiKeyValidationResult {\n if (\n this.requiredClientId &&\n result.appId &&\n result.appId !== this.requiredClientId\n ) {\n const keyScopes = result.scopes ?? [];\n const hasWildcard =\n keyScopes.includes(\"*\") ||\n keyScopes.includes(`${this.requiredClientId}:*`) ||\n keyScopes.includes(this.requiredClientId);\n if (!hasWildcard) {\n throw new ForbiddenError(\n \"API key assigned to \" +\n result.appId +\n \", required \" +\n this.requiredClientId,\n );\n }\n }\n\n if (this.requiredScopes && this.requiredScopes.length > 0) {\n const keyScopes = result.scopes ?? [];\n const hasWildcard = keyScopes.includes(\"*\");\n if (!hasWildcard) {\n const hasAll = this.requiredScopes.every((scope) =>\n keyScopes.includes(scope),\n );\n if (!hasAll) {\n throw new ForbiddenError(\n \"API key lacks required scopes: [\" +\n this.requiredScopes.join(\", \") +\n \"]\",\n );\n }\n }\n }\n\n return result;\n }\n\n public clearCache(): void {\n this.keyCache.clear();\n logger.info(\"[clearCache] API key validation cache cleared\");\n }\n}\n","/**\n * @file packages/auth-server/src/guards/auth-guards.ts\n * @description Authorization assertions and scope/role enforcement guards.\n * @module @yuva-devlab/auth-server\n */\n\nimport { ForbiddenError, UnauthorizedError } from \"@yuva-devlab/errors\";\nimport { Logger, loggerWithConfig } from \"@yuva-devlab/logger\";\nimport type { ApiKey, AuthContext, AuthUser } from \"../types\";\n\nconst logger = loggerWithConfig(new Logger(\"AuthGuards\"));\n\n/**\n * Asserts that the request is authenticated with an active user session and optional role requirements.\n *\n * @param ctx - Inbound request auth context\n * @param requiredRoles - Optional list of required roles (user must possess at least one)\n * @returns Authenticated AuthUser\n * @throws UnauthorizedError if not authenticated as a user\n * @throws ForbiddenError if user lacks the required roles\n *\n * @example\n * ```typescript\n * const user = requireUser(ctx, [\"admin\", \"operator\"]);\n * ```\n */\nexport function requireUser(\n ctx: AuthContext,\n requiredRoles?: readonly string[],\n): AuthUser {\n if (!ctx.authenticated || !ctx.user) {\n logger.warn(\n \"[requireUser] Request rejected: missing authenticated user context\",\n );\n throw new UnauthorizedError(\n \"Authentication required: valid user session required\",\n );\n }\n\n if (requiredRoles && requiredRoles.length > 0) {\n const hasRole = requiredRoles.some((role) => ctx.roles.includes(role));\n if (!hasRole) {\n logger.warn(\"[requireUser] User lacks required roles\", {\n userId: ctx.user.id,\n userRoles: ctx.roles,\n requiredRoles,\n });\n throw new ForbiddenError(\n `Insufficient privileges: requires one of [${requiredRoles.join(\", \")}]`,\n );\n }\n }\n\n return ctx.user;\n}\n\n/**\n * Asserts that the request is authenticated via a machine-to-machine API key.\n *\n * @param ctx - Inbound request auth context\n * @returns Authenticated ApiKey\n * @throws UnauthorizedError if not authenticated via an API key\n */\nexport function requireApiKey(ctx: AuthContext): ApiKey {\n if (!ctx.authenticated || !ctx.apiKey) {\n logger.warn(\"[requireApiKey] Request rejected: missing machine API key\");\n throw new UnauthorizedError(\n \"Authentication required: valid API key required\",\n );\n }\n\n return ctx.apiKey;\n}\n\n/**\n * Asserts that the authenticated context (user or API key) possesses a specific operational scope.\n *\n * @param ctx - Inbound request auth context\n * @param scope - Required scope or permission string\n * @throws ForbiddenError if scope is missing\n */\nexport function requireScope(ctx: AuthContext, scope: string): void {\n if (!ctx.roles.includes(scope)) {\n logger.warn(\"[requireScope] Missing required scope\", {\n roles: ctx.roles,\n requiredScope: scope,\n });\n throw new ForbiddenError(`Missing required scope: '${scope}'`);\n }\n}\n","/**\n * @file packages/auth-server/src/middleware/auth-middleware.ts\n * @description Universal HTTP middleware providing platform kill-switch enforcement and credential validation.\n * @module @yuva-devlab/auth-server\n */\n\nimport { Logger, loggerWithConfig } from \"@yuva-devlab/logger\";\nimport {\n DEFAULT_API_KEY_HEADER,\n DEFAULT_AUTH_HEADER,\n DEFAULT_BEARER_PREFIX,\n DEFAULT_PUBLIC_PATHS,\n HTTP_STATUS_SERVICE_UNAVAILABLE,\n HTTP_STATUS_UNAUTHORIZED,\n} from \"../constants\";\nimport { AppStatus, type AuthContext } from \"../types\";\n\nconst logger = loggerWithConfig(new Logger(\"AuthMiddleware\"));\n\n/**\n * Minimal HTTP request and response interfaces for framework neutrality (Express, Fastify, Node http).\n */\nexport interface GenericHttpRequest {\n url?: string;\n method?: string;\n headers: Record<string, string | string[] | undefined>;\n authContext?: AuthContext;\n}\n\nexport interface GenericHttpResponse {\n statusCode?: number;\n setHeader(name: string, value: string): void;\n end(body?: string): void;\n}\n\n/**\n * Creates a kill-switch middleware that intercepts all traffic if an application is deactivated.\n *\n * @param getAppStatus - Callback returning current platform status of the application\n * @returns Middleware function returning true if allowed to proceed, false if blocked (503)\n *\n * @example\n * ```typescript\n * const killSwitch = createAppKillSwitchMiddleware(() => appState.status);\n * const allowed = await killSwitch(req, res);\n * if (!allowed) return;\n * ```\n */\nexport function createAppKillSwitchMiddleware(\n getAppStatus: () => Promise<AppStatus> | AppStatus,\n) {\n return async (\n req: GenericHttpRequest,\n res: GenericHttpResponse,\n ): Promise<boolean> => {\n const status = await getAppStatus();\n\n // If application has been administratively disabled or put into maintenance\n if (status === AppStatus.INACTIVE || status === AppStatus.MAINTENANCE) {\n logger.warn(\n \"[killSwitch] Application is inactive or under maintenance; request rejected\",\n {\n url: req.url,\n method: req.method,\n appStatus: status,\n },\n );\n\n res.statusCode = HTTP_STATUS_SERVICE_UNAVAILABLE;\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(\n JSON.stringify({\n error: {\n code: \"APP_DEACTIVATED\",\n message:\n status === AppStatus.MAINTENANCE\n ? \"Application is currently undergoing scheduled maintenance\"\n : \"Application has been deactivated by platform administrators\",\n status,\n },\n }),\n );\n return false;\n }\n\n return true;\n };\n}\n\n/**\n * Options configuring request authentication middleware.\n */\nexport interface CreateAuthMiddlewareOptions {\n /** Whitelisted public endpoints bypassing authentication */\n publicPaths?: readonly string[];\n /** Function resolving an API key against store */\n validateApiKey?: (key: string) => Promise<AuthContext | null>;\n /** Function resolving a Bearer session token against JWKS or session store */\n validateToken?: (token: string) => Promise<AuthContext | null>;\n}\n\n/**\n * Universal authentication middleware verifying API keys and Bearer tokens.\n *\n * @param options - Configuration options\n * @returns Middleware handler\n */\nexport function createAuthMiddleware(options: CreateAuthMiddlewareOptions) {\n const publicPaths = new Set([\n ...DEFAULT_PUBLIC_PATHS,\n ...(options.publicPaths ?? []),\n ]);\n\n return async (\n req: GenericHttpRequest,\n res: GenericHttpResponse,\n ): Promise<boolean> => {\n const urlPath = (req.url || \"/\").split(\"?\")[0] || \"/\";\n\n // 1. Bypass authentication for whitelisted public paths\n if (publicPaths.has(urlPath)) {\n req.authContext = {\n authenticated: false,\n roles: [],\n isAppActive: true,\n };\n return true;\n }\n\n // 2. Check for Machine-to-Machine API Key\n const rawApiKey = req.headers[DEFAULT_API_KEY_HEADER];\n const apiKey = typeof rawApiKey === \"string\" ? rawApiKey.trim() : undefined;\n\n if (apiKey && options.validateApiKey) {\n const apiKeyCtx = await options.validateApiKey(apiKey);\n if (apiKeyCtx?.authenticated) {\n logger.info(\"[authenticate] Authenticated via API Key\", {\n path: urlPath,\n });\n req.authContext = apiKeyCtx;\n return true;\n }\n }\n\n // 3. Check for Bearer Token\n const rawAuth = req.headers[DEFAULT_AUTH_HEADER];\n const authHeader = typeof rawAuth === \"string\" ? rawAuth.trim() : undefined;\n\n if (\n authHeader?.startsWith(DEFAULT_BEARER_PREFIX) &&\n options.validateToken\n ) {\n const token = authHeader.slice(DEFAULT_BEARER_PREFIX.length).trim();\n const tokenCtx = await options.validateToken(token);\n if (tokenCtx?.authenticated) {\n logger.info(\"[authenticate] Authenticated via Bearer token\", {\n path: urlPath,\n });\n req.authContext = tokenCtx;\n return true;\n }\n }\n\n // 4. Reject with 401 Unauthorized\n logger.warn(\"[authenticate] Unauthorized request rejected\", {\n path: urlPath,\n method: req.method,\n });\n\n res.statusCode = HTTP_STATUS_UNAUTHORIZED;\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(\n JSON.stringify({\n error: {\n code: \"UNAUTHORIZED\",\n message:\n \"Missing or invalid authentication credentials (Bearer token or API key required)\",\n },\n }),\n );\n return false;\n };\n}\n"]}
package/dist/index.js CHANGED
@@ -34,7 +34,7 @@ var JwksValidator = class {
34
34
  keyCache = /* @__PURE__ */ new Map();
35
35
  jwksSetCache = null;
36
36
  constructor(config = {}) {
37
- this.jwksUri = config.jwksUri ?? (config.issuer ? `${config.issuer}/.well-known/jwks.json` : void 0) ?? (typeof process !== "undefined" ? process.env?.DEVLAB_JWKS_URI ?? (process.env?.DEVLAB_AUTH_URL ? `${process.env.DEVLAB_AUTH_URL}/.well-known/jwks.json` : void 0) : void 0) ?? "";
37
+ this.jwksUri = config.jwksUri ?? (config.issuer ? `${config.issuer}/.well-known/jwks.json` : void 0) ?? (typeof process !== "undefined" ? process.env?.DEVLAB_PORTAL_API_URL ? `${process.env.DEVLAB_PORTAL_API_URL}/.well-known/jwks.json` : void 0 : void 0) ?? "";
38
38
  this.audience = config.audience;
39
39
  this.issuer = config.issuer;
40
40
  this.cacheTtlMs = config.cacheTtlMs ?? DEFAULT_JWKS_CACHE_TTL_MS;
@@ -67,7 +67,7 @@ var JwksValidator = class {
67
67
  async fetchJwks() {
68
68
  if (!this.jwksUri) {
69
69
  throw new UnauthorizedError(
70
- "JwksValidator: jwksUri, issuer, or DEVLAB_AUTH_URL must be configured"
70
+ "JwksValidator: jwksUri, issuer, or DEVLAB_PORTAL_API_URL must be configured"
71
71
  );
72
72
  }
73
73
  const now = Date.now();
@@ -187,7 +187,7 @@ var M2mApiKeyValidator = class {
187
187
  requiredScopes;
188
188
  keyCache = /* @__PURE__ */ new Map();
189
189
  constructor(config = {}) {
190
- this.authUrl = config.authUrl ?? config.portalUrl ?? process.env.DEVLAB_AUTH_URL ?? process.env.DEVLAB_PORTAL_URL;
190
+ this.authUrl = config.authUrl ?? config.portalUrl ?? process.env.DEVLAB_PORTAL_API_URL;
191
191
  this.cacheTtlMs = config.cacheTtlMs ?? DEFAULT_API_KEY_CACHE_TTL_MS;
192
192
  this.requiredClientId = config.requiredClientId ?? config.requiredAppId;
193
193
  this.requiredScopes = config.requiredScopes;
@@ -199,7 +199,7 @@ var M2mApiKeyValidator = class {
199
199
  }
200
200
  if (!this.authUrl) {
201
201
  throw new UnauthorizedError(
202
- "M2mApiKeyValidator: authUrl or DEVLAB_AUTH_URL must be configured for validation"
202
+ "M2mApiKeyValidator: authUrl or DEVLAB_PORTAL_API_URL must be configured for validation"
203
203
  );
204
204
  }
205
205
  const now = Date.now();
@@ -244,9 +244,13 @@ var M2mApiKeyValidator = class {
244
244
  }
245
245
  enforceKeyConstraints(result) {
246
246
  if (this.requiredClientId && result.appId && result.appId !== this.requiredClientId) {
247
- throw new ForbiddenError(
248
- "API key assigned to " + result.appId + ", required " + this.requiredClientId
249
- );
247
+ const keyScopes = result.scopes ?? [];
248
+ const hasWildcard = keyScopes.includes("*") || keyScopes.includes(`${this.requiredClientId}:*`) || keyScopes.includes(this.requiredClientId);
249
+ if (!hasWildcard) {
250
+ throw new ForbiddenError(
251
+ "API key assigned to " + result.appId + ", required " + this.requiredClientId
252
+ );
253
+ }
250
254
  }
251
255
  if (this.requiredScopes && this.requiredScopes.length > 0) {
252
256
  const keyScopes = result.scopes ?? [];
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants/auth.constants.ts","../src/types/auth.types.ts","../src/token/jwt-validator.ts","../src/token/m2m-validator.ts","../src/guards/auth-guards.ts","../src/middleware/auth-middleware.ts"],"names":["AppStatus","logger","loggerWithConfig","Logger","UnauthorizedError","ForbiddenError"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @file packages/auth-server/src/constants/auth.constants.ts\n * @description Centralized constants and header keys for server-side authentication.\n * @module @yuva-devlab/auth-server\n */\n\n/** Standard Authorization HTTP header name (lowercase for case-insensitive lookup) */\nexport const DEFAULT_AUTH_HEADER = \"authorization\";\n\n/** Standard machine-to-machine API key HTTP header name */\nexport const DEFAULT_API_KEY_HEADER = \"x-api-key\";\n\n/** Standard Bearer token prefix */\nexport const DEFAULT_BEARER_PREFIX = \"Bearer \";\n\n/** Default cache time-to-live for fetched RS256 JWKS public keys (1 hour) */\nexport const DEFAULT_JWKS_CACHE_TTL_MS = 3_600_000;\n\n/** Default cache time-to-live for validated M2M API keys (5 minutes) */\nexport const DEFAULT_API_KEY_CACHE_TTL_MS = 300_000;\n\n/** Default public endpoints bypassing mandatory authentication */\nexport const DEFAULT_PUBLIC_PATHS = Object.freeze([\n \"/health\",\n \"/ready\",\n \"/live\",\n \"/metrics\",\n]);\n\n/** HTTP Status Code for unauthenticated requests */\nexport const HTTP_STATUS_UNAUTHORIZED = 401;\n\n/** HTTP Status Code for unauthorized / forbidden role requests */\nexport const HTTP_STATUS_FORBIDDEN = 403;\n\n/** HTTP Status Code returned when an application is deactivated or killed */\nexport const HTTP_STATUS_SERVICE_UNAVAILABLE = 503;\n","/**\n * @file packages/auth-server/src/types/auth.types.ts\n * @description Type definitions and contracts for server authentication, JWKS, and app lifecycle.\n * @module @yuva-devlab/auth-server\n */\n\n/**\n * Operational state of an application on the DevLab platform.\n */\nexport enum AppStatus {\n /** Application is fully operational and serving traffic */\n ACTIVE = \"ACTIVE\",\n /** Application has been administratively disabled / deactivated (Kill-Switch triggered) */\n INACTIVE = \"INACTIVE\",\n /** Application is undergoing scheduled maintenance */\n MAINTENANCE = \"MAINTENANCE\",\n}\n\n/**\n * Authenticated user profile representation.\n */\nexport interface AuthUser {\n /** Canonical user UUID */\n readonly id: string;\n /** Primary user email address */\n readonly email?: string;\n /** Granted role slugs */\n readonly roles: readonly string[];\n /** Tenant isolation partition ID */\n readonly tenantId?: string;\n /** Arbitrary profile metadata */\n readonly metadata?: Record<string, unknown>;\n}\n\n/**\n * Machine-to-machine API key credential.\n */\nexport interface ApiKey {\n /** Primary API key UUID */\n readonly id: string;\n /** Secure key string */\n readonly key: string;\n /** Human-readable key name or description */\n readonly name: string;\n /** Scoped roles granted to this key */\n readonly roles: readonly string[];\n /** Tenant ID partition */\n readonly tenantId?: string;\n /** Active status flag */\n readonly isEnabled: boolean;\n}\n\n/**\n * Evaluated authentication context attached to inbound requests.\n */\nexport interface AuthContext {\n /** Whether the request carries valid credentials */\n readonly authenticated: boolean;\n /** Authenticated user if authenticated via session/JWT */\n readonly user?: AuthUser;\n /** Authenticated API key if authenticated via machine credential */\n readonly apiKey?: ApiKey;\n /** Consolidated roles granted across user or API key */\n readonly roles: readonly string[];\n /** Whether the host application is currently active on the platform */\n readonly isAppActive: boolean;\n /** Detailed app status */\n readonly appStatus?: AppStatus;\n /** Tenant partition ID */\n readonly tenantId?: string;\n /** Distributed trace request identifier */\n readonly requestId?: string;\n}\n\n/**\n * Configuration for verifying RS256 JWTs against a remote JWKS endpoint.\n */\nexport interface JwksConfig {\n /** JWKS endpoint URI exposing JSON Web Keys */\n readonly jwksUri: string;\n /** Expected audience claim */\n readonly audience?: string;\n /** Expected issuer claim */\n readonly issuer?: string;\n /** In-memory cache duration for public keys in milliseconds */\n readonly cacheTtlMs?: number;\n}\n\n/**\n * Decoded JWT claims payload.\n */\n\n/**\n * Result of validating a Machine-to-Machine API key against the DevLab Portal.\n */\nexport interface ApiKeyValidationResult {\n /** Whether the API key is active, valid, and unexpired */\n readonly valid: boolean;\n /** Primary API key UUID */\n readonly keyId?: string;\n /** Name assigned to this machine key */\n readonly name?: string;\n /** Target application slug this key belongs to */\n readonly appId?: string;\n /** Target application display name */\n readonly appName?: string;\n /** Scopes and permissions granted to this key */\n readonly scopes?: readonly string[];\n /** Allowed requests per minute limit */\n readonly rateLimitRpm?: number;\n /** Human-readable rejection reason if invalid */\n readonly reason?: string;\n}\n\n/**\n * Configuration options for Machine-to-Machine API key validation.\n */\nexport interface M2mConfig {\n /** Base URL of the authentication server / identity provider */\n readonly authUrl?: string;\n /** Legacy alias for authUrl */\n readonly portalUrl?: string;\n /** In-memory cache duration for validated API keys in milliseconds */\n readonly cacheTtlMs?: number;\n /** Expected client or application identifier */\n readonly requiredClientId?: string;\n /** Legacy alias for requiredClientId */\n readonly requiredAppId?: string;\n /** Required operational scopes that must be granted to the API key */\n readonly requiredScopes?: readonly string[];\n}\n\nexport interface JwtPayload {\n readonly sub: string;\n readonly iss?: string;\n readonly aud?: string | readonly string[];\n readonly exp?: number;\n readonly nbf?: number;\n readonly iat?: number;\n readonly email?: string;\n readonly name?: string;\n readonly role?: string;\n readonly roles?: readonly string[];\n readonly tenantId?: string;\n readonly tenant_id?: string;\n readonly tenant_slug?: string;\n readonly tenant_name?: string;\n readonly apps?: readonly string[];\n readonly appId?: string;\n readonly [key: string]: unknown;\n}\n","/**\n * @file packages/auth-server/src/token/jwt-validator.ts\n * @description RS256 JWKS validator providing cryptographic signature verification and claims checking.\n * @module @yuva-devlab/auth-server\n */\n\nimport crypto from \"node:crypto\";\nimport { UnauthorizedError } from \"@yuva-devlab/errors\";\nimport { Logger, loggerWithConfig } from \"@yuva-devlab/logger\";\nimport { DEFAULT_JWKS_CACHE_TTL_MS } from \"../constants\";\nimport type { JwksConfig, JwtPayload } from \"../types\";\n\nconst logger = loggerWithConfig(new Logger(\"JwksValidator\"));\n\ninterface CachedKey {\n readonly keyObject: crypto.KeyObject;\n readonly expiresAt: number;\n}\n\ninterface JwkRecord {\n readonly kid?: string;\n readonly kty: string;\n readonly alg?: string;\n readonly use?: string;\n readonly [key: string]: unknown;\n}\n\n/**\n * Validates JWT access tokens against an in-memory cached JWKS public key set.\n */\nexport class JwksValidator {\n private readonly jwksUri: string;\n private readonly audience?: string;\n private readonly issuer?: string;\n private readonly cacheTtlMs: number;\n private keyCache = new Map<string, CachedKey>();\n private jwksSetCache: {\n keys: readonly JwkRecord[];\n expiresAt: number;\n } | null = null;\n\n public constructor(config: Partial<JwksConfig> = {}) {\n this.jwksUri =\n config.jwksUri ??\n (config.issuer ? `${config.issuer}/.well-known/jwks.json` : undefined) ??\n (typeof process !== \"undefined\"\n ? (process.env?.DEVLAB_JWKS_URI ??\n (process.env?.DEVLAB_AUTH_URL\n ? `${process.env.DEVLAB_AUTH_URL}/.well-known/jwks.json`\n : undefined))\n : undefined) ??\n \"\";\n this.audience = config.audience;\n this.issuer = config.issuer;\n this.cacheTtlMs = config.cacheTtlMs ?? DEFAULT_JWKS_CACHE_TTL_MS;\n }\n\n public parseUnverified(token: string): {\n header: Record<string, unknown>;\n payload: JwtPayload;\n rawHeader: string;\n rawPayload: string;\n signature: string;\n } {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new UnauthorizedError(\n \"Malformed JWT token format: expected 3 segments\",\n );\n }\n\n try {\n const header = JSON.parse(\n Buffer.from(parts[0]!, \"base64url\").toString(\"utf-8\"),\n ) as Record<string, unknown>;\n const payload = JSON.parse(\n Buffer.from(parts[1]!, \"base64url\").toString(\"utf-8\"),\n ) as JwtPayload;\n return {\n header,\n payload,\n rawHeader: parts[0]!,\n rawPayload: parts[1]!,\n signature: parts[2]!,\n };\n } catch {\n throw new UnauthorizedError(\"Unable to decode JWT header or payload\");\n }\n }\n\n private async fetchJwks(): Promise<readonly JwkRecord[]> {\n if (!this.jwksUri) {\n throw new UnauthorizedError(\n \"JwksValidator: jwksUri, issuer, or DEVLAB_AUTH_URL must be configured\",\n );\n }\n const now = Date.now();\n if (this.jwksSetCache && this.jwksSetCache.expiresAt > now) {\n return this.jwksSetCache.keys;\n }\n\n try {\n const res = await fetch(this.jwksUri, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(5000),\n });\n\n if (!res.ok) {\n throw new Error(`JWKS endpoint returned HTTP ${res.status}`);\n }\n\n const body = (await res.json()) as { keys?: readonly JwkRecord[] };\n const keys = body.keys ?? [];\n this.jwksSetCache = { keys, expiresAt: now + this.cacheTtlMs };\n return keys;\n } catch (err) {\n logger.error(\"[fetchJwks] Failed to fetch JWKS public keys\", {\n uri: this.jwksUri,\n err,\n });\n if (this.jwksSetCache?.keys) return this.jwksSetCache.keys;\n throw new UnauthorizedError(\"Authentication service JWKS unreachable\");\n }\n }\n\n private async resolveKeyObject(kid?: string): Promise<crypto.KeyObject> {\n const now = Date.now();\n const cacheKey = kid ?? \"__default__\";\n const cached = this.keyCache.get(cacheKey);\n\n if (cached && cached.expiresAt > now) {\n return cached.keyObject;\n }\n\n const jwks = await this.fetchJwks();\n const matched = kid ? jwks.find((k) => k.kid === kid) : jwks[0];\n\n if (!matched) {\n throw new UnauthorizedError(\n `No matching JWK public key found for kid: ${kid ?? \"default\"}`,\n );\n }\n\n try {\n const keyObject = crypto.createPublicKey({\n key: matched as crypto.JsonWebKey,\n format: \"jwk\",\n });\n this.keyCache.set(cacheKey, {\n keyObject,\n expiresAt: now + this.cacheTtlMs,\n });\n return keyObject;\n } catch (err) {\n logger.error(\"[resolveKeyObject] Failed to parse JWK public key\", {\n kid,\n err,\n });\n throw new UnauthorizedError(\"Invalid JWK public key format\");\n }\n }\n\n public async verifyToken(token: string): Promise<JwtPayload> {\n const { header, payload, rawHeader, rawPayload, signature } =\n this.parseUnverified(token);\n\n if (header.alg && header.alg !== \"RS256\") {\n throw new UnauthorizedError(\n `Unsupported signing algorithm: ${header.alg}. Expected RS256`,\n );\n }\n\n const kid = typeof header.kid === \"string\" ? header.kid : undefined;\n const keyObject = await this.resolveKeyObject(kid);\n\n const data = Buffer.from(`${rawHeader}.${rawPayload}`);\n const sigBuffer = Buffer.from(signature, \"base64url\");\n const isSignatureValid = crypto.verify(\n \"RSA-SHA256\",\n data,\n keyObject,\n sigBuffer,\n );\n\n if (!isSignatureValid) {\n logger.warn(\"[verifyToken] Cryptographic signature check failed\", {\n sub: payload.sub,\n kid,\n });\n throw new UnauthorizedError(\"Invalid token signature\");\n }\n\n const nowSeconds = Math.floor(Date.now() / 1000);\n if (typeof payload.exp === \"number\" && nowSeconds >= payload.exp) {\n throw new UnauthorizedError(\"Authentication token has expired\");\n }\n\n if (typeof payload.nbf === \"number\" && nowSeconds < payload.nbf) {\n throw new UnauthorizedError(\"Authentication token is not yet active\");\n }\n\n if (this.issuer && payload.iss && payload.iss !== this.issuer) {\n throw new UnauthorizedError(\n `Token issuer mismatch: expected ${this.issuer}, got ${payload.iss}`,\n );\n }\n\n if (this.audience && payload.aud) {\n const audList = Array.isArray(payload.aud) ? payload.aud : [payload.aud];\n if (!audList.includes(this.audience)) {\n throw new UnauthorizedError(\n `Token audience mismatch: expected ${this.audience}`,\n );\n }\n }\n\n return payload;\n }\n\n public clearKeyCache(): void {\n this.keyCache.clear();\n this.jwksSetCache = null;\n logger.info(\"[clearKeyCache] JWKS public key cache cleared\");\n }\n}\n","/**\n * @file packages/auth-server/src/token/m2m-validator.ts\n * @description In-memory cached Machine-to-Machine API key validator introspecting the auth provider.\n * @module @yuva-devlab/auth-server\n */\n\nimport { ForbiddenError, UnauthorizedError } from \"@yuva-devlab/errors\";\nimport { Logger, loggerWithConfig } from \"@yuva-devlab/logger\";\nimport { DEFAULT_API_KEY_CACHE_TTL_MS } from \"../constants\";\nimport type { ApiKeyValidationResult, M2mConfig } from \"../types\";\n\nconst logger = loggerWithConfig(new Logger(\"M2mApiKeyValidator\"));\n\ninterface CachedApiKey {\n readonly result: ApiKeyValidationResult;\n readonly expiresAt: number;\n}\n\n/**\n * Validates machine-to-machine API keys against central authentication server with in-memory caching.\n */\nexport class M2mApiKeyValidator {\n private readonly authUrl?: string;\n private readonly cacheTtlMs: number;\n private readonly requiredClientId?: string;\n private readonly requiredScopes?: readonly string[];\n private readonly keyCache = new Map<string, CachedApiKey>();\n\n public constructor(config: Partial<M2mConfig> = {}) {\n this.authUrl =\n config.authUrl ??\n config.portalUrl ??\n process.env.DEVLAB_AUTH_URL ??\n process.env.DEVLAB_PORTAL_URL;\n this.cacheTtlMs = config.cacheTtlMs ?? DEFAULT_API_KEY_CACHE_TTL_MS;\n this.requiredClientId = config.requiredClientId ?? config.requiredAppId;\n this.requiredScopes = config.requiredScopes;\n }\n\n public async validateKey(rawKey: string): Promise<ApiKeyValidationResult> {\n const trimmed = rawKey.trim();\n if (!trimmed.startsWith(\"dlk_\")) {\n throw new UnauthorizedError(\"Malformed API key: expected 'dlk_' prefix\");\n }\n\n if (!this.authUrl) {\n throw new UnauthorizedError(\n \"M2mApiKeyValidator: authUrl or DEVLAB_AUTH_URL must be configured for validation\",\n );\n }\n\n const now = Date.now();\n const cached = this.keyCache.get(trimmed);\n if (cached && cached.expiresAt > now) {\n return this.enforceKeyConstraints(cached.result);\n }\n\n try {\n const url = `${this.authUrl}/api/v1/api-keys/validate`;\n const res = await fetch(url, {\n headers: {\n Accept: \"application/json\",\n Authorization: `Bearer ${trimmed}`,\n },\n signal: AbortSignal.timeout(5000),\n });\n\n if (!res.ok) {\n throw new Error(\n `Authentication validation endpoint returned HTTP ${res.status}`,\n );\n }\n\n const body = (await res.json()) as ApiKeyValidationResult;\n if (!body.valid) {\n throw new UnauthorizedError(body.reason ?? \"Invalid machine API key\");\n }\n\n this.keyCache.set(trimmed, {\n result: body,\n expiresAt: now + this.cacheTtlMs,\n });\n\n return this.enforceKeyConstraints(body);\n } catch (err) {\n if (err instanceof UnauthorizedError || err instanceof ForbiddenError) {\n throw err;\n }\n logger.error(\"[validateKey] Network error during API key introspection\", {\n err,\n });\n throw new UnauthorizedError(\n \"Authentication service unreachable for API key verification\",\n );\n }\n }\n\n private enforceKeyConstraints(\n result: ApiKeyValidationResult,\n ): ApiKeyValidationResult {\n if (\n this.requiredClientId &&\n result.appId &&\n result.appId !== this.requiredClientId\n ) {\n throw new ForbiddenError(\n \"API key assigned to \" +\n result.appId +\n \", required \" +\n this.requiredClientId,\n );\n }\n\n if (this.requiredScopes && this.requiredScopes.length > 0) {\n const keyScopes = result.scopes ?? [];\n const hasWildcard = keyScopes.includes(\"*\");\n if (!hasWildcard) {\n const hasAll = this.requiredScopes.every((scope) =>\n keyScopes.includes(scope),\n );\n if (!hasAll) {\n throw new ForbiddenError(\n \"API key lacks required scopes: [\" +\n this.requiredScopes.join(\", \") +\n \"]\",\n );\n }\n }\n }\n\n return result;\n }\n\n public clearCache(): void {\n this.keyCache.clear();\n logger.info(\"[clearCache] API key validation cache cleared\");\n }\n}\n","/**\n * @file packages/auth-server/src/guards/auth-guards.ts\n * @description Authorization assertions and scope/role enforcement guards.\n * @module @yuva-devlab/auth-server\n */\n\nimport { ForbiddenError, UnauthorizedError } from \"@yuva-devlab/errors\";\nimport { Logger, loggerWithConfig } from \"@yuva-devlab/logger\";\nimport type { ApiKey, AuthContext, AuthUser } from \"../types\";\n\nconst logger = loggerWithConfig(new Logger(\"AuthGuards\"));\n\n/**\n * Asserts that the request is authenticated with an active user session and optional role requirements.\n *\n * @param ctx - Inbound request auth context\n * @param requiredRoles - Optional list of required roles (user must possess at least one)\n * @returns Authenticated AuthUser\n * @throws UnauthorizedError if not authenticated as a user\n * @throws ForbiddenError if user lacks the required roles\n *\n * @example\n * ```typescript\n * const user = requireUser(ctx, [\"admin\", \"operator\"]);\n * ```\n */\nexport function requireUser(\n ctx: AuthContext,\n requiredRoles?: readonly string[],\n): AuthUser {\n if (!ctx.authenticated || !ctx.user) {\n logger.warn(\n \"[requireUser] Request rejected: missing authenticated user context\",\n );\n throw new UnauthorizedError(\n \"Authentication required: valid user session required\",\n );\n }\n\n if (requiredRoles && requiredRoles.length > 0) {\n const hasRole = requiredRoles.some((role) => ctx.roles.includes(role));\n if (!hasRole) {\n logger.warn(\"[requireUser] User lacks required roles\", {\n userId: ctx.user.id,\n userRoles: ctx.roles,\n requiredRoles,\n });\n throw new ForbiddenError(\n `Insufficient privileges: requires one of [${requiredRoles.join(\", \")}]`,\n );\n }\n }\n\n return ctx.user;\n}\n\n/**\n * Asserts that the request is authenticated via a machine-to-machine API key.\n *\n * @param ctx - Inbound request auth context\n * @returns Authenticated ApiKey\n * @throws UnauthorizedError if not authenticated via an API key\n */\nexport function requireApiKey(ctx: AuthContext): ApiKey {\n if (!ctx.authenticated || !ctx.apiKey) {\n logger.warn(\"[requireApiKey] Request rejected: missing machine API key\");\n throw new UnauthorizedError(\n \"Authentication required: valid API key required\",\n );\n }\n\n return ctx.apiKey;\n}\n\n/**\n * Asserts that the authenticated context (user or API key) possesses a specific operational scope.\n *\n * @param ctx - Inbound request auth context\n * @param scope - Required scope or permission string\n * @throws ForbiddenError if scope is missing\n */\nexport function requireScope(ctx: AuthContext, scope: string): void {\n if (!ctx.roles.includes(scope)) {\n logger.warn(\"[requireScope] Missing required scope\", {\n roles: ctx.roles,\n requiredScope: scope,\n });\n throw new ForbiddenError(`Missing required scope: '${scope}'`);\n }\n}\n","/**\n * @file packages/auth-server/src/middleware/auth-middleware.ts\n * @description Universal HTTP middleware providing platform kill-switch enforcement and credential validation.\n * @module @yuva-devlab/auth-server\n */\n\nimport { Logger, loggerWithConfig } from \"@yuva-devlab/logger\";\nimport {\n DEFAULT_API_KEY_HEADER,\n DEFAULT_AUTH_HEADER,\n DEFAULT_BEARER_PREFIX,\n DEFAULT_PUBLIC_PATHS,\n HTTP_STATUS_SERVICE_UNAVAILABLE,\n HTTP_STATUS_UNAUTHORIZED,\n} from \"../constants\";\nimport { AppStatus, type AuthContext } from \"../types\";\n\nconst logger = loggerWithConfig(new Logger(\"AuthMiddleware\"));\n\n/**\n * Minimal HTTP request and response interfaces for framework neutrality (Express, Fastify, Node http).\n */\nexport interface GenericHttpRequest {\n url?: string;\n method?: string;\n headers: Record<string, string | string[] | undefined>;\n authContext?: AuthContext;\n}\n\nexport interface GenericHttpResponse {\n statusCode?: number;\n setHeader(name: string, value: string): void;\n end(body?: string): void;\n}\n\n/**\n * Creates a kill-switch middleware that intercepts all traffic if an application is deactivated.\n *\n * @param getAppStatus - Callback returning current platform status of the application\n * @returns Middleware function returning true if allowed to proceed, false if blocked (503)\n *\n * @example\n * ```typescript\n * const killSwitch = createAppKillSwitchMiddleware(() => appState.status);\n * const allowed = await killSwitch(req, res);\n * if (!allowed) return;\n * ```\n */\nexport function createAppKillSwitchMiddleware(\n getAppStatus: () => Promise<AppStatus> | AppStatus,\n) {\n return async (\n req: GenericHttpRequest,\n res: GenericHttpResponse,\n ): Promise<boolean> => {\n const status = await getAppStatus();\n\n // If application has been administratively disabled or put into maintenance\n if (status === AppStatus.INACTIVE || status === AppStatus.MAINTENANCE) {\n logger.warn(\n \"[killSwitch] Application is inactive or under maintenance; request rejected\",\n {\n url: req.url,\n method: req.method,\n appStatus: status,\n },\n );\n\n res.statusCode = HTTP_STATUS_SERVICE_UNAVAILABLE;\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(\n JSON.stringify({\n error: {\n code: \"APP_DEACTIVATED\",\n message:\n status === AppStatus.MAINTENANCE\n ? \"Application is currently undergoing scheduled maintenance\"\n : \"Application has been deactivated by platform administrators\",\n status,\n },\n }),\n );\n return false;\n }\n\n return true;\n };\n}\n\n/**\n * Options configuring request authentication middleware.\n */\nexport interface CreateAuthMiddlewareOptions {\n /** Whitelisted public endpoints bypassing authentication */\n publicPaths?: readonly string[];\n /** Function resolving an API key against store */\n validateApiKey?: (key: string) => Promise<AuthContext | null>;\n /** Function resolving a Bearer session token against JWKS or session store */\n validateToken?: (token: string) => Promise<AuthContext | null>;\n}\n\n/**\n * Universal authentication middleware verifying API keys and Bearer tokens.\n *\n * @param options - Configuration options\n * @returns Middleware handler\n */\nexport function createAuthMiddleware(options: CreateAuthMiddlewareOptions) {\n const publicPaths = new Set([\n ...DEFAULT_PUBLIC_PATHS,\n ...(options.publicPaths ?? []),\n ]);\n\n return async (\n req: GenericHttpRequest,\n res: GenericHttpResponse,\n ): Promise<boolean> => {\n const urlPath = (req.url || \"/\").split(\"?\")[0] || \"/\";\n\n // 1. Bypass authentication for whitelisted public paths\n if (publicPaths.has(urlPath)) {\n req.authContext = {\n authenticated: false,\n roles: [],\n isAppActive: true,\n };\n return true;\n }\n\n // 2. Check for Machine-to-Machine API Key\n const rawApiKey = req.headers[DEFAULT_API_KEY_HEADER];\n const apiKey = typeof rawApiKey === \"string\" ? rawApiKey.trim() : undefined;\n\n if (apiKey && options.validateApiKey) {\n const apiKeyCtx = await options.validateApiKey(apiKey);\n if (apiKeyCtx?.authenticated) {\n logger.info(\"[authenticate] Authenticated via API Key\", {\n path: urlPath,\n });\n req.authContext = apiKeyCtx;\n return true;\n }\n }\n\n // 3. Check for Bearer Token\n const rawAuth = req.headers[DEFAULT_AUTH_HEADER];\n const authHeader = typeof rawAuth === \"string\" ? rawAuth.trim() : undefined;\n\n if (\n authHeader?.startsWith(DEFAULT_BEARER_PREFIX) &&\n options.validateToken\n ) {\n const token = authHeader.slice(DEFAULT_BEARER_PREFIX.length).trim();\n const tokenCtx = await options.validateToken(token);\n if (tokenCtx?.authenticated) {\n logger.info(\"[authenticate] Authenticated via Bearer token\", {\n path: urlPath,\n });\n req.authContext = tokenCtx;\n return true;\n }\n }\n\n // 4. Reject with 401 Unauthorized\n logger.warn(\"[authenticate] Unauthorized request rejected\", {\n path: urlPath,\n method: req.method,\n });\n\n res.statusCode = HTTP_STATUS_UNAUTHORIZED;\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(\n JSON.stringify({\n error: {\n code: \"UNAUTHORIZED\",\n message:\n \"Missing or invalid authentication credentials (Bearer token or API key required)\",\n },\n }),\n );\n return false;\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/constants/auth.constants.ts","../src/types/auth.types.ts","../src/token/jwt-validator.ts","../src/token/m2m-validator.ts","../src/guards/auth-guards.ts","../src/middleware/auth-middleware.ts"],"names":["AppStatus","logger","loggerWithConfig","Logger","UnauthorizedError","ForbiddenError"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @file packages/auth-server/src/constants/auth.constants.ts\n * @description Centralized constants and header keys for server-side authentication.\n * @module @yuva-devlab/auth-server\n */\n\n/** Standard Authorization HTTP header name (lowercase for case-insensitive lookup) */\nexport const DEFAULT_AUTH_HEADER = \"authorization\";\n\n/** Standard machine-to-machine API key HTTP header name */\nexport const DEFAULT_API_KEY_HEADER = \"x-api-key\";\n\n/** Standard Bearer token prefix */\nexport const DEFAULT_BEARER_PREFIX = \"Bearer \";\n\n/** Default cache time-to-live for fetched RS256 JWKS public keys (1 hour) */\nexport const DEFAULT_JWKS_CACHE_TTL_MS = 3_600_000;\n\n/** Default cache time-to-live for validated M2M API keys (5 minutes) */\nexport const DEFAULT_API_KEY_CACHE_TTL_MS = 300_000;\n\n/** Default public endpoints bypassing mandatory authentication */\nexport const DEFAULT_PUBLIC_PATHS = Object.freeze([\n \"/health\",\n \"/ready\",\n \"/live\",\n \"/metrics\",\n]);\n\n/** HTTP Status Code for unauthenticated requests */\nexport const HTTP_STATUS_UNAUTHORIZED = 401;\n\n/** HTTP Status Code for unauthorized / forbidden role requests */\nexport const HTTP_STATUS_FORBIDDEN = 403;\n\n/** HTTP Status Code returned when an application is deactivated or killed */\nexport const HTTP_STATUS_SERVICE_UNAVAILABLE = 503;\n","/**\n * @file packages/auth-server/src/types/auth.types.ts\n * @description Type definitions and contracts for server authentication, JWKS, and app lifecycle.\n * @module @yuva-devlab/auth-server\n */\n\n/**\n * Operational state of an application on the DevLab platform.\n */\nexport enum AppStatus {\n /** Application is fully operational and serving traffic */\n ACTIVE = \"ACTIVE\",\n /** Application has been administratively disabled / deactivated (Kill-Switch triggered) */\n INACTIVE = \"INACTIVE\",\n /** Application is undergoing scheduled maintenance */\n MAINTENANCE = \"MAINTENANCE\",\n}\n\n/**\n * Authenticated user profile representation.\n */\nexport interface AuthUser {\n /** Canonical user UUID */\n readonly id: string;\n /** Primary user email address */\n readonly email?: string;\n /** Granted role slugs */\n readonly roles: readonly string[];\n /** Tenant isolation partition ID */\n readonly tenantId?: string;\n /** Arbitrary profile metadata */\n readonly metadata?: Record<string, unknown>;\n}\n\n/**\n * Machine-to-machine API key credential.\n */\nexport interface ApiKey {\n /** Primary API key UUID */\n readonly id: string;\n /** Secure key string */\n readonly key: string;\n /** Human-readable key name or description */\n readonly name: string;\n /** Scoped roles granted to this key */\n readonly roles: readonly string[];\n /** Tenant ID partition */\n readonly tenantId?: string;\n /** Active status flag */\n readonly isEnabled: boolean;\n}\n\n/**\n * Evaluated authentication context attached to inbound requests.\n */\nexport interface AuthContext {\n /** Whether the request carries valid credentials */\n readonly authenticated: boolean;\n /** Authenticated user if authenticated via session/JWT */\n readonly user?: AuthUser;\n /** Authenticated API key if authenticated via machine credential */\n readonly apiKey?: ApiKey;\n /** Consolidated roles granted across user or API key */\n readonly roles: readonly string[];\n /** Whether the host application is currently active on the platform */\n readonly isAppActive: boolean;\n /** Detailed app status */\n readonly appStatus?: AppStatus;\n /** Tenant partition ID */\n readonly tenantId?: string;\n /** Distributed trace request identifier */\n readonly requestId?: string;\n}\n\n/**\n * Configuration for verifying RS256 JWTs against a remote JWKS endpoint.\n */\nexport interface JwksConfig {\n /** JWKS endpoint URI exposing JSON Web Keys */\n readonly jwksUri: string;\n /** Expected audience claim */\n readonly audience?: string;\n /** Expected issuer claim */\n readonly issuer?: string;\n /** In-memory cache duration for public keys in milliseconds */\n readonly cacheTtlMs?: number;\n}\n\n/**\n * Decoded JWT claims payload.\n */\n\n/**\n * Result of validating a Machine-to-Machine API key against the DevLab Portal.\n */\nexport interface ApiKeyValidationResult {\n /** Whether the API key is active, valid, and unexpired */\n readonly valid: boolean;\n /** Primary API key UUID */\n readonly keyId?: string;\n /** Name assigned to this machine key */\n readonly name?: string;\n /** Target application slug this key belongs to */\n readonly appId?: string;\n /** Target application display name */\n readonly appName?: string;\n /** Scopes and permissions granted to this key */\n readonly scopes?: readonly string[];\n /** Allowed requests per minute limit */\n readonly rateLimitRpm?: number;\n /** Human-readable rejection reason if invalid */\n readonly reason?: string;\n}\n\n/**\n * Configuration options for Machine-to-Machine API key validation.\n */\nexport interface M2mConfig {\n /** Base URL of the authentication server / identity provider */\n readonly authUrl?: string;\n /** Legacy alias for authUrl */\n readonly portalUrl?: string;\n /** In-memory cache duration for validated API keys in milliseconds */\n readonly cacheTtlMs?: number;\n /** Expected client or application identifier */\n readonly requiredClientId?: string;\n /** Legacy alias for requiredClientId */\n readonly requiredAppId?: string;\n /** Required operational scopes that must be granted to the API key */\n readonly requiredScopes?: readonly string[];\n}\n\nexport interface JwtPayload {\n readonly sub: string;\n readonly iss?: string;\n readonly aud?: string | readonly string[];\n readonly exp?: number;\n readonly nbf?: number;\n readonly iat?: number;\n readonly email?: string;\n readonly name?: string;\n readonly role?: string;\n readonly roles?: readonly string[];\n readonly tenantId?: string;\n readonly tenant_id?: string;\n readonly tenant_slug?: string;\n readonly tenant_name?: string;\n readonly apps?: readonly string[];\n readonly appId?: string;\n readonly [key: string]: unknown;\n}\n","/**\n * @file packages/auth-server/src/token/jwt-validator.ts\n * @description RS256 JWKS validator providing cryptographic signature verification and claims checking.\n * @module @yuva-devlab/auth-server\n */\n\nimport crypto from \"node:crypto\";\nimport { UnauthorizedError } from \"@yuva-devlab/errors\";\nimport { Logger, loggerWithConfig } from \"@yuva-devlab/logger\";\nimport { DEFAULT_JWKS_CACHE_TTL_MS } from \"../constants\";\nimport type { JwksConfig, JwtPayload } from \"../types\";\n\nconst logger = loggerWithConfig(new Logger(\"JwksValidator\"));\n\ninterface CachedKey {\n readonly keyObject: crypto.KeyObject;\n readonly expiresAt: number;\n}\n\ninterface JwkRecord {\n readonly kid?: string;\n readonly kty: string;\n readonly alg?: string;\n readonly use?: string;\n readonly [key: string]: unknown;\n}\n\n/**\n * Validates JWT access tokens against an in-memory cached JWKS public key set.\n */\nexport class JwksValidator {\n private readonly jwksUri: string;\n private readonly audience?: string;\n private readonly issuer?: string;\n private readonly cacheTtlMs: number;\n private keyCache = new Map<string, CachedKey>();\n private jwksSetCache: {\n keys: readonly JwkRecord[];\n expiresAt: number;\n } | null = null;\n\n public constructor(config: Partial<JwksConfig> = {}) {\n this.jwksUri =\n config.jwksUri ??\n (config.issuer ? `${config.issuer}/.well-known/jwks.json` : undefined) ??\n (typeof process !== \"undefined\"\n ? process.env?.DEVLAB_PORTAL_API_URL\n ? `${process.env.DEVLAB_PORTAL_API_URL}/.well-known/jwks.json`\n : undefined\n : undefined) ??\n \"\";\n this.audience = config.audience;\n this.issuer = config.issuer;\n this.cacheTtlMs = config.cacheTtlMs ?? DEFAULT_JWKS_CACHE_TTL_MS;\n }\n\n public parseUnverified(token: string): {\n header: Record<string, unknown>;\n payload: JwtPayload;\n rawHeader: string;\n rawPayload: string;\n signature: string;\n } {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw new UnauthorizedError(\n \"Malformed JWT token format: expected 3 segments\",\n );\n }\n\n try {\n const header = JSON.parse(\n Buffer.from(parts[0]!, \"base64url\").toString(\"utf-8\"),\n ) as Record<string, unknown>;\n const payload = JSON.parse(\n Buffer.from(parts[1]!, \"base64url\").toString(\"utf-8\"),\n ) as JwtPayload;\n return {\n header,\n payload,\n rawHeader: parts[0]!,\n rawPayload: parts[1]!,\n signature: parts[2]!,\n };\n } catch {\n throw new UnauthorizedError(\"Unable to decode JWT header or payload\");\n }\n }\n\n private async fetchJwks(): Promise<readonly JwkRecord[]> {\n if (!this.jwksUri) {\n throw new UnauthorizedError(\n \"JwksValidator: jwksUri, issuer, or DEVLAB_PORTAL_API_URL must be configured\",\n );\n }\n const now = Date.now();\n if (this.jwksSetCache && this.jwksSetCache.expiresAt > now) {\n return this.jwksSetCache.keys;\n }\n\n try {\n const res = await fetch(this.jwksUri, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(5000),\n });\n\n if (!res.ok) {\n throw new Error(`JWKS endpoint returned HTTP ${res.status}`);\n }\n\n const body = (await res.json()) as { keys?: readonly JwkRecord[] };\n const keys = body.keys ?? [];\n this.jwksSetCache = { keys, expiresAt: now + this.cacheTtlMs };\n return keys;\n } catch (err) {\n logger.error(\"[fetchJwks] Failed to fetch JWKS public keys\", {\n uri: this.jwksUri,\n err,\n });\n if (this.jwksSetCache?.keys) return this.jwksSetCache.keys;\n throw new UnauthorizedError(\"Authentication service JWKS unreachable\");\n }\n }\n\n private async resolveKeyObject(kid?: string): Promise<crypto.KeyObject> {\n const now = Date.now();\n const cacheKey = kid ?? \"__default__\";\n const cached = this.keyCache.get(cacheKey);\n\n if (cached && cached.expiresAt > now) {\n return cached.keyObject;\n }\n\n const jwks = await this.fetchJwks();\n const matched = kid ? jwks.find((k) => k.kid === kid) : jwks[0];\n\n if (!matched) {\n throw new UnauthorizedError(\n `No matching JWK public key found for kid: ${kid ?? \"default\"}`,\n );\n }\n\n try {\n const keyObject = crypto.createPublicKey({\n key: matched as crypto.JsonWebKey,\n format: \"jwk\",\n });\n this.keyCache.set(cacheKey, {\n keyObject,\n expiresAt: now + this.cacheTtlMs,\n });\n return keyObject;\n } catch (err) {\n logger.error(\"[resolveKeyObject] Failed to parse JWK public key\", {\n kid,\n err,\n });\n throw new UnauthorizedError(\"Invalid JWK public key format\");\n }\n }\n\n public async verifyToken(token: string): Promise<JwtPayload> {\n const { header, payload, rawHeader, rawPayload, signature } =\n this.parseUnverified(token);\n\n if (header.alg && header.alg !== \"RS256\") {\n throw new UnauthorizedError(\n `Unsupported signing algorithm: ${header.alg}. Expected RS256`,\n );\n }\n\n const kid = typeof header.kid === \"string\" ? header.kid : undefined;\n const keyObject = await this.resolveKeyObject(kid);\n\n const data = Buffer.from(`${rawHeader}.${rawPayload}`);\n const sigBuffer = Buffer.from(signature, \"base64url\");\n const isSignatureValid = crypto.verify(\n \"RSA-SHA256\",\n data,\n keyObject,\n sigBuffer,\n );\n\n if (!isSignatureValid) {\n logger.warn(\"[verifyToken] Cryptographic signature check failed\", {\n sub: payload.sub,\n kid,\n });\n throw new UnauthorizedError(\"Invalid token signature\");\n }\n\n const nowSeconds = Math.floor(Date.now() / 1000);\n if (typeof payload.exp === \"number\" && nowSeconds >= payload.exp) {\n throw new UnauthorizedError(\"Authentication token has expired\");\n }\n\n if (typeof payload.nbf === \"number\" && nowSeconds < payload.nbf) {\n throw new UnauthorizedError(\"Authentication token is not yet active\");\n }\n\n if (this.issuer && payload.iss && payload.iss !== this.issuer) {\n throw new UnauthorizedError(\n `Token issuer mismatch: expected ${this.issuer}, got ${payload.iss}`,\n );\n }\n\n if (this.audience && payload.aud) {\n const audList = Array.isArray(payload.aud) ? payload.aud : [payload.aud];\n if (!audList.includes(this.audience)) {\n throw new UnauthorizedError(\n `Token audience mismatch: expected ${this.audience}`,\n );\n }\n }\n\n return payload;\n }\n\n public clearKeyCache(): void {\n this.keyCache.clear();\n this.jwksSetCache = null;\n logger.info(\"[clearKeyCache] JWKS public key cache cleared\");\n }\n}\n","/**\n * @file packages/auth-server/src/token/m2m-validator.ts\n * @description In-memory cached Machine-to-Machine API key validator introspecting the auth provider.\n * @module @yuva-devlab/auth-server\n */\n\nimport { ForbiddenError, UnauthorizedError } from \"@yuva-devlab/errors\";\nimport { Logger, loggerWithConfig } from \"@yuva-devlab/logger\";\nimport { DEFAULT_API_KEY_CACHE_TTL_MS } from \"../constants\";\nimport type { ApiKeyValidationResult, M2mConfig } from \"../types\";\n\nconst logger = loggerWithConfig(new Logger(\"M2mApiKeyValidator\"));\n\ninterface CachedApiKey {\n readonly result: ApiKeyValidationResult;\n readonly expiresAt: number;\n}\n\n/**\n * Validates machine-to-machine API keys against central authentication server with in-memory caching.\n */\nexport class M2mApiKeyValidator {\n private readonly authUrl?: string;\n private readonly cacheTtlMs: number;\n private readonly requiredClientId?: string;\n private readonly requiredScopes?: readonly string[];\n private readonly keyCache = new Map<string, CachedApiKey>();\n\n public constructor(config: Partial<M2mConfig> = {}) {\n this.authUrl =\n config.authUrl ?? config.portalUrl ?? process.env.DEVLAB_PORTAL_API_URL;\n this.cacheTtlMs = config.cacheTtlMs ?? DEFAULT_API_KEY_CACHE_TTL_MS;\n this.requiredClientId = config.requiredClientId ?? config.requiredAppId;\n this.requiredScopes = config.requiredScopes;\n }\n\n public async validateKey(rawKey: string): Promise<ApiKeyValidationResult> {\n const trimmed = rawKey.trim();\n if (!trimmed.startsWith(\"dlk_\")) {\n throw new UnauthorizedError(\"Malformed API key: expected 'dlk_' prefix\");\n }\n\n if (!this.authUrl) {\n throw new UnauthorizedError(\n \"M2mApiKeyValidator: authUrl or DEVLAB_PORTAL_API_URL must be configured for validation\",\n );\n }\n\n const now = Date.now();\n const cached = this.keyCache.get(trimmed);\n if (cached && cached.expiresAt > now) {\n return this.enforceKeyConstraints(cached.result);\n }\n\n try {\n const url = `${this.authUrl}/api/v1/api-keys/validate`;\n const res = await fetch(url, {\n headers: {\n Accept: \"application/json\",\n Authorization: `Bearer ${trimmed}`,\n },\n signal: AbortSignal.timeout(5000),\n });\n\n if (!res.ok) {\n throw new Error(\n `Authentication validation endpoint returned HTTP ${res.status}`,\n );\n }\n\n const body = (await res.json()) as ApiKeyValidationResult;\n if (!body.valid) {\n throw new UnauthorizedError(body.reason ?? \"Invalid machine API key\");\n }\n\n this.keyCache.set(trimmed, {\n result: body,\n expiresAt: now + this.cacheTtlMs,\n });\n\n return this.enforceKeyConstraints(body);\n } catch (err) {\n if (err instanceof UnauthorizedError || err instanceof ForbiddenError) {\n throw err;\n }\n logger.error(\"[validateKey] Network error during API key introspection\", {\n err,\n });\n throw new UnauthorizedError(\n \"Authentication service unreachable for API key verification\",\n );\n }\n }\n\n private enforceKeyConstraints(\n result: ApiKeyValidationResult,\n ): ApiKeyValidationResult {\n if (\n this.requiredClientId &&\n result.appId &&\n result.appId !== this.requiredClientId\n ) {\n const keyScopes = result.scopes ?? [];\n const hasWildcard =\n keyScopes.includes(\"*\") ||\n keyScopes.includes(`${this.requiredClientId}:*`) ||\n keyScopes.includes(this.requiredClientId);\n if (!hasWildcard) {\n throw new ForbiddenError(\n \"API key assigned to \" +\n result.appId +\n \", required \" +\n this.requiredClientId,\n );\n }\n }\n\n if (this.requiredScopes && this.requiredScopes.length > 0) {\n const keyScopes = result.scopes ?? [];\n const hasWildcard = keyScopes.includes(\"*\");\n if (!hasWildcard) {\n const hasAll = this.requiredScopes.every((scope) =>\n keyScopes.includes(scope),\n );\n if (!hasAll) {\n throw new ForbiddenError(\n \"API key lacks required scopes: [\" +\n this.requiredScopes.join(\", \") +\n \"]\",\n );\n }\n }\n }\n\n return result;\n }\n\n public clearCache(): void {\n this.keyCache.clear();\n logger.info(\"[clearCache] API key validation cache cleared\");\n }\n}\n","/**\n * @file packages/auth-server/src/guards/auth-guards.ts\n * @description Authorization assertions and scope/role enforcement guards.\n * @module @yuva-devlab/auth-server\n */\n\nimport { ForbiddenError, UnauthorizedError } from \"@yuva-devlab/errors\";\nimport { Logger, loggerWithConfig } from \"@yuva-devlab/logger\";\nimport type { ApiKey, AuthContext, AuthUser } from \"../types\";\n\nconst logger = loggerWithConfig(new Logger(\"AuthGuards\"));\n\n/**\n * Asserts that the request is authenticated with an active user session and optional role requirements.\n *\n * @param ctx - Inbound request auth context\n * @param requiredRoles - Optional list of required roles (user must possess at least one)\n * @returns Authenticated AuthUser\n * @throws UnauthorizedError if not authenticated as a user\n * @throws ForbiddenError if user lacks the required roles\n *\n * @example\n * ```typescript\n * const user = requireUser(ctx, [\"admin\", \"operator\"]);\n * ```\n */\nexport function requireUser(\n ctx: AuthContext,\n requiredRoles?: readonly string[],\n): AuthUser {\n if (!ctx.authenticated || !ctx.user) {\n logger.warn(\n \"[requireUser] Request rejected: missing authenticated user context\",\n );\n throw new UnauthorizedError(\n \"Authentication required: valid user session required\",\n );\n }\n\n if (requiredRoles && requiredRoles.length > 0) {\n const hasRole = requiredRoles.some((role) => ctx.roles.includes(role));\n if (!hasRole) {\n logger.warn(\"[requireUser] User lacks required roles\", {\n userId: ctx.user.id,\n userRoles: ctx.roles,\n requiredRoles,\n });\n throw new ForbiddenError(\n `Insufficient privileges: requires one of [${requiredRoles.join(\", \")}]`,\n );\n }\n }\n\n return ctx.user;\n}\n\n/**\n * Asserts that the request is authenticated via a machine-to-machine API key.\n *\n * @param ctx - Inbound request auth context\n * @returns Authenticated ApiKey\n * @throws UnauthorizedError if not authenticated via an API key\n */\nexport function requireApiKey(ctx: AuthContext): ApiKey {\n if (!ctx.authenticated || !ctx.apiKey) {\n logger.warn(\"[requireApiKey] Request rejected: missing machine API key\");\n throw new UnauthorizedError(\n \"Authentication required: valid API key required\",\n );\n }\n\n return ctx.apiKey;\n}\n\n/**\n * Asserts that the authenticated context (user or API key) possesses a specific operational scope.\n *\n * @param ctx - Inbound request auth context\n * @param scope - Required scope or permission string\n * @throws ForbiddenError if scope is missing\n */\nexport function requireScope(ctx: AuthContext, scope: string): void {\n if (!ctx.roles.includes(scope)) {\n logger.warn(\"[requireScope] Missing required scope\", {\n roles: ctx.roles,\n requiredScope: scope,\n });\n throw new ForbiddenError(`Missing required scope: '${scope}'`);\n }\n}\n","/**\n * @file packages/auth-server/src/middleware/auth-middleware.ts\n * @description Universal HTTP middleware providing platform kill-switch enforcement and credential validation.\n * @module @yuva-devlab/auth-server\n */\n\nimport { Logger, loggerWithConfig } from \"@yuva-devlab/logger\";\nimport {\n DEFAULT_API_KEY_HEADER,\n DEFAULT_AUTH_HEADER,\n DEFAULT_BEARER_PREFIX,\n DEFAULT_PUBLIC_PATHS,\n HTTP_STATUS_SERVICE_UNAVAILABLE,\n HTTP_STATUS_UNAUTHORIZED,\n} from \"../constants\";\nimport { AppStatus, type AuthContext } from \"../types\";\n\nconst logger = loggerWithConfig(new Logger(\"AuthMiddleware\"));\n\n/**\n * Minimal HTTP request and response interfaces for framework neutrality (Express, Fastify, Node http).\n */\nexport interface GenericHttpRequest {\n url?: string;\n method?: string;\n headers: Record<string, string | string[] | undefined>;\n authContext?: AuthContext;\n}\n\nexport interface GenericHttpResponse {\n statusCode?: number;\n setHeader(name: string, value: string): void;\n end(body?: string): void;\n}\n\n/**\n * Creates a kill-switch middleware that intercepts all traffic if an application is deactivated.\n *\n * @param getAppStatus - Callback returning current platform status of the application\n * @returns Middleware function returning true if allowed to proceed, false if blocked (503)\n *\n * @example\n * ```typescript\n * const killSwitch = createAppKillSwitchMiddleware(() => appState.status);\n * const allowed = await killSwitch(req, res);\n * if (!allowed) return;\n * ```\n */\nexport function createAppKillSwitchMiddleware(\n getAppStatus: () => Promise<AppStatus> | AppStatus,\n) {\n return async (\n req: GenericHttpRequest,\n res: GenericHttpResponse,\n ): Promise<boolean> => {\n const status = await getAppStatus();\n\n // If application has been administratively disabled or put into maintenance\n if (status === AppStatus.INACTIVE || status === AppStatus.MAINTENANCE) {\n logger.warn(\n \"[killSwitch] Application is inactive or under maintenance; request rejected\",\n {\n url: req.url,\n method: req.method,\n appStatus: status,\n },\n );\n\n res.statusCode = HTTP_STATUS_SERVICE_UNAVAILABLE;\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(\n JSON.stringify({\n error: {\n code: \"APP_DEACTIVATED\",\n message:\n status === AppStatus.MAINTENANCE\n ? \"Application is currently undergoing scheduled maintenance\"\n : \"Application has been deactivated by platform administrators\",\n status,\n },\n }),\n );\n return false;\n }\n\n return true;\n };\n}\n\n/**\n * Options configuring request authentication middleware.\n */\nexport interface CreateAuthMiddlewareOptions {\n /** Whitelisted public endpoints bypassing authentication */\n publicPaths?: readonly string[];\n /** Function resolving an API key against store */\n validateApiKey?: (key: string) => Promise<AuthContext | null>;\n /** Function resolving a Bearer session token against JWKS or session store */\n validateToken?: (token: string) => Promise<AuthContext | null>;\n}\n\n/**\n * Universal authentication middleware verifying API keys and Bearer tokens.\n *\n * @param options - Configuration options\n * @returns Middleware handler\n */\nexport function createAuthMiddleware(options: CreateAuthMiddlewareOptions) {\n const publicPaths = new Set([\n ...DEFAULT_PUBLIC_PATHS,\n ...(options.publicPaths ?? []),\n ]);\n\n return async (\n req: GenericHttpRequest,\n res: GenericHttpResponse,\n ): Promise<boolean> => {\n const urlPath = (req.url || \"/\").split(\"?\")[0] || \"/\";\n\n // 1. Bypass authentication for whitelisted public paths\n if (publicPaths.has(urlPath)) {\n req.authContext = {\n authenticated: false,\n roles: [],\n isAppActive: true,\n };\n return true;\n }\n\n // 2. Check for Machine-to-Machine API Key\n const rawApiKey = req.headers[DEFAULT_API_KEY_HEADER];\n const apiKey = typeof rawApiKey === \"string\" ? rawApiKey.trim() : undefined;\n\n if (apiKey && options.validateApiKey) {\n const apiKeyCtx = await options.validateApiKey(apiKey);\n if (apiKeyCtx?.authenticated) {\n logger.info(\"[authenticate] Authenticated via API Key\", {\n path: urlPath,\n });\n req.authContext = apiKeyCtx;\n return true;\n }\n }\n\n // 3. Check for Bearer Token\n const rawAuth = req.headers[DEFAULT_AUTH_HEADER];\n const authHeader = typeof rawAuth === \"string\" ? rawAuth.trim() : undefined;\n\n if (\n authHeader?.startsWith(DEFAULT_BEARER_PREFIX) &&\n options.validateToken\n ) {\n const token = authHeader.slice(DEFAULT_BEARER_PREFIX.length).trim();\n const tokenCtx = await options.validateToken(token);\n if (tokenCtx?.authenticated) {\n logger.info(\"[authenticate] Authenticated via Bearer token\", {\n path: urlPath,\n });\n req.authContext = tokenCtx;\n return true;\n }\n }\n\n // 4. Reject with 401 Unauthorized\n logger.warn(\"[authenticate] Unauthorized request rejected\", {\n path: urlPath,\n method: req.method,\n });\n\n res.statusCode = HTTP_STATUS_UNAUTHORIZED;\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(\n JSON.stringify({\n error: {\n code: \"UNAUTHORIZED\",\n message:\n \"Missing or invalid authentication credentials (Bearer token or API key required)\",\n },\n }),\n );\n return false;\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuva-devlab/auth-server",
3
- "version": "0.3.0",
3
+ "version": "1.0.0",
4
4
  "description": "Server-side authentication guards, cached JWKS validator, and kill-switch middleware for DevLab",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -26,8 +26,8 @@
26
26
  "license": "MIT",
27
27
  "dependencies": {
28
28
  "zod": "^3.25.76",
29
- "@yuva-devlab/logger": "1.2.0",
30
- "@yuva-devlab/errors": "0.2.0"
29
+ "@yuva-devlab/errors": "0.2.0",
30
+ "@yuva-devlab/logger": "1.2.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^24.10.1",
@@ -35,8 +35,8 @@
35
35
  "prettier": "^3.9.8",
36
36
  "tsup": "^8.5.1",
37
37
  "typescript": "^6.0.3",
38
- "@yuva-devlab/prettier-config": "0.1.1",
39
38
  "@yuva-devlab/eslint-config": "0.1.1",
39
+ "@yuva-devlab/prettier-config": "0.1.1",
40
40
  "@yuva-devlab/typescript-config": "0.1.1"
41
41
  },
42
42
  "scripts": {