@vritti/api-sdk 0.4.2 → 0.4.4

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.
Files changed (61) hide show
  1. package/dist/auth.cjs +58 -17
  2. package/dist/auth.cjs.map +1 -1
  3. package/dist/auth.d.cts +1 -1
  4. package/dist/auth.d.ts +1 -1
  5. package/dist/auth.js +58 -17
  6. package/dist/auth.js.map +1 -1
  7. package/dist/cache.cjs +51 -17
  8. package/dist/cache.cjs.map +1 -1
  9. package/dist/cache.js +51 -17
  10. package/dist/cache.js.map +1 -1
  11. package/dist/catalog-resolver.cjs +3 -3
  12. package/dist/catalog-resolver.cjs.map +1 -1
  13. package/dist/catalog-resolver.d.cts +2 -2
  14. package/dist/catalog-resolver.d.ts +2 -2
  15. package/dist/catalog-resolver.js +3 -3
  16. package/dist/catalog-resolver.js.map +1 -1
  17. package/dist/{code-pattern-BP4zkNtT.d.cts → code-pattern-BQM7qLcU.d.cts} +1 -1
  18. package/dist/{code-pattern-BP4zkNtT.d.ts → code-pattern-BQM7qLcU.d.ts} +1 -1
  19. package/dist/data-table.cjs +153 -47
  20. package/dist/data-table.cjs.map +1 -1
  21. package/dist/data-table.js +153 -47
  22. package/dist/data-table.js.map +1 -1
  23. package/dist/database.cjs +101 -31
  24. package/dist/database.cjs.map +1 -1
  25. package/dist/database.js +101 -31
  26. package/dist/database.js.map +1 -1
  27. package/dist/decorators.d.cts +2 -2
  28. package/dist/decorators.d.ts +2 -2
  29. package/dist/drizzle-pg-core.d.cts +1 -1
  30. package/dist/drizzle-pg-core.d.ts +1 -1
  31. package/dist/email.cjs +22 -6
  32. package/dist/email.cjs.map +1 -1
  33. package/dist/email.js +22 -6
  34. package/dist/email.js.map +1 -1
  35. package/dist/files.cjs +1 -1
  36. package/dist/files.cjs.map +1 -1
  37. package/dist/files.js +1 -1
  38. package/dist/files.js.map +1 -1
  39. package/dist/filters.cjs +18 -4
  40. package/dist/filters.cjs.map +1 -1
  41. package/dist/filters.js +18 -4
  42. package/dist/filters.js.map +1 -1
  43. package/dist/index.d.cts +1 -1
  44. package/dist/index.d.ts +1 -1
  45. package/dist/license.d.cts +1 -1
  46. package/dist/license.d.ts +1 -1
  47. package/dist/logger.cjs +49 -15
  48. package/dist/logger.cjs.map +1 -1
  49. package/dist/logger.js +49 -15
  50. package/dist/logger.js.map +1 -1
  51. package/dist/nats.cjs +49 -17
  52. package/dist/nats.cjs.map +1 -1
  53. package/dist/nats.js +49 -17
  54. package/dist/nats.js.map +1 -1
  55. package/dist/root.cjs +44 -12
  56. package/dist/root.cjs.map +1 -1
  57. package/dist/root.js +44 -12
  58. package/dist/root.js.map +1 -1
  59. package/dist/{types-Cj6uMN5E.d.cts → types-VDKSjje5.d.cts} +3 -3
  60. package/dist/{types-Cj6uMN5E.d.ts → types-VDKSjje5.d.ts} +3 -3
  61. package/package.json +29 -29
package/dist/auth.cjs CHANGED
@@ -77,7 +77,7 @@ var PARTY_ID_HEADER = "x-party-id";
77
77
  var MAX_CLOCK_SKEW_SECONDS = 300;
78
78
 
79
79
  // src/auth/auth.config.ts
80
- var AUTH_CONFIG = Symbol("AUTH_CONFIG");
80
+ var AUTH_CONFIG = /* @__PURE__ */ Symbol("AUTH_CONFIG");
81
81
  var AUTH_CONFIG_DEFAULTS = {
82
82
  cookie: {
83
83
  refreshCookieName: "vritti_refresh",
@@ -127,13 +127,22 @@ __name(resolveInjectedRequest, "resolveInjectedRequest");
127
127
  // src/request/services/request.service.ts
128
128
  function _ts_decorate(decorators, target, key, desc) {
129
129
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
130
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
131
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
130
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
131
+ r = Reflect.decorate(decorators, target, key, desc);
132
+ } else {
133
+ for (var i = decorators.length - 1; i >= 0; i--) {
134
+ if (d = decorators[i]) {
135
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
136
+ }
137
+ }
138
+ }
132
139
  return c > 3 && r && Object.defineProperty(target, key, r), r;
133
140
  }
134
141
  __name(_ts_decorate, "_ts_decorate");
135
- function _ts_metadata(k, v) {
136
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
142
+ function _ts_metadata(metadataKey, metadataValue) {
143
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
144
+ return Reflect.metadata(metadataKey, metadataValue);
145
+ }
137
146
  }
138
147
  __name(_ts_metadata, "_ts_metadata");
139
148
  function _ts_param(paramIndex, decorator) {
@@ -258,8 +267,15 @@ RequestService = _ts_decorate([
258
267
  // src/request/request.module.ts
259
268
  function _ts_decorate2(decorators, target, key, desc) {
260
269
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
261
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
262
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
270
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
271
+ r = Reflect.decorate(decorators, target, key, desc);
272
+ } else {
273
+ for (var i = decorators.length - 1; i >= 0; i--) {
274
+ if (d = decorators[i]) {
275
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
276
+ }
277
+ }
278
+ }
263
279
  return c > 3 && r && Object.defineProperty(target, key, r), r;
264
280
  }
265
281
  __name(_ts_decorate2, "_ts_decorate");
@@ -385,13 +401,22 @@ __name(verifyTokenHash, "verifyTokenHash");
385
401
  // src/auth/services/token.service.ts
386
402
  function _ts_decorate3(decorators, target, key, desc) {
387
403
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
388
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
389
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
404
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
405
+ r = Reflect.decorate(decorators, target, key, desc);
406
+ } else {
407
+ for (var i = decorators.length - 1; i >= 0; i--) {
408
+ if (d = decorators[i]) {
409
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
410
+ }
411
+ }
412
+ }
390
413
  return c > 3 && r && Object.defineProperty(target, key, r), r;
391
414
  }
392
415
  __name(_ts_decorate3, "_ts_decorate");
393
- function _ts_metadata2(k, v) {
394
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
416
+ function _ts_metadata2(metadataKey, metadataValue) {
417
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
418
+ return Reflect.metadata(metadataKey, metadataValue);
419
+ }
395
420
  }
396
421
  __name(_ts_metadata2, "_ts_metadata");
397
422
  function _ts_param2(paramIndex, decorator) {
@@ -521,13 +546,22 @@ TokenService = _ts_decorate3([
521
546
  // src/auth/guards/vritti-auth.guard.ts
522
547
  function _ts_decorate4(decorators, target, key, desc) {
523
548
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
524
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
525
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
549
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
550
+ r = Reflect.decorate(decorators, target, key, desc);
551
+ } else {
552
+ for (var i = decorators.length - 1; i >= 0; i--) {
553
+ if (d = decorators[i]) {
554
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
555
+ }
556
+ }
557
+ }
526
558
  return c > 3 && r && Object.defineProperty(target, key, r), r;
527
559
  }
528
560
  __name(_ts_decorate4, "_ts_decorate");
529
- function _ts_metadata3(k, v) {
530
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
561
+ function _ts_metadata3(metadataKey, metadataValue) {
562
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
563
+ return Reflect.metadata(metadataKey, metadataValue);
564
+ }
531
565
  }
532
566
  __name(_ts_metadata3, "_ts_metadata");
533
567
  function _ts_param3(paramIndex, decorator) {
@@ -772,8 +806,15 @@ VrittiAuthGuard = _ts_decorate4([
772
806
  // src/auth/auth-config.module.ts
773
807
  function _ts_decorate5(decorators, target, key, desc) {
774
808
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
775
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
776
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
809
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
810
+ r = Reflect.decorate(decorators, target, key, desc);
811
+ } else {
812
+ for (var i = decorators.length - 1; i >= 0; i--) {
813
+ if (d = decorators[i]) {
814
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
815
+ }
816
+ }
817
+ }
777
818
  return c > 3 && r && Object.defineProperty(target, key, r), r;
778
819
  }
779
820
  __name(_ts_decorate5, "_ts_decorate");
package/dist/auth.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/auth/index.ts","../src/signing/request.ts","../src/auth/app-request.ts","../src/auth/auth.config.ts","../src/auth/auth-config.module.ts","../src/request/request.module.ts","../src/request/services/request.service.ts","../src/context/resolve-request.ts","../src/auth/guards/vritti-auth.guard.ts","../src/context/extractors/graphql.extractor.ts","../src/context/extractors/http.extractor.ts","../src/context/context.registry.ts","../src/context/get-request.ts","../src/context/get-response.ts","../src/auth/decorators/require.decorator.ts","../src/auth/decorators/skip-csrf.decorator.ts","../src/auth/services/token.service.ts","../src/utils/time.utils.ts","../src/auth/utils/token-hash.util.ts","../src/auth/decorators/access-token.decorator.ts","../src/auth/decorators/client-ip.decorator.ts","../src/auth/decorators/cookie-domain.decorator.ts","../src/auth/decorators/cookie-name.decorator.ts","../src/auth/decorators/hostname.decorator.ts","../src/auth/decorators/refresh-cookie-options.decorator.ts","../src/exceptions/bad-gateway.exception.ts","../src/exceptions/base-field.exception.ts","../src/exceptions/bad-request.exception.ts","../src/exceptions/conflict.exception.ts","../src/exceptions/forbidden.exception.ts","../src/exceptions/gone.exception.ts","../src/exceptions/internal-server-error.exception.ts","../src/exceptions/method-not-allowed.exception.ts","../src/exceptions/not-acceptable.exception.ts","../src/exceptions/not-found.exception.ts","../src/exceptions/not-implemented.exception.ts","../src/exceptions/payload-too-large.exception.ts","../src/exceptions/request-timeout.exception.ts","../src/exceptions/service-unavailable.exception.ts","../src/exceptions/too-many-requests.exception.ts","../src/exceptions/unauthorized.exception.ts","../src/exceptions/unprocessable-entity.exception.ts","../src/exceptions/unsupported-media-type.exception.ts","../src/exceptions/validation.exception.ts","../src/auth/decorators/refresh-token-cookie.decorator.ts","../src/auth/decorators/session-data.decorator.ts","../src/auth/decorators/subdomain.decorator.ts","../src/auth/decorators/user-agent.decorator.ts","../src/auth/decorators/user-id.decorator.ts"],"sourcesContent":["// Token types and config\n\nexport * from './app-request';\nexport {\n type AccessTokenPayload,\n AUTH_CONFIG,\n AUTH_CONFIG_DEFAULTS,\n type AuthConfig,\n type CookieConfig,\n type CookieSerializeOptions,\n type DecodedAccessToken,\n type DecodedRefreshToken,\n type GuardConfig,\n type OnAuthenticatedCallback,\n type RefreshTokenPayload,\n type TokenExpiry,\n type TokenExpiryString,\n TokenType,\n} from './auth.config';\nexport * from './auth-config.module';\nexport * from './decorators/access-token.decorator';\nexport * from './decorators/client-ip.decorator';\nexport * from './decorators/cookie-domain.decorator';\nexport * from './decorators/cookie-name.decorator';\nexport * from './decorators/hostname.decorator';\nexport * from './decorators/refresh-cookie-options.decorator';\nexport * from './decorators/refresh-token-cookie.decorator';\nexport * from './decorators/require.decorator';\nexport type { SessionInfo } from './decorators/session-data.decorator';\nexport { SessionData } from './decorators/session-data.decorator';\nexport { SKIP_CSRF_KEY, SkipCsrf } from './decorators/skip-csrf.decorator';\nexport * from './decorators/subdomain.decorator';\nexport * from './decorators/user-agent.decorator';\nexport * from './decorators/user-id.decorator';\nexport * from './guards/vritti-auth.guard';\n// Token service — generation, validation, and binding verification\nexport { TokenService } from './services/token.service';\n\n// Token hash utilities\nexport { hashToken, verifyTokenHash } from './utils/token-hash.util';\n","import { createHash, createPrivateKey, createPublicKey, sign, verify } from 'node:crypto';\n\n/**\n * The workspace-scope headers, in the order the canonical appends them.\n *\n * Exactly one is expected — the kind of scope *is* which header is sent, the same\n * convention core-web and core-app already use. Signing the header **name** rather\n * than a separate `kind` field means a verifier needs no scope vocabulary: it scans\n * this same list in this same order and rebuilds an identical string.\n *\n * Fixed and shared so a signer and a verifier can never disagree about it, and so the\n * discouraged multi-header case is deterministic rather than ambiguous.\n */\nexport const WORKSPACE_HEADER_ORDER = ['x-site-id', 'x-sg-id', 'x-le-id', 'x-org-id'] as const;\n\nexport interface RequestCanonicalInput {\n method: string;\n path: string;\n orgId?: string;\n body?: string | Buffer;\n timestamp: number;\n /**\n * The raw query string, without the leading `?`.\n *\n * Signed byte for byte, with no sorting or re-encoding, so the signer and the\n * verifier cannot disagree about normalization. The consequence is that a proxy\n * which reorders or re-encodes parameters invalidates the signature.\n */\n query?: string;\n /**\n * The party (person) the request acts for, when there is one.\n *\n * Signed so a caller cannot be swapped for another shopper in transit.\n */\n partyId?: string;\n /**\n * The workspace-scope headers actually present on the request, by header name.\n *\n * Only names in `WORKSPACE_HEADER_ORDER` are considered, and they are appended in\n * that order — so signer and verifier agree without either needing to know what a\n * \"site\" or a \"legal entity\" is.\n */\n workspaceHeaders?: Record<string, string | undefined>;\n}\n\nexport interface SignRequestInput {\n method: string;\n path: string;\n orgId?: string;\n body?: string | Buffer;\n /** Raw query string, without the leading `?`. See `RequestCanonicalInput`. */\n query?: string;\n /**\n * The party (person) the request acts for, when there is one.\n *\n * Signed so a caller cannot be swapped for another shopper in transit.\n */\n partyId?: string;\n /**\n * The workspace-scope headers actually present on the request, by header name.\n *\n * Only names in `WORKSPACE_HEADER_ORDER` are considered, and they are appended in\n * that order — so signer and verifier agree without either needing to know what a\n * \"site\" or a \"legal entity\" is.\n */\n workspaceHeaders?: Record<string, string | undefined>;\n}\n\nexport interface VerifySignedRequestInput {\n method: string;\n path: string;\n orgId?: string;\n /** Raw query string, without the leading `?`. See `RequestCanonicalInput`. */\n query?: string;\n /**\n * The party (person) the request acts for, when there is one.\n *\n * Signed so a caller cannot be swapped for another shopper in transit.\n */\n partyId?: string;\n /**\n * The workspace-scope headers actually present on the request, by header name.\n *\n * Only names in `WORKSPACE_HEADER_ORDER` are considered, and they are appended in\n * that order — so signer and verifier agree without either needing to know what a\n * \"site\" or a \"legal entity\" is.\n */\n workspaceHeaders?: Record<string, string | undefined>;\n rawBody?: string | Buffer;\n timestamp: string | number;\n signature: string;\n publicKey: string;\n maxSkewSeconds?: number;\n}\n\n/**\n * Builds the canonical string a request's signature is made over:\n * `METHOD\\npath\\norgId\\nsha256hex(body)\\ntimestamp`, then a labelled line per\n * optional field that is present.\n *\n * Optional fields are appended **only when set**, so a caller that supplies none\n * produces exactly the string this function has always produced — which is what\n * lets new fields be added without invalidating existing signers. They are\n * **labelled** so that \"query present, party absent\" can never be confused with\n * \"party present, query absent\".\n *\n * Absence is still tamper-evident: strip a signed field in transit and the\n * verifier rebuilds the canonical without it, which no longer matches.\n */\nexport function buildRequestCanonical(input: RequestCanonicalInput): string {\n const bodyHash = createHash('sha256')\n .update(input.body ?? '')\n .digest('hex');\n\n const lines = [input.method.toUpperCase(), input.path, input.orgId ?? '', bodyHash, String(input.timestamp)];\n if (input.query) lines.push(`query:${input.query}`);\n if (input.partyId) lines.push(`party:${input.partyId}`);\n\n // Header name and value both go in, so swapping x-le-id for x-site-id on the same\n // value breaks the signature. Fixed order, so multiple headers stay deterministic.\n for (const header of WORKSPACE_HEADER_ORDER) {\n const value = input.workspaceHeaders?.[header];\n if (value) lines.push(`${header}:${value}`);\n }\n\n return lines.join('\\n');\n}\n\n// Signs a request with an Ed25519 private key (base64 pkcs8 DER), stamping the current unix time\nexport function signRequestHeaders(\n input: SignRequestInput,\n privateKeyBase64: string,\n): { 'x-timestamp': string; 'x-signature': string } {\n const timestamp = Math.floor(Date.now() / 1000);\n const canonical = buildRequestCanonical({ ...input, timestamp });\n const key = createPrivateKey({ key: Buffer.from(privateKeyBase64, 'base64'), format: 'der', type: 'pkcs8' });\n const signature = sign(null, Buffer.from(canonical, 'utf8'), key).toString('base64');\n return { 'x-timestamp': String(timestamp), 'x-signature': signature };\n}\n\n// Verifies a signed request (signature + timestamp skew) against an Ed25519 public key; malformed input ⇒ false\nexport function verifySignedRequest(input: VerifySignedRequestInput): boolean {\n try {\n const timestamp = Number(input.timestamp);\n if (!Number.isFinite(timestamp)) return false;\n const maxSkew = input.maxSkewSeconds ?? 300;\n if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > maxSkew) return false;\n const canonical = buildRequestCanonical({\n method: input.method,\n path: input.path,\n orgId: input.orgId,\n query: input.query,\n partyId: input.partyId,\n workspaceHeaders: input.workspaceHeaders,\n body: input.rawBody,\n timestamp,\n });\n const key = createPublicKey({ key: Buffer.from(input.publicKey, 'base64'), format: 'der', type: 'spki' });\n return verify(null, Buffer.from(canonical, 'utf8'), key, Buffer.from(input.signature, 'base64'));\n } catch {\n return false;\n }\n}\n","/**\n * The shared contract between this guard and a server that authenticates apps.\n *\n * The guard recognises `@RequireApp()` and enforces the type filter; the server\n * resolves the credential in `guard.onAuthenticated`, because only it has a\n * database. These are the values both sides have to agree on.\n */\n\n/** The client id an app sends alongside the standard signature headers. */\nexport const CLIENT_ID_HEADER = 'x-vritti-client-id';\n\n/** The party (person) a signed request is acting for, when there is one. */\nexport const PARTY_ID_HEADER = 'x-party-id';\n\n// Re-exported from the signing module, which owns it because the canonical is what\n// fixes the order. Kept visible here so an auth-side consumer has one import.\nexport { WORKSPACE_HEADER_ORDER } from '../signing/request';\n\n/**\n * How far a signed request's timestamp may drift before it is refused.\n *\n * Five minutes, matching the deployment-signing path. Wide enough for ordinary\n * clock drift between two servers; a captured request is replayable inside the\n * window, which is why app operations should stay idempotent.\n */\nexport const MAX_CLOCK_SKEW_SECONDS = 300;\n\n","import type { FastifyRequest } from 'fastify';\nimport type { RequestService } from '../request/services/request.service';\n\nexport const AUTH_CONFIG = Symbol('AUTH_CONFIG');\n\n// Called once the guard has decided which kind of caller this is, with the partially-built\n// auth object to fill in. The consuming server is the only side with a database, so tenant\n// resolution, signature verification against a stored key, and workspace context all land here.\n//\n// `auth.kind` is already set and is what the server switches on. Everything else the server\n// knows how to resolve — organization, subdomain, app identity — it writes onto `auth`.\nexport type OnAuthenticatedCallback = (\n requestService: RequestService,\n auth: NonNullable<FastifyRequest['auth']>,\n) => void | Promise<void>;\n\nexport type TokenExpiryString = `${number}${'s' | 'm' | 'h' | 'd' | 'w' | 'y'}`;\n\nexport interface TokenExpiry {\n access: TokenExpiryString;\n refresh: TokenExpiryString;\n}\n\nexport interface CookieConfig {\n refreshCookieName: string;\n refreshCookieMaxAge: number;\n refreshCookiePath: string;\n refreshCookieSecure: boolean;\n refreshCookieSameSite: 'strict' | 'lax' | 'none';\n refreshCookieDomain?: string;\n}\n\nexport interface GuardConfig {\n authHeaderName: string;\n tokenPrefix: string;\n csrfExemptSessionTypes?: string[];\n csrfExemptTransports?: string[];\n refreshTokenBindingExemptSessionTypes?: string[];\n onAuthenticated?: OnAuthenticatedCallback;\n}\n\nexport interface AuthConfig {\n tokenExpiry: TokenExpiry;\n cookie: CookieConfig;\n guard: GuardConfig;\n}\n\nexport const AUTH_CONFIG_DEFAULTS = {\n cookie: {\n refreshCookieName: 'vritti_refresh',\n refreshCookieMaxAge: 30 * 24 * 60 * 60 * 1000, // 30 days\n refreshCookiePath: '/',\n refreshCookieSecure: process.env.NODE_ENV === 'production',\n refreshCookieSameSite: 'strict' as const,\n refreshCookieDomain: 'localhost',\n },\n guard: {\n authHeaderName: 'authorization',\n tokenPrefix: 'Bearer',\n csrfExemptSessionTypes: [],\n csrfExemptTransports: [],\n refreshTokenBindingExemptSessionTypes: [],\n },\n} satisfies Omit<AuthConfig, 'tokenExpiry'>;\n\nexport interface CookieSerializeOptions {\n httpOnly: boolean;\n secure: boolean;\n sameSite: 'strict' | 'lax' | 'none';\n path: string;\n maxAge: number;\n domain: string;\n}\n\nexport enum TokenType {\n ACCESS = 'access',\n REFRESH = 'refresh',\n}\n\ninterface JwtClaims {\n exp: number;\n iat: number;\n}\n\nexport interface AccessTokenPayload {\n sessionType: string;\n tokenType: TokenType.ACCESS;\n userId: string;\n sessionId: string;\n refreshTokenHash: string;\n}\n\nexport type DecodedAccessToken = AccessTokenPayload & JwtClaims;\n\nexport interface RefreshTokenPayload {\n sessionType: string;\n tokenType: TokenType.REFRESH;\n userId: string;\n sessionId: string;\n}\n\nexport type DecodedRefreshToken = RefreshTokenPayload & JwtClaims;\n","import { type DynamicModule, Global, type InjectionToken, Module, type ModuleMetadata } from '@nestjs/common';\nimport { ConfigModule, ConfigService } from '@nestjs/config';\nimport { APP_GUARD, Reflector } from '@nestjs/core';\nimport { JwtModule } from '@nestjs/jwt';\nimport { RequestModule } from '../request/request.module';\nimport {\n AUTH_CONFIG,\n AUTH_CONFIG_DEFAULTS,\n type AuthConfig,\n type CookieConfig,\n type GuardConfig,\n type TokenExpiry,\n} from './auth.config';\nimport { VrittiAuthGuard } from './guards/vritti-auth.guard';\nimport { TokenService } from './services/token.service';\n\n// Factory return type — tokenExpiry is required, cookie and guard are partial with defaults merged\ninterface AuthConfigInput {\n tokenExpiry: TokenExpiry;\n cookie?: Partial<CookieConfig>;\n guard?: Partial<GuardConfig>;\n}\n\ninterface AuthConfigModuleOptions<T extends unknown[] = unknown[]> {\n useFactory: (...args: [...T]) => AuthConfigInput | Promise<AuthConfigInput>;\n inject?: InjectionToken[];\n /**\n * Extra modules the factory's injected providers come from.\n *\n * Needed because `guard.onAuthenticated` is where a server resolves an app\n * credential, so the factory has to be able to inject the service that owns that\n * lookup. Mirrors `NatsClientModule.forRoot`.\n */\n imports?: ModuleMetadata['imports'];\n}\n\n// Merges user-provided partial config with defaults to produce a complete AuthConfig\nfunction mergeWithDefaults(input: AuthConfigInput): AuthConfig {\n return {\n tokenExpiry: input.tokenExpiry,\n cookie: {\n ...AUTH_CONFIG_DEFAULTS.cookie,\n ...(input.cookie ?? {}),\n },\n guard: {\n ...AUTH_CONFIG_DEFAULTS.guard,\n ...(input.guard ?? {}),\n },\n };\n}\n\n@Global()\n@Module({})\nexport class AuthConfigModule {\n // Registers JWT, TokenService, and global VrittiAuthGuard\n static forRootAsync<T extends unknown[] = unknown[]>(options: AuthConfigModuleOptions<T>): DynamicModule {\n return {\n module: AuthConfigModule,\n imports: [\n ConfigModule,\n RequestModule,\n ...(options.imports ?? []),\n JwtModule.registerAsync({\n imports: [ConfigModule],\n inject: [ConfigService],\n useFactory: (config: ConfigService) => ({\n secret: config.getOrThrow<string>('JWT_SECRET'),\n signOptions: { algorithm: 'HS256' as const },\n }),\n }),\n ],\n providers: [\n {\n provide: Reflector,\n useClass: Reflector,\n },\n {\n provide: APP_GUARD,\n useClass: VrittiAuthGuard,\n },\n {\n provide: AUTH_CONFIG,\n useFactory: async (...args: unknown[]) => {\n const input = await options.useFactory(...(args as [...T]));\n return mergeWithDefaults(input);\n },\n inject: options.inject || [],\n },\n TokenService,\n ],\n exports: [JwtModule, TokenService, AUTH_CONFIG],\n };\n }\n}\n","import { Global, Module } from '@nestjs/common';\nimport { RequestService } from './services/request.service';\n\n@Global()\n@Module({\n providers: [RequestService],\n exports: [RequestService],\n})\nexport class RequestModule {}\n","import { Inject, Injectable, Scope } from '@nestjs/common';\nimport { REQUEST } from '@nestjs/core';\nimport type { FastifyRequest } from 'fastify';\nimport { AUTH_CONFIG, type AuthConfig } from '../../auth/auth.config';\nimport { resolveInjectedRequest } from '../../context/resolve-request';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class RequestService {\n constructor(\n @Inject(REQUEST) private readonly injectedRequest: FastifyRequest,\n @Inject(AUTH_CONFIG) private readonly config: AuthConfig,\n ) {}\n\n // Unwraps the GraphQL { req, reply } context wrapper so every accessor below works across both transports\n private get request(): FastifyRequest {\n return resolveInjectedRequest(this.injectedRequest);\n }\n\n // Extracts the bearer access token from the Authorization header\n getAccessToken(): string | null {\n const authHeader = this.request.headers?.[this.config.guard.authHeaderName];\n if (!authHeader || typeof authHeader !== 'string') {\n return null;\n }\n const [type, token] = authHeader.split(' ') ?? [];\n return type === this.config.guard.tokenPrefix && token ? token : null;\n }\n\n // Extracts the refresh token from the configured httpOnly cookie\n getRefreshToken(): string | null {\n try {\n const cookies = (this.request as unknown as { cookies?: Record<string, string> }).cookies;\n if (cookies && typeof cookies === 'object') {\n const refreshToken = cookies[this.config.cookie.refreshCookieName];\n if (refreshToken) {\n return refreshToken;\n }\n }\n return null;\n } catch (_error: unknown) {\n return null;\n }\n }\n\n // Returns the value of a specific request header by key\n getHeader(key: string): string | string[] | undefined {\n return this.request.headers?.[key];\n }\n\n // Returns the request hostname (without port)\n getHostname(): string {\n return this.request.hostname ?? '';\n }\n\n // Returns all request headers\n getAllHeaders(): FastifyRequest['headers'] {\n return this.request.headers || {};\n }\n\n // Returns the HTTP method\n getMethod(): string {\n return this.request.method ?? '';\n }\n\n /**\n * Returns the path with any query string stripped.\n *\n * Signature canonicals cover the path only, so a query string must not be part\n * of what gets signed or verified.\n */\n getPath(): string {\n const url = this.request.url ?? '';\n return url.split('?')[0] ?? url;\n }\n\n /**\n * Returns the raw query string, without the leading `?`.\n *\n * Companion to `getPath()`, which strips it. Signed separately so a REST request's\n * filters are covered — `getPath()` alone would sign `GET /people?search=salt` as\n * though the filter were not there.\n */\n getQuery(): string {\n const url = this.request.url ?? '';\n const index = url.indexOf('?');\n return index === -1 ? '' : url.slice(index + 1);\n }\n\n /**\n * Returns a single decoded query param, or undefined when absent.\n *\n * Reads the raw URL rather than Fastify's parsed `query` so it is usable from the\n * guard, which runs before a route's own parsing is meaningful.\n */\n getQueryParam(key: string): string | undefined {\n return new URLSearchParams(this.getQuery()).get(key) ?? undefined;\n }\n\n /**\n * Returns the raw request body, as `fastify-raw-body` leaves it.\n *\n * Read structurally rather than through a module augmentation: that plugin is the\n * consuming server's dependency, not this SDK's, and declaring `rawBody` here\n * would collide with the plugin's own declaration the moment the two drift.\n *\n * A server that has not registered it yields `undefined`, which hashes as an empty\n * body and therefore fails any signature made over real bytes — refused, never\n * waved through.\n */\n getRawBody(): string | Buffer | undefined {\n return (this.request as unknown as { rawBody?: string | Buffer }).rawBody;\n }\n}\n","import type { FastifyRequest } from 'fastify';\n\n// Unwraps the @Inject(REQUEST) value to the real Fastify request (GraphQL injects a { req, reply } context).\nexport function resolveInjectedRequest(injected: FastifyRequest): FastifyRequest {\n const candidate = injected as unknown as { headers?: unknown; req?: FastifyRequest };\n if (candidate && candidate.headers === undefined && candidate.req) {\n return candidate.req;\n }\n return injected;\n}\n","import {\n type CanActivate,\n type ExecutionContext,\n ForbiddenException,\n Inject,\n Injectable,\n Logger,\n Scope,\n UnauthorizedException,\n} from '@nestjs/common';\nimport { SSE_METADATA } from '@nestjs/common/constants';\nimport { Reflector } from '@nestjs/core';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport '../../types/fastify-augmentation';\nimport { getRequestFromContext, getResponseFromContext } from '../../context';\nimport { RequestService } from '../../request/services/request.service';\nimport { AUTH_CONFIG, type AuthConfig } from '../auth.config';\nimport { type AuthRequirement, AuthType, REQUIRE_AUTH_KEY } from '../decorators/require.decorator';\nimport { SKIP_CSRF_KEY } from '../decorators/skip-csrf.decorator';\nimport { TokenService } from '../services/token.service';\n\ninterface FastifyInstanceWithCsrf {\n csrfProtection?: (req: FastifyRequest, reply: FastifyReply, next: (err?: Error) => void) => void;\n}\n\ntype PatchableReply = { send: (...args: unknown[]) => unknown };\n\n@Injectable({ scope: Scope.REQUEST })\nexport class VrittiAuthGuard implements CanActivate {\n private readonly logger = new Logger(VrittiAuthGuard.name);\n\n constructor(\n private readonly reflector: Reflector,\n private readonly requestService: RequestService,\n private readonly tokenService: TokenService,\n @Inject(AUTH_CONFIG) private readonly config: AuthConfig,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = getRequestFromContext(context);\n const reply = getResponseFromContext(context);\n const route = `${request.method} ${request.url}`;\n\n // Attach auth config to request so decorators can access it without injection\n request.authConfig = this.config;\n\n // CSRF is skipped via @SkipCsrf() or when the request transport is CSRF-exempt (e.g. 'graphql')\n const csrfExemptTransports = this.config.guard.csrfExemptTransports ?? [];\n const skipCsrf =\n this.reflector.getAllAndOverride<boolean>(SKIP_CSRF_KEY, [context.getHandler(), context.getClass()]) ||\n csrfExemptTransports.includes(context.getType<string>());\n\n // A route with no @Require() is a session route restricted to nothing — the historical\n // default, kept so an undecorated endpoint stays authenticated rather than falling open.\n const requirement = this.reflector.getAllAndOverride<AuthRequirement>(REQUIRE_AUTH_KEY, [\n context.getHandler(),\n context.getClass(),\n ]) ?? { type: AuthType.Session, subtypes: [] };\n\n switch (requirement.type) {\n // Signed server-to-server calls return before CSRF is ever reached. Neither carries a\n // cookie for CSRF to protect, and that holds on REST too — which a transport-level\n // exemption would not cover.\n case AuthType.App:\n return this.handleAppAuth(request, requirement.subtypes, route);\n\n case AuthType.Cloud:\n return this.handleCloudAuth(request, route);\n\n case AuthType.Public: {\n if (!skipCsrf) {\n await this.validateCsrf(request, reply);\n }\n this.logger.debug(`${route} — public endpoint, skipping auth`);\n return true;\n }\n\n case AuthType.Session: {\n // SSE authenticates via the refresh cookie — EventSource cannot send Authorization headers\n const isSseEndpoint = this.reflector.get<boolean>(SSE_METADATA, context.getHandler());\n if (isSseEndpoint) {\n this.logger.debug(`${route} — SSE endpoint, authenticating via refresh cookie`);\n return this.handleSseAuth(request, requirement.subtypes);\n }\n\n const sessionType = await this.handleHttpAuth(request, requirement.subtypes);\n\n const csrfExemptSessionTypes = this.config.guard.csrfExemptSessionTypes ?? [];\n if (!skipCsrf && !csrfExemptSessionTypes.includes(sessionType)) {\n await this.validateCsrf(request, reply);\n }\n\n return true;\n }\n }\n }\n\n /**\n * Authenticates a signed request from the control plane.\n *\n * Same shape as the app branch and for the same reason: verifying the signature needs a key\n * the consuming server holds, so this delegates to `guard.onAuthenticated` and keeps only\n * what this side can do — recognising the decorator and seeding the context.\n *\n * There is no subtype filter. Cloud is one caller, not a family of them.\n */\n private async handleCloudAuth(request: FastifyRequest, route: string): Promise<boolean> {\n const onAuthenticated = this.config.guard.onAuthenticated;\n if (!onAuthenticated) {\n // Fail closed. Without the hook nothing verifies the signature, and treating the\n // request as authenticated would leave the endpoint open to anyone who found it.\n this.logger.error(`${route} — @Require(AuthType.Cloud) requires guard.onAuthenticated to be configured`);\n throw new UnauthorizedException('Cloud authentication is not configured');\n }\n\n const auth = { kind: 'cloud' } as NonNullable<FastifyRequest['auth']>;\n request.auth = auth;\n\n await onAuthenticated(this.requestService, auth);\n\n this.logger.debug(`${route} — authenticated cloud request`);\n return true;\n }\n\n /**\n * Authenticates a signed request from an external app.\n *\n * The credential lookup and the signature check are the consuming server's job —\n * it is the only side with a database — so this delegates to\n * `guard.onAuthenticated`, the same hook the session path already uses to resolve\n * organization and workspace context. That server fills in `organizationId`,\n * `appType` and whatever else it knows.\n *\n * What stays here is the part only this side can do: reading the decorator's\n * metadata and enforcing the app-type filter against what the server resolved.\n *\n * An empty list means \"any type\" — the caller is still authenticated. That makes\n * `@RequireApp()` with no arguments mean what it reads like.\n */\n private async handleAppAuth(request: FastifyRequest, requiredAppTypes: string[], route: string): Promise<boolean> {\n const onAuthenticated = this.config.guard.onAuthenticated;\n if (!onAuthenticated) {\n // Fail closed. Without the hook nothing can verify the signature, and\n // treating the request as authenticated would leave the endpoint wide open.\n this.logger.error(`${route} — @RequireApp() requires guard.onAuthenticated to be configured`);\n throw new UnauthorizedException('App authentication is not configured');\n }\n\n // Seeded so the hook has something to populate, matching how the session path\n // hands it an object to mutate.\n const auth = { kind: 'app' } as NonNullable<FastifyRequest['auth']>;\n request.auth = auth;\n\n await onAuthenticated(this.requestService, auth);\n\n const appType = auth.kind === 'app' ? auth.appType : undefined;\n if (requiredAppTypes.length && !requiredAppTypes.includes(appType ?? '')) {\n this.logger.warn(`${route} — app type ${appType ?? 'unknown'} not in allowed: [${requiredAppTypes.join(', ')}]`);\n // Deliberately the same error the server raises for an unknown client, a\n // revoked one or a bad signature. Distinguishing them would tell whoever is\n // probing which of the four they hit.\n throw new UnauthorizedException('This client is not recognised.');\n }\n\n // No organization in this message on purpose: which field holds the tenant is\n // the consuming server's augmentation, not something this side knows about.\n this.logger.debug(`${route} — authenticated app (${appType})`);\n return true;\n }\n\n // Authenticates standard HTTP requests using the access token from Authorization header\n private async handleHttpAuth(request: FastifyRequest, requiredSessionTypes?: string[]): Promise<string> {\n const route = `${request.method} ${request.url}`;\n\n const accessToken = this.requestService.getAccessToken();\n if (!accessToken) {\n this.logger.warn(`${route} — no access token found`);\n throw new UnauthorizedException('Access token not found');\n }\n\n const decoded = this.tokenService.validateAccessToken(accessToken);\n\n const refreshTokenBindingExemptSessionTypes = this.config.guard.refreshTokenBindingExemptSessionTypes ?? [];\n\n if (!refreshTokenBindingExemptSessionTypes.includes(decoded.sessionType)) {\n const refreshToken = this.requestService.getRefreshToken();\n if (!refreshToken) {\n throw new UnauthorizedException('Session validation failed');\n }\n this.tokenService.validateTokenBinding(decoded, refreshToken);\n }\n\n // Validate session type access (only if @RequireSession specifies types)\n if (requiredSessionTypes?.length && !requiredSessionTypes.includes(decoded.sessionType)) {\n this.logger.warn(\n `${route} — session type ${decoded.sessionType} not in allowed: [${requiredSessionTypes.join(', ')}]`,\n );\n throw new UnauthorizedException(`${decoded.sessionType} sessions cannot access this endpoint`);\n }\n\n // Attach auth to request — spread full decoded token (includes metadata fields)\n const { tokenType: _tokenType, refreshTokenHash: _hash, exp: _exp, iat: _iat, ...claims } = decoded;\n const auth = { kind: 'session', ...claims } as NonNullable<FastifyRequest['auth']>;\n request.auth = auth;\n\n // Call onAuthenticated callback if configured\n const onAuthenticated = this.config.guard.onAuthenticated;\n if (onAuthenticated) {\n await onAuthenticated(this.requestService, auth);\n }\n\n this.logger.debug(`${route} — authenticated user: ${decoded.userId} (${decoded.sessionType})`);\n return decoded.sessionType;\n }\n\n // Authenticates SSE connections using the refresh token httpOnly cookie\n private async handleSseAuth(request: FastifyRequest, requiredSessionTypes?: string[]): Promise<boolean> {\n const refreshToken = this.requestService.getRefreshToken();\n if (!refreshToken) {\n this.logger.warn(`SSE ${request.url} — no refresh token cookie`);\n throw new UnauthorizedException('Authentication required');\n }\n\n const decoded = this.tokenService.validateRefreshToken(refreshToken);\n\n if (requiredSessionTypes?.length && !requiredSessionTypes.includes(decoded.sessionType)) {\n this.logger.warn(`SSE ${request.url} — session type ${decoded.sessionType} not allowed`);\n throw new UnauthorizedException(`${decoded.sessionType} sessions cannot access this endpoint`);\n }\n\n const { tokenType: _tokenType, exp: _exp, iat: _iat, ...claims } = decoded;\n const auth = { kind: 'session', ...claims } as NonNullable<FastifyRequest['auth']>;\n request.auth = auth;\n\n // Same authorization hook the HTTP path runs. SSE carries its tenant context in the query string\n // (EventSource cannot set headers), and that is no more trustworthy than a header — so it has to be\n // checked against the session here too, or every @Sse() route silently skips the consuming server's\n // scoping. The refresh token carries the same session metadata the access token does, so the hook has\n // everything it needs.\n const onAuthenticated = this.config.guard.onAuthenticated;\n if (onAuthenticated) {\n await onAuthenticated(this.requestService, auth);\n }\n\n this.logger.debug(`SSE ${request.url} — authenticated user: ${decoded.userId} (${decoded.sessionType})`);\n return true;\n }\n\n // Validates CSRF token for state-changing requests\n private async validateCsrf(request: FastifyRequest, reply: FastifyReply): Promise<void> {\n const safeMethods = ['GET', 'HEAD', 'OPTIONS'];\n if (safeMethods.includes(request.method)) return;\n\n try {\n const fastifyInstance = request.server as unknown as FastifyInstanceWithCsrf;\n const csrfProtection = fastifyInstance.csrfProtection;\n if (!csrfProtection) {\n throw new ForbiddenException('CSRF protection not configured');\n }\n\n await new Promise<void>((resolve, reject) => {\n const originalSend = reply.send.bind(reply);\n (reply as PatchableReply).send = () => {\n (reply as PatchableReply).send = originalSend as PatchableReply['send'];\n reject(new Error('CSRF validation failed'));\n return reply;\n };\n\n csrfProtection(request, reply, (err?: Error) => {\n (reply as PatchableReply).send = originalSend as PatchableReply['send'];\n if (err) reject(err);\n else resolve();\n });\n });\n } catch (_error: unknown) {\n this.logger.warn(`${request.method} ${request.url} — CSRF validation failed`);\n throw new ForbiddenException({\n errors: [{ field: 'csrf', message: 'Invalid or missing CSRF token' }],\n message: 'CSRF validation failed',\n });\n }\n }\n}\n","import type { ArgumentsHost } from '@nestjs/common';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { RequestExtractor } from '../context.types';\n\ninterface GqlContext {\n req: FastifyRequest;\n reply?: FastifyReply;\n}\n\nexport const graphqlExtractor: RequestExtractor = {\n getRequest: (host: ArgumentsHost) => host.getArgByIndex<GqlContext>(2).req,\n getResponse: (host: ArgumentsHost) => host.getArgByIndex<GqlContext>(2).reply as FastifyReply,\n};\n","import type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { RequestExtractor } from '../context.types';\n\n// Default transport: standard HTTP via the Fastify adapter.\nexport const httpExtractor: RequestExtractor = {\n getRequest: (host) => host.switchToHttp().getRequest<FastifyRequest>(),\n getResponse: (host) => host.switchToHttp().getResponse<FastifyReply>(),\n};\n","import type { RequestExtractor, TransportType } from './context.types';\nimport { graphqlExtractor } from './extractors/graphql.extractor';\nimport { httpExtractor } from './extractors/http.extractor';\n\nconst registry = new Map<TransportType, RequestExtractor>([\n ['http', httpExtractor],\n ['graphql', graphqlExtractor],\n]);\n\n// Registers (or overrides) the extractor for a transport type without modifying the SDK\nexport function registerTransport(type: TransportType, extractor: RequestExtractor): void {\n registry.set(type, extractor);\n}\n\n// Resolves the extractor for the host's reported transport, falling back to HTTP.\nexport function resolveExtractor(type: string): RequestExtractor {\n return registry.get(type as TransportType) ?? httpExtractor;\n}\n","import type { ArgumentsHost } from '@nestjs/common';\nimport type { FastifyRequest } from 'fastify';\nimport { resolveExtractor } from './context.registry';\n\n// Returns the underlying Fastify request for any registered transport, via the transport registry.\nexport function getRequestFromContext(host: ArgumentsHost): FastifyRequest {\n return resolveExtractor(host.getType()).getRequest(host);\n}\n","import type { ArgumentsHost } from '@nestjs/common';\nimport type { FastifyReply } from 'fastify';\nimport { resolveExtractor } from './context.registry';\n\n// Returns the underlying Fastify reply for any registered transport (HTTP, GraphQL, ...).\nexport function getResponseFromContext(host: ArgumentsHost): FastifyReply {\n return resolveExtractor(host.getType()).getResponse(host);\n}\n","import { SetMetadata } from '@nestjs/common';\n\nexport const REQUIRE_AUTH_KEY = 'requireAuth';\n\nexport enum AuthType {\n Session = 'session',\n App = 'app',\n Cloud = 'cloud',\n Public = 'public',\n}\n\nexport interface AuthRequirement {\n type: AuthType;\n subtypes: string[];\n}\n\n// Declares how a route authenticates, and which subtypes of that caller may reach it.\n//\n// One decorator for every caller kind, so the branch the guard takes is stated at the call\n// site rather than inferred from which of three decorators happens to be present:\n//\n// @Require(AuthType.Session, SessionTypeValues.WEB) session types WEB\n// @Require(AuthType.App, AppTypeValues.GRAPHQL) app credentials of type GRAPHQL\n// @Require(AuthType.Cloud) signed control-plane calls\n// @Require(AuthType.Public) no authentication\n//\n// Subtypes are compared as strings — the enums belong to the consuming server's schema and\n// this only ever compares. Passing none means \"any subtype\", so the caller is still\n// authenticated but unrestricted.\nexport const Require = (type: AuthType, ...subtypes: string[]) =>\n SetMetadata<string, AuthRequirement>(REQUIRE_AUTH_KEY, { type, subtypes });\n","import { SetMetadata } from '@nestjs/common';\n\nexport const SKIP_CSRF_KEY = 'skipCsrf';\n\nexport const SkipCsrf = () => SetMetadata(SKIP_CSRF_KEY, true);\n","import { Inject, Injectable, Logger, UnauthorizedException } from '@nestjs/common';\nimport { JwtService, type JwtSignOptions } from '@nestjs/jwt';\nimport type { VrittiSessionAuth } from 'fastify';\nimport '../../types/fastify-augmentation';\nimport { parseExpiryToMs } from '../../utils/time.utils';\nimport {\n AUTH_CONFIG,\n type AuthConfig,\n type DecodedAccessToken,\n type DecodedRefreshToken,\n TokenType,\n} from '../auth.config';\nimport { hashToken, verifyTokenHash } from '../utils/token-hash.util';\n\nexport type { DecodedAccessToken, DecodedRefreshToken };\n\n// The claims a session token carries. Derived from the request-side session auth minus its\n// discriminator — the guard re-adds `kind` when it decodes, so storing it would be redundant.\ntype SessionInfo = Omit<VrittiSessionAuth, 'kind'>;\n\ninterface TokenError extends Error {\n name: 'TokenExpiredError' | 'JsonWebTokenError' | 'NotBeforeError';\n}\n\n// Handles all token operations — generation, validation, and binding verification\n@Injectable()\nexport class TokenService {\n private readonly logger = new Logger(TokenService.name);\n\n constructor(\n private readonly jwtService: JwtService,\n @Inject(AUTH_CONFIG) private readonly config: AuthConfig,\n ) {}\n\n // ---- Generation ----\n\n // Generates an access token bound to the given refresh token\n generateAccessToken(sessionInfo: SessionInfo, refreshToken: string): string {\n const { userId, sessionId, sessionType, ...metadata } = sessionInfo;\n return this.jwtService.sign(\n {\n sessionType,\n tokenType: TokenType.ACCESS,\n userId,\n sessionId,\n refreshTokenHash: hashToken(refreshToken),\n ...metadata,\n },\n { expiresIn: this.config.tokenExpiry.access },\n );\n }\n\n // Generates a refresh token for session persistence\n generateRefreshToken(sessionInfo: SessionInfo): string {\n const { userId, sessionId, sessionType, ...metadata } = sessionInfo;\n return this.jwtService.sign(\n { sessionType, tokenType: TokenType.REFRESH, userId, sessionId, ...metadata },\n { expiresIn: this.config.tokenExpiry.refresh },\n );\n }\n\n // Signs an arbitrary payload with optional JWT options\n sign(payload: object, options?: JwtSignOptions): string {\n return this.jwtService.sign(payload, options);\n }\n\n // Verifies a token and ensures it matches the expected token type\n verify(\n token: string,\n expectedType: TokenType,\n ): { userId: string; sessionId: string; sessionType: string; tokenType: TokenType } {\n try {\n const payload = this.jwtService.verify(token);\n\n if (payload.tokenType !== expectedType) {\n throw new Error(`Expected ${expectedType} token, got ${payload.tokenType}`);\n }\n\n return payload;\n } catch (error) {\n this.logger.error(`Failed to verify ${expectedType} token`, error);\n throw error;\n }\n }\n\n // Returns the expiry as a Date for the given token type\n getExpiryTime(type: TokenType): Date {\n return new Date(Date.now() + parseExpiryToMs(this.config.tokenExpiry[type]));\n }\n\n // Returns the token lifetime in seconds for the given type\n getExpiryInSeconds(type: TokenType): number {\n return Math.floor(parseExpiryToMs(this.config.tokenExpiry[type]) / 1000);\n }\n\n // ---- Validation ----\n\n // Decodes and validates an access token JWT\n validateAccessToken(token: string): DecodedAccessToken {\n try {\n const decoded = this.jwtService.verify<DecodedAccessToken>(token);\n\n if (decoded.tokenType !== TokenType.ACCESS) {\n throw new UnauthorizedException('Invalid token type');\n }\n\n return decoded;\n } catch (error) {\n if (error instanceof UnauthorizedException) throw error;\n\n const jwtError = error as TokenError;\n switch (jwtError.name) {\n case 'TokenExpiredError':\n throw new UnauthorizedException('Access token has expired');\n case 'JsonWebTokenError':\n throw new UnauthorizedException('Invalid access token');\n case 'NotBeforeError':\n throw new UnauthorizedException('Access token not yet valid');\n default:\n throw new UnauthorizedException('Access token validation failed');\n }\n }\n }\n\n // Decodes and validates a refresh token JWT\n validateRefreshToken(token: string): DecodedRefreshToken {\n try {\n const decoded = this.jwtService.verify<DecodedRefreshToken>(token);\n\n if (decoded.tokenType !== TokenType.REFRESH) {\n throw new UnauthorizedException('Invalid token type');\n }\n\n return decoded;\n } catch (error) {\n if (error instanceof UnauthorizedException) throw error;\n throw new UnauthorizedException('Invalid or expired session');\n }\n }\n\n // Validates that the access token is bound to the refresh token\n validateTokenBinding(accessToken: DecodedAccessToken, refreshToken: string): void {\n if (!verifyTokenHash(refreshToken, accessToken.refreshTokenHash)) {\n throw new UnauthorizedException('Session validation failed');\n }\n }\n}\n","// Parses a duration string (e.g. '10m', '1h', '30s', '7d') to milliseconds\nexport function parseExpiryToMs(expiry: string): number {\n const match = expiry.match(/^(\\d+)([smhdwy])$/);\n if (!match) throw new Error(`Invalid expiry format: ${expiry}`);\n\n const [, digits = '', unit = ''] = match;\n const value = Number.parseInt(digits, 10);\n const multipliers: Record<string, number> = {\n s: 1000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n w: 604_800_000,\n y: 31_536_000_000,\n };\n\n const multiplier = multipliers[unit];\n if (multiplier === undefined) throw new Error(`Invalid expiry format: ${expiry}`);\n\n return value * multiplier;\n}\n","import * as crypto from 'node:crypto';\n\n// Hashes a token using SHA-256\nexport function hashToken(token: string): string {\n return crypto.createHash('sha256').update(token).digest('hex');\n}\n\n// Verifies a token against its stored hash\nexport function verifyTokenHash(token: string, expectedHash: string): boolean {\n const computedHash = hashToken(token);\n if (computedHash.length !== expectedHash.length) return false;\n return crypto.timingSafeEqual(Buffer.from(computedHash, 'hex'), Buffer.from(expectedHash, 'hex'));\n}\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the bearer token from the Authorization header\nexport const AccessToken = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n const authHeader = request.headers.authorization;\n return authHeader?.replace('Bearer ', '') || '';\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the client IP from the request, working across HTTP and GraphQL transports\nexport const ClientIp = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n return getRequestFromContext(ctx).ip;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { AUTH_CONFIG_DEFAULTS } from '../auth.config';\n\n// Extracts the cookie domain from x-forwarded-host (injected by proxy), validated against baseDomain, falls back to baseDomain if invalid\nexport const CookieDomain = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n const domain = hostStr.split(':')[0] ?? hostStr;\n const baseDomain =\n request.authConfig?.cookie.refreshCookieDomain ?? AUTH_CONFIG_DEFAULTS.cookie.refreshCookieDomain ?? '';\n return domain.endsWith(`.${baseDomain}`) ? domain : baseDomain;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { AUTH_CONFIG_DEFAULTS } from '../auth.config';\n\n// Returns the configured refresh cookie name from request.authConfig\nexport const CookieName = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n return request.authConfig?.cookie.refreshCookieName ?? AUTH_CONFIG_DEFAULTS.cookie.refreshCookieName;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the request hostname from x-forwarded-host (set by reverse proxies and dev proxy) with fallback to request.hostname\nexport const Hostname = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n return hostStr.split(':')[0] ?? hostStr;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { UnauthorizedException } from '../../exceptions';\nimport { AUTH_CONFIG_DEFAULTS, type CookieConfig, type CookieSerializeOptions } from '../auth.config';\n\n// Builds cookie serialize options from the given cookie config\nfunction buildCookieOptionsForHost(cookieConfig: CookieConfig, hostname: string): CookieSerializeOptions {\n const baseDomain = cookieConfig.refreshCookieDomain;\n\n if (!baseDomain) {\n throw new Error('refreshCookieDomain must be configured before using @RefreshCookieOptions()');\n }\n\n if (!hostname.endsWith(`.${baseDomain}`)) {\n throw new UnauthorizedException('Invalid request host.');\n }\n\n return {\n httpOnly: true,\n secure: cookieConfig.refreshCookieSecure,\n sameSite: cookieConfig.refreshCookieSameSite,\n path: cookieConfig.refreshCookiePath,\n maxAge: cookieConfig.refreshCookieMaxAge,\n domain: hostname,\n };\n}\n\n// Returns refresh cookie options with domain scoped to the request subdomain (reads x-forwarded-host injected by proxy)\nexport const RefreshCookieOptions = createParamDecorator(\n (_data: unknown, ctx: ExecutionContext): CookieSerializeOptions => {\n const request = getRequestFromContext(ctx);\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n const domain = hostStr.split(':')[0] ?? hostStr;\n const cookieConfig: CookieConfig = request.authConfig?.cookie ?? AUTH_CONFIG_DEFAULTS.cookie;\n return buildCookieOptionsForHost(cookieConfig, domain);\n },\n);\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadGatewayException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Gateway', HttpStatus.BAD_GATEWAY);\n }\n}\n","import { HttpException, HttpStatus } from '@nestjs/common';\nimport type { FieldError } from '../types/error-response.types';\n\n// Re-export FieldError for backwards compatibility\nexport type { FieldError } from '../types/error-response.types';\n\nexport interface ProblemOptions {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\nexport abstract class HttpProblemException extends HttpException {\n constructor(detailOrOptions: string | ProblemOptions, httpStatus: HttpStatus) {\n const options = typeof detailOrOptions === 'string' ? { detail: detailOrOptions } : detailOrOptions;\n\n super(\n {\n type: options.type ?? 'about:blank',\n label: options.label,\n detail: options.detail,\n errors: options.errors ?? [],\n },\n httpStatus,\n );\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadRequestException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Request', HttpStatus.BAD_REQUEST);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ConflictException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Conflict', HttpStatus.CONFLICT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ForbiddenException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Forbidden', HttpStatus.FORBIDDEN);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class GoneException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Gone', HttpStatus.GONE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class InternalServerErrorException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Internal Server Error', HttpStatus.INTERNAL_SERVER_ERROR);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class MethodNotAllowedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Method Not Allowed', HttpStatus.METHOD_NOT_ALLOWED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotAcceptableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Acceptable', HttpStatus.NOT_ACCEPTABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotFoundException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Found', HttpStatus.NOT_FOUND);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotImplementedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Implemented', HttpStatus.NOT_IMPLEMENTED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class PayloadTooLargeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Payload Too Large', HttpStatus.PAYLOAD_TOO_LARGE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class RequestTimeoutException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Request Timeout', HttpStatus.REQUEST_TIMEOUT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ServiceUnavailableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Service Unavailable', HttpStatus.SERVICE_UNAVAILABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class TooManyRequestsException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Too Many Requests', HttpStatus.TOO_MANY_REQUESTS);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnauthorizedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unauthorized', HttpStatus.UNAUTHORIZED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnprocessableEntityException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unprocessable Entity', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnsupportedMediaTypeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unsupported Media Type', HttpStatus.UNSUPPORTED_MEDIA_TYPE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ValidationException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Validation Failed', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { AUTH_CONFIG_DEFAULTS } from '../auth.config';\n\nexport const RefreshTokenCookie = createParamDecorator((_data: unknown, ctx: ExecutionContext): string | undefined => {\n const request = getRequestFromContext(ctx);\n const cookies = request.cookies ?? {};\n const cookieName = request.authConfig?.cookie.refreshCookieName ?? AUTH_CONFIG_DEFAULTS.cookie.refreshCookieName;\n return cookies[cookieName] as string | undefined;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport '../../types/fastify-augmentation';\n\nexport interface SessionInfo {\n userId: string;\n sessionId: string;\n sessionType: string;\n}\n\n// Returns the session's own claims from request.auth (set by VrittiAuthGuard). Session-only,\n// for the same reason as @UserId() — an app or cloud request has no session to describe.\nexport const SessionData = createParamDecorator((_data: unknown, ctx: ExecutionContext): SessionInfo => {\n const auth = getRequestFromContext(ctx).auth;\n\n if (auth?.kind !== 'session') {\n throw new Error(`No session on this request (auth: ${auth?.kind ?? 'none'}). @SessionData() is session-only.`);\n }\n\n return {\n userId: auth.userId,\n sessionId: auth.sessionId,\n sessionType: auth.sessionType,\n };\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the subdomain from the request host, resolved the same way as @Hostname() — x-forwarded-host\n// (set by dev and reverse proxies) with a fallback to request.hostname. Origin is deliberately not used:\n// it is client-controlled and can disagree with the Host header, which would mint a session for one\n// subdomain that the auth guard's host check then rejects against another.\nexport const Subdomain = createParamDecorator((_data: unknown, ctx: ExecutionContext): string | undefined => {\n const request = getRequestFromContext(ctx);\n\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n if (!hostStr) return undefined;\n\n const host = hostStr.split(':')[0] ?? hostStr;\n return host.split('.')[0] || undefined;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the User-Agent header from the request, working across HTTP and GraphQL transports\nexport const UserAgent = createParamDecorator((_data: unknown, ctx: ExecutionContext): string | undefined => {\n const userAgent = getRequestFromContext(ctx).headers['user-agent'];\n return Array.isArray(userAgent) ? userAgent[0] : userAgent;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport '../../types/fastify-augmentation';\n\n// Extracts userId from request.auth (set by VrittiAuthGuard).\n//\n// Only a session has one. An app request acts for a credential and a cloud request for the\n// control plane, so asking for a user on either is a mistake at the call site — it throws\n// rather than returning a stand-in that would be recorded as if a person had acted.\nexport const UserId = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const auth = getRequestFromContext(ctx).auth;\n\n if (auth?.kind !== 'session') {\n throw new Error(`No user on this request (auth: ${auth?.kind ?? 'none'}). @UserId() is session-only.`);\n }\n\n return auth.userId;\n});\n"],"mappings":"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","names":["WORKSPACE_HEADER_ORDER","CLIENT_ID_HEADER","PARTY_ID_HEADER","MAX_CLOCK_SKEW_SECONDS","AUTH_CONFIG","Symbol","AUTH_CONFIG_DEFAULTS","cookie","refreshCookieName","refreshCookieMaxAge","refreshCookiePath","refreshCookieSecure","process","env","NODE_ENV","refreshCookieSameSite","refreshCookieDomain","guard","authHeaderName","tokenPrefix","csrfExemptSessionTypes","csrfExemptTransports","refreshTokenBindingExemptSessionTypes","TokenType","import_common","import_core","import_jwt","import_common","resolveInjectedRequest","injected","candidate","headers","undefined","req","RequestService","injectedRequest","config","request","resolveInjectedRequest","getAccessToken","authHeader","headers","guard","authHeaderName","type","token","split","tokenPrefix","getRefreshToken","cookies","refreshToken","cookie","refreshCookieName","_error","getHeader","key","getHostname","hostname","getAllHeaders","getMethod","method","getPath","url","getQuery","index","indexOf","slice","getQueryParam","URLSearchParams","get","undefined","getRawBody","rawBody","scope","Scope","REQUEST","RequestModule","providers","RequestService","exports","import_common","import_core","graphqlExtractor","getRequest","host","getArgByIndex","req","getResponse","reply","httpExtractor","getRequest","host","switchToHttp","getResponse","registry","Map","httpExtractor","graphqlExtractor","resolveExtractor","type","registry","get","httpExtractor","getRequestFromContext","host","resolveExtractor","getType","getRequest","getResponseFromContext","host","resolveExtractor","getType","getResponse","import_common","REQUIRE_AUTH_KEY","AuthType","Require","type","subtypes","SetMetadata","import_common","SKIP_CSRF_KEY","SkipCsrf","SetMetadata","import_common","parseExpiryToMs","expiry","match","Error","digits","unit","value","Number","parseInt","multipliers","s","m","h","d","w","y","multiplier","undefined","hashToken","token","createHash","update","digest","verifyTokenHash","expectedHash","computedHash","length","timingSafeEqual","Buffer","from","TokenService","logger","Logger","name","jwtService","config","generateAccessToken","sessionInfo","refreshToken","userId","sessionId","sessionType","metadata","sign","tokenType","TokenType","ACCESS","refreshTokenHash","hashToken","expiresIn","tokenExpiry","access","generateRefreshToken","REFRESH","refresh","payload","options","verify","token","expectedType","Error","error","getExpiryTime","type","Date","now","parseExpiryToMs","getExpiryInSeconds","Math","floor","validateAccessToken","decoded","UnauthorizedException","jwtError","validateRefreshToken","validateTokenBinding","accessToken","verifyTokenHash","VrittiAuthGuard","logger","Logger","name","reflector","requestService","tokenService","config","canActivate","context","request","getRequestFromContext","reply","getResponseFromContext","route","method","url","authConfig","csrfExemptTransports","guard","skipCsrf","getAllAndOverride","SKIP_CSRF_KEY","getHandler","getClass","includes","getType","requirement","REQUIRE_AUTH_KEY","type","AuthType","Session","subtypes","App","handleAppAuth","Cloud","handleCloudAuth","Public","validateCsrf","debug","isSseEndpoint","get","SSE_METADATA","handleSseAuth","sessionType","handleHttpAuth","csrfExemptSessionTypes","onAuthenticated","error","UnauthorizedException","auth","kind","requiredAppTypes","appType","undefined","length","warn","join","requiredSessionTypes","accessToken","getAccessToken","decoded","validateAccessToken","refreshTokenBindingExemptSessionTypes","refreshToken","getRefreshToken","validateTokenBinding","tokenType","_tokenType","refreshTokenHash","_hash","exp","_exp","iat","_iat","claims","userId","validateRefreshToken","safeMethods","fastifyInstance","server","csrfProtection","ForbiddenException","Promise","resolve","reject","originalSend","send","bind","Error","err","_error","errors","field","message","scope","Scope","REQUEST","mergeWithDefaults","input","tokenExpiry","cookie","AUTH_CONFIG_DEFAULTS","guard","AuthConfigModule","forRootAsync","options","module","imports","ConfigModule","RequestModule","JwtModule","registerAsync","inject","ConfigService","useFactory","config","secret","getOrThrow","signOptions","algorithm","providers","provide","Reflector","useClass","APP_GUARD","VrittiAuthGuard","AUTH_CONFIG","args","TokenService","exports","import_common","AccessToken","createParamDecorator","_data","ctx","request","getRequestFromContext","authHeader","headers","authorization","replace","import_common","ClientIp","createParamDecorator","_data","ctx","getRequestFromContext","ip","import_common","CookieDomain","createParamDecorator","_data","ctx","request","getRequestFromContext","forwarded","headers","raw","Array","isArray","hostStr","hostname","domain","split","baseDomain","authConfig","cookie","refreshCookieDomain","AUTH_CONFIG_DEFAULTS","endsWith","import_common","CookieName","createParamDecorator","_data","ctx","request","getRequestFromContext","authConfig","cookie","refreshCookieName","AUTH_CONFIG_DEFAULTS","import_common","Hostname","createParamDecorator","_data","ctx","request","getRequestFromContext","forwarded","headers","raw","Array","isArray","hostStr","hostname","split","import_common","import_common","import_common","HttpProblemException","HttpException","detailOrOptions","httpStatus","options","detail","type","label","errors","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","UnauthorizedException","HttpProblemException","detailOrOptions","HttpStatus","UNAUTHORIZED","import_common","import_common","import_common","buildCookieOptionsForHost","cookieConfig","hostname","baseDomain","refreshCookieDomain","Error","endsWith","UnauthorizedException","httpOnly","secure","refreshCookieSecure","sameSite","refreshCookieSameSite","path","refreshCookiePath","maxAge","refreshCookieMaxAge","domain","RefreshCookieOptions","createParamDecorator","_data","ctx","request","getRequestFromContext","forwarded","headers","raw","Array","isArray","hostStr","split","authConfig","cookie","AUTH_CONFIG_DEFAULTS","import_common","RefreshTokenCookie","createParamDecorator","_data","ctx","request","getRequestFromContext","cookies","cookieName","authConfig","cookie","refreshCookieName","AUTH_CONFIG_DEFAULTS","import_common","SessionData","createParamDecorator","_data","ctx","auth","getRequestFromContext","kind","Error","userId","sessionId","sessionType","import_common","Subdomain","createParamDecorator","_data","ctx","request","getRequestFromContext","forwarded","headers","raw","Array","isArray","hostStr","hostname","undefined","host","split","import_common","UserAgent","createParamDecorator","_data","ctx","userAgent","getRequestFromContext","headers","Array","isArray","import_common","UserId","createParamDecorator","_data","ctx","auth","getRequestFromContext","kind","Error","userId"]}
1
+ {"version":3,"sources":["../src/auth/index.ts","../src/signing/request.ts","../src/auth/app-request.ts","../src/auth/auth.config.ts","../src/auth/auth-config.module.ts","../src/request/request.module.ts","../src/request/services/request.service.ts","../src/context/resolve-request.ts","../src/auth/guards/vritti-auth.guard.ts","../src/context/extractors/graphql.extractor.ts","../src/context/extractors/http.extractor.ts","../src/context/context.registry.ts","../src/context/get-request.ts","../src/context/get-response.ts","../src/auth/decorators/require.decorator.ts","../src/auth/decorators/skip-csrf.decorator.ts","../src/auth/services/token.service.ts","../src/utils/time.utils.ts","../src/auth/utils/token-hash.util.ts","../src/auth/decorators/access-token.decorator.ts","../src/auth/decorators/client-ip.decorator.ts","../src/auth/decorators/cookie-domain.decorator.ts","../src/auth/decorators/cookie-name.decorator.ts","../src/auth/decorators/hostname.decorator.ts","../src/auth/decorators/refresh-cookie-options.decorator.ts","../src/exceptions/bad-gateway.exception.ts","../src/exceptions/base-field.exception.ts","../src/exceptions/bad-request.exception.ts","../src/exceptions/conflict.exception.ts","../src/exceptions/forbidden.exception.ts","../src/exceptions/gone.exception.ts","../src/exceptions/internal-server-error.exception.ts","../src/exceptions/method-not-allowed.exception.ts","../src/exceptions/not-acceptable.exception.ts","../src/exceptions/not-found.exception.ts","../src/exceptions/not-implemented.exception.ts","../src/exceptions/payload-too-large.exception.ts","../src/exceptions/request-timeout.exception.ts","../src/exceptions/service-unavailable.exception.ts","../src/exceptions/too-many-requests.exception.ts","../src/exceptions/unauthorized.exception.ts","../src/exceptions/unprocessable-entity.exception.ts","../src/exceptions/unsupported-media-type.exception.ts","../src/exceptions/validation.exception.ts","../src/auth/decorators/refresh-token-cookie.decorator.ts","../src/auth/decorators/session-data.decorator.ts","../src/auth/decorators/subdomain.decorator.ts","../src/auth/decorators/user-agent.decorator.ts","../src/auth/decorators/user-id.decorator.ts"],"sourcesContent":["// Token types and config\n\nexport * from './app-request';\nexport {\n type AccessTokenPayload,\n AUTH_CONFIG,\n AUTH_CONFIG_DEFAULTS,\n type AuthConfig,\n type CookieConfig,\n type CookieSerializeOptions,\n type DecodedAccessToken,\n type DecodedRefreshToken,\n type GuardConfig,\n type OnAuthenticatedCallback,\n type RefreshTokenPayload,\n type TokenExpiry,\n type TokenExpiryString,\n TokenType,\n} from './auth.config';\nexport * from './auth-config.module';\nexport * from './decorators/access-token.decorator';\nexport * from './decorators/client-ip.decorator';\nexport * from './decorators/cookie-domain.decorator';\nexport * from './decorators/cookie-name.decorator';\nexport * from './decorators/hostname.decorator';\nexport * from './decorators/refresh-cookie-options.decorator';\nexport * from './decorators/refresh-token-cookie.decorator';\nexport * from './decorators/require.decorator';\nexport type { SessionInfo } from './decorators/session-data.decorator';\nexport { SessionData } from './decorators/session-data.decorator';\nexport { SKIP_CSRF_KEY, SkipCsrf } from './decorators/skip-csrf.decorator';\nexport * from './decorators/subdomain.decorator';\nexport * from './decorators/user-agent.decorator';\nexport * from './decorators/user-id.decorator';\nexport * from './guards/vritti-auth.guard';\n// Token service — generation, validation, and binding verification\nexport { TokenService } from './services/token.service';\n\n// Token hash utilities\nexport { hashToken, verifyTokenHash } from './utils/token-hash.util';\n","import { createHash, createPrivateKey, createPublicKey, sign, verify } from 'node:crypto';\n\n/**\n * The workspace-scope headers, in the order the canonical appends them.\n *\n * Exactly one is expected — the kind of scope *is* which header is sent, the same\n * convention core-web and core-app already use. Signing the header **name** rather\n * than a separate `kind` field means a verifier needs no scope vocabulary: it scans\n * this same list in this same order and rebuilds an identical string.\n *\n * Fixed and shared so a signer and a verifier can never disagree about it, and so the\n * discouraged multi-header case is deterministic rather than ambiguous.\n */\nexport const WORKSPACE_HEADER_ORDER = ['x-site-id', 'x-sg-id', 'x-le-id', 'x-org-id'] as const;\n\nexport interface RequestCanonicalInput {\n method: string;\n path: string;\n orgId?: string;\n body?: string | Buffer;\n timestamp: number;\n /**\n * The raw query string, without the leading `?`.\n *\n * Signed byte for byte, with no sorting or re-encoding, so the signer and the\n * verifier cannot disagree about normalization. The consequence is that a proxy\n * which reorders or re-encodes parameters invalidates the signature.\n */\n query?: string;\n /**\n * The party (person) the request acts for, when there is one.\n *\n * Signed so a caller cannot be swapped for another shopper in transit.\n */\n partyId?: string;\n /**\n * The workspace-scope headers actually present on the request, by header name.\n *\n * Only names in `WORKSPACE_HEADER_ORDER` are considered, and they are appended in\n * that order — so signer and verifier agree without either needing to know what a\n * \"site\" or a \"legal entity\" is.\n */\n workspaceHeaders?: Record<string, string | undefined>;\n}\n\nexport interface SignRequestInput {\n method: string;\n path: string;\n orgId?: string;\n body?: string | Buffer;\n /** Raw query string, without the leading `?`. See `RequestCanonicalInput`. */\n query?: string;\n /**\n * The party (person) the request acts for, when there is one.\n *\n * Signed so a caller cannot be swapped for another shopper in transit.\n */\n partyId?: string;\n /**\n * The workspace-scope headers actually present on the request, by header name.\n *\n * Only names in `WORKSPACE_HEADER_ORDER` are considered, and they are appended in\n * that order — so signer and verifier agree without either needing to know what a\n * \"site\" or a \"legal entity\" is.\n */\n workspaceHeaders?: Record<string, string | undefined>;\n}\n\nexport interface VerifySignedRequestInput {\n method: string;\n path: string;\n orgId?: string;\n /** Raw query string, without the leading `?`. See `RequestCanonicalInput`. */\n query?: string;\n /**\n * The party (person) the request acts for, when there is one.\n *\n * Signed so a caller cannot be swapped for another shopper in transit.\n */\n partyId?: string;\n /**\n * The workspace-scope headers actually present on the request, by header name.\n *\n * Only names in `WORKSPACE_HEADER_ORDER` are considered, and they are appended in\n * that order — so signer and verifier agree without either needing to know what a\n * \"site\" or a \"legal entity\" is.\n */\n workspaceHeaders?: Record<string, string | undefined>;\n rawBody?: string | Buffer;\n timestamp: string | number;\n signature: string;\n publicKey: string;\n maxSkewSeconds?: number;\n}\n\n/**\n * Builds the canonical string a request's signature is made over:\n * `METHOD\\npath\\norgId\\nsha256hex(body)\\ntimestamp`, then a labelled line per\n * optional field that is present.\n *\n * Optional fields are appended **only when set**, so a caller that supplies none\n * produces exactly the string this function has always produced — which is what\n * lets new fields be added without invalidating existing signers. They are\n * **labelled** so that \"query present, party absent\" can never be confused with\n * \"party present, query absent\".\n *\n * Absence is still tamper-evident: strip a signed field in transit and the\n * verifier rebuilds the canonical without it, which no longer matches.\n */\nexport function buildRequestCanonical(input: RequestCanonicalInput): string {\n const bodyHash = createHash('sha256')\n .update(input.body ?? '')\n .digest('hex');\n\n const lines = [input.method.toUpperCase(), input.path, input.orgId ?? '', bodyHash, String(input.timestamp)];\n if (input.query) lines.push(`query:${input.query}`);\n if (input.partyId) lines.push(`party:${input.partyId}`);\n\n // Header name and value both go in, so swapping x-le-id for x-site-id on the same\n // value breaks the signature. Fixed order, so multiple headers stay deterministic.\n for (const header of WORKSPACE_HEADER_ORDER) {\n const value = input.workspaceHeaders?.[header];\n if (value) lines.push(`${header}:${value}`);\n }\n\n return lines.join('\\n');\n}\n\n// Signs a request with an Ed25519 private key (base64 pkcs8 DER), stamping the current unix time\nexport function signRequestHeaders(\n input: SignRequestInput,\n privateKeyBase64: string,\n): { 'x-timestamp': string; 'x-signature': string } {\n const timestamp = Math.floor(Date.now() / 1000);\n const canonical = buildRequestCanonical({ ...input, timestamp });\n const key = createPrivateKey({ key: Buffer.from(privateKeyBase64, 'base64'), format: 'der', type: 'pkcs8' });\n const signature = sign(null, Buffer.from(canonical, 'utf8'), key).toString('base64');\n return { 'x-timestamp': String(timestamp), 'x-signature': signature };\n}\n\n// Verifies a signed request (signature + timestamp skew) against an Ed25519 public key; malformed input ⇒ false\nexport function verifySignedRequest(input: VerifySignedRequestInput): boolean {\n try {\n const timestamp = Number(input.timestamp);\n if (!Number.isFinite(timestamp)) return false;\n const maxSkew = input.maxSkewSeconds ?? 300;\n if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > maxSkew) return false;\n const canonical = buildRequestCanonical({\n method: input.method,\n path: input.path,\n orgId: input.orgId,\n query: input.query,\n partyId: input.partyId,\n workspaceHeaders: input.workspaceHeaders,\n body: input.rawBody,\n timestamp,\n });\n const key = createPublicKey({ key: Buffer.from(input.publicKey, 'base64'), format: 'der', type: 'spki' });\n return verify(null, Buffer.from(canonical, 'utf8'), key, Buffer.from(input.signature, 'base64'));\n } catch {\n return false;\n }\n}\n","/**\n * The shared contract between this guard and a server that authenticates apps.\n *\n * The guard recognises `@RequireApp()` and enforces the type filter; the server\n * resolves the credential in `guard.onAuthenticated`, because only it has a\n * database. These are the values both sides have to agree on.\n */\n\n/** The client id an app sends alongside the standard signature headers. */\nexport const CLIENT_ID_HEADER = 'x-vritti-client-id';\n\n/** The party (person) a signed request is acting for, when there is one. */\nexport const PARTY_ID_HEADER = 'x-party-id';\n\n// Re-exported from the signing module, which owns it because the canonical is what\n// fixes the order. Kept visible here so an auth-side consumer has one import.\nexport { WORKSPACE_HEADER_ORDER } from '../signing/request';\n\n/**\n * How far a signed request's timestamp may drift before it is refused.\n *\n * Five minutes, matching the deployment-signing path. Wide enough for ordinary\n * clock drift between two servers; a captured request is replayable inside the\n * window, which is why app operations should stay idempotent.\n */\nexport const MAX_CLOCK_SKEW_SECONDS = 300;\n\n","import type { FastifyRequest } from 'fastify';\nimport type { RequestService } from '../request/services/request.service';\n\nexport const AUTH_CONFIG = Symbol('AUTH_CONFIG');\n\n// Called once the guard has decided which kind of caller this is, with the partially-built\n// auth object to fill in. The consuming server is the only side with a database, so tenant\n// resolution, signature verification against a stored key, and workspace context all land here.\n//\n// `auth.kind` is already set and is what the server switches on. Everything else the server\n// knows how to resolve — organization, subdomain, app identity — it writes onto `auth`.\nexport type OnAuthenticatedCallback = (\n requestService: RequestService,\n auth: NonNullable<FastifyRequest['auth']>,\n) => void | Promise<void>;\n\nexport type TokenExpiryString = `${number}${'s' | 'm' | 'h' | 'd' | 'w' | 'y'}`;\n\nexport interface TokenExpiry {\n access: TokenExpiryString;\n refresh: TokenExpiryString;\n}\n\nexport interface CookieConfig {\n refreshCookieName: string;\n refreshCookieMaxAge: number;\n refreshCookiePath: string;\n refreshCookieSecure: boolean;\n refreshCookieSameSite: 'strict' | 'lax' | 'none';\n refreshCookieDomain?: string;\n}\n\nexport interface GuardConfig {\n authHeaderName: string;\n tokenPrefix: string;\n csrfExemptSessionTypes?: string[];\n csrfExemptTransports?: string[];\n refreshTokenBindingExemptSessionTypes?: string[];\n onAuthenticated?: OnAuthenticatedCallback;\n}\n\nexport interface AuthConfig {\n tokenExpiry: TokenExpiry;\n cookie: CookieConfig;\n guard: GuardConfig;\n}\n\nexport const AUTH_CONFIG_DEFAULTS = {\n cookie: {\n refreshCookieName: 'vritti_refresh',\n refreshCookieMaxAge: 30 * 24 * 60 * 60 * 1000, // 30 days\n refreshCookiePath: '/',\n refreshCookieSecure: process.env.NODE_ENV === 'production',\n refreshCookieSameSite: 'strict' as const,\n refreshCookieDomain: 'localhost',\n },\n guard: {\n authHeaderName: 'authorization',\n tokenPrefix: 'Bearer',\n csrfExemptSessionTypes: [],\n csrfExemptTransports: [],\n refreshTokenBindingExemptSessionTypes: [],\n },\n} satisfies Omit<AuthConfig, 'tokenExpiry'>;\n\nexport interface CookieSerializeOptions {\n httpOnly: boolean;\n secure: boolean;\n sameSite: 'strict' | 'lax' | 'none';\n path: string;\n maxAge: number;\n domain: string;\n}\n\nexport enum TokenType {\n ACCESS = 'access',\n REFRESH = 'refresh',\n}\n\ninterface JwtClaims {\n exp: number;\n iat: number;\n}\n\nexport interface AccessTokenPayload {\n sessionType: string;\n tokenType: TokenType.ACCESS;\n userId: string;\n sessionId: string;\n refreshTokenHash: string;\n}\n\nexport type DecodedAccessToken = AccessTokenPayload & JwtClaims;\n\nexport interface RefreshTokenPayload {\n sessionType: string;\n tokenType: TokenType.REFRESH;\n userId: string;\n sessionId: string;\n}\n\nexport type DecodedRefreshToken = RefreshTokenPayload & JwtClaims;\n","import { type DynamicModule, Global, type InjectionToken, Module, type ModuleMetadata } from '@nestjs/common';\nimport { ConfigModule, ConfigService } from '@nestjs/config';\nimport { APP_GUARD, Reflector } from '@nestjs/core';\nimport { JwtModule } from '@nestjs/jwt';\nimport { RequestModule } from '../request/request.module';\nimport {\n AUTH_CONFIG,\n AUTH_CONFIG_DEFAULTS,\n type AuthConfig,\n type CookieConfig,\n type GuardConfig,\n type TokenExpiry,\n} from './auth.config';\nimport { VrittiAuthGuard } from './guards/vritti-auth.guard';\nimport { TokenService } from './services/token.service';\n\n// Factory return type — tokenExpiry is required, cookie and guard are partial with defaults merged\ninterface AuthConfigInput {\n tokenExpiry: TokenExpiry;\n cookie?: Partial<CookieConfig>;\n guard?: Partial<GuardConfig>;\n}\n\ninterface AuthConfigModuleOptions<T extends unknown[] = unknown[]> {\n useFactory: (...args: [...T]) => AuthConfigInput | Promise<AuthConfigInput>;\n inject?: InjectionToken[];\n /**\n * Extra modules the factory's injected providers come from.\n *\n * Needed because `guard.onAuthenticated` is where a server resolves an app\n * credential, so the factory has to be able to inject the service that owns that\n * lookup. Mirrors `NatsClientModule.forRoot`.\n */\n imports?: ModuleMetadata['imports'];\n}\n\n// Merges user-provided partial config with defaults to produce a complete AuthConfig\nfunction mergeWithDefaults(input: AuthConfigInput): AuthConfig {\n return {\n tokenExpiry: input.tokenExpiry,\n cookie: {\n ...AUTH_CONFIG_DEFAULTS.cookie,\n ...(input.cookie ?? {}),\n },\n guard: {\n ...AUTH_CONFIG_DEFAULTS.guard,\n ...(input.guard ?? {}),\n },\n };\n}\n\n@Global()\n@Module({})\nexport class AuthConfigModule {\n // Registers JWT, TokenService, and global VrittiAuthGuard\n static forRootAsync<T extends unknown[] = unknown[]>(options: AuthConfigModuleOptions<T>): DynamicModule {\n return {\n module: AuthConfigModule,\n imports: [\n ConfigModule,\n RequestModule,\n ...(options.imports ?? []),\n JwtModule.registerAsync({\n imports: [ConfigModule],\n inject: [ConfigService],\n useFactory: (config: ConfigService) => ({\n secret: config.getOrThrow<string>('JWT_SECRET'),\n signOptions: { algorithm: 'HS256' as const },\n }),\n }),\n ],\n providers: [\n {\n provide: Reflector,\n useClass: Reflector,\n },\n {\n provide: APP_GUARD,\n useClass: VrittiAuthGuard,\n },\n {\n provide: AUTH_CONFIG,\n useFactory: async (...args: unknown[]) => {\n const input = await options.useFactory(...(args as [...T]));\n return mergeWithDefaults(input);\n },\n inject: options.inject || [],\n },\n TokenService,\n ],\n exports: [JwtModule, TokenService, AUTH_CONFIG],\n };\n }\n}\n","import { Global, Module } from '@nestjs/common';\nimport { RequestService } from './services/request.service';\n\n@Global()\n@Module({\n providers: [RequestService],\n exports: [RequestService],\n})\nexport class RequestModule {}\n","import { Inject, Injectable, Scope } from '@nestjs/common';\nimport { REQUEST } from '@nestjs/core';\nimport type { FastifyRequest } from 'fastify';\nimport { AUTH_CONFIG, type AuthConfig } from '../../auth/auth.config';\nimport { resolveInjectedRequest } from '../../context/resolve-request';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class RequestService {\n constructor(\n @Inject(REQUEST) private readonly injectedRequest: FastifyRequest,\n @Inject(AUTH_CONFIG) private readonly config: AuthConfig,\n ) {}\n\n // Unwraps the GraphQL { req, reply } context wrapper so every accessor below works across both transports\n private get request(): FastifyRequest {\n return resolveInjectedRequest(this.injectedRequest);\n }\n\n // Extracts the bearer access token from the Authorization header\n getAccessToken(): string | null {\n const authHeader = this.request.headers?.[this.config.guard.authHeaderName];\n if (!authHeader || typeof authHeader !== 'string') {\n return null;\n }\n const [type, token] = authHeader.split(' ') ?? [];\n return type === this.config.guard.tokenPrefix && token ? token : null;\n }\n\n // Extracts the refresh token from the configured httpOnly cookie\n getRefreshToken(): string | null {\n try {\n const cookies = (this.request as unknown as { cookies?: Record<string, string> }).cookies;\n if (cookies && typeof cookies === 'object') {\n const refreshToken = cookies[this.config.cookie.refreshCookieName];\n if (refreshToken) {\n return refreshToken;\n }\n }\n return null;\n } catch (_error: unknown) {\n return null;\n }\n }\n\n // Returns the value of a specific request header by key\n getHeader(key: string): string | string[] | undefined {\n return this.request.headers?.[key];\n }\n\n // Returns the request hostname (without port)\n getHostname(): string {\n return this.request.hostname ?? '';\n }\n\n // Returns all request headers\n getAllHeaders(): FastifyRequest['headers'] {\n return this.request.headers || {};\n }\n\n // Returns the HTTP method\n getMethod(): string {\n return this.request.method ?? '';\n }\n\n /**\n * Returns the path with any query string stripped.\n *\n * Signature canonicals cover the path only, so a query string must not be part\n * of what gets signed or verified.\n */\n getPath(): string {\n const url = this.request.url ?? '';\n return url.split('?')[0] ?? url;\n }\n\n /**\n * Returns the raw query string, without the leading `?`.\n *\n * Companion to `getPath()`, which strips it. Signed separately so a REST request's\n * filters are covered — `getPath()` alone would sign `GET /people?search=salt` as\n * though the filter were not there.\n */\n getQuery(): string {\n const url = this.request.url ?? '';\n const index = url.indexOf('?');\n return index === -1 ? '' : url.slice(index + 1);\n }\n\n /**\n * Returns a single decoded query param, or undefined when absent.\n *\n * Reads the raw URL rather than Fastify's parsed `query` so it is usable from the\n * guard, which runs before a route's own parsing is meaningful.\n */\n getQueryParam(key: string): string | undefined {\n return new URLSearchParams(this.getQuery()).get(key) ?? undefined;\n }\n\n /**\n * Returns the raw request body, as `fastify-raw-body` leaves it.\n *\n * Read structurally rather than through a module augmentation: that plugin is the\n * consuming server's dependency, not this SDK's, and declaring `rawBody` here\n * would collide with the plugin's own declaration the moment the two drift.\n *\n * A server that has not registered it yields `undefined`, which hashes as an empty\n * body and therefore fails any signature made over real bytes — refused, never\n * waved through.\n */\n getRawBody(): string | Buffer | undefined {\n return (this.request as unknown as { rawBody?: string | Buffer }).rawBody;\n }\n}\n","import type { FastifyRequest } from 'fastify';\n\n// Unwraps the @Inject(REQUEST) value to the real Fastify request (GraphQL injects a { req, reply } context).\nexport function resolveInjectedRequest(injected: FastifyRequest): FastifyRequest {\n const candidate = injected as unknown as { headers?: unknown; req?: FastifyRequest };\n if (candidate && candidate.headers === undefined && candidate.req) {\n return candidate.req;\n }\n return injected;\n}\n","import {\n type CanActivate,\n type ExecutionContext,\n ForbiddenException,\n Inject,\n Injectable,\n Logger,\n Scope,\n UnauthorizedException,\n} from '@nestjs/common';\nimport { SSE_METADATA } from '@nestjs/common/constants';\nimport { Reflector } from '@nestjs/core';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport '../../types/fastify-augmentation';\nimport { getRequestFromContext, getResponseFromContext } from '../../context';\nimport { RequestService } from '../../request/services/request.service';\nimport { AUTH_CONFIG, type AuthConfig } from '../auth.config';\nimport { type AuthRequirement, AuthType, REQUIRE_AUTH_KEY } from '../decorators/require.decorator';\nimport { SKIP_CSRF_KEY } from '../decorators/skip-csrf.decorator';\nimport { TokenService } from '../services/token.service';\n\ninterface FastifyInstanceWithCsrf {\n csrfProtection?: (req: FastifyRequest, reply: FastifyReply, next: (err?: Error) => void) => void;\n}\n\ntype PatchableReply = { send: (...args: unknown[]) => unknown };\n\n@Injectable({ scope: Scope.REQUEST })\nexport class VrittiAuthGuard implements CanActivate {\n private readonly logger = new Logger(VrittiAuthGuard.name);\n\n constructor(\n private readonly reflector: Reflector,\n private readonly requestService: RequestService,\n private readonly tokenService: TokenService,\n @Inject(AUTH_CONFIG) private readonly config: AuthConfig,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = getRequestFromContext(context);\n const reply = getResponseFromContext(context);\n const route = `${request.method} ${request.url}`;\n\n // Attach auth config to request so decorators can access it without injection\n request.authConfig = this.config;\n\n // CSRF is skipped via @SkipCsrf() or when the request transport is CSRF-exempt (e.g. 'graphql')\n const csrfExemptTransports = this.config.guard.csrfExemptTransports ?? [];\n const skipCsrf =\n this.reflector.getAllAndOverride<boolean>(SKIP_CSRF_KEY, [context.getHandler(), context.getClass()]) ||\n csrfExemptTransports.includes(context.getType<string>());\n\n // A route with no @Require() is a session route restricted to nothing — the historical\n // default, kept so an undecorated endpoint stays authenticated rather than falling open.\n const requirement = this.reflector.getAllAndOverride<AuthRequirement>(REQUIRE_AUTH_KEY, [\n context.getHandler(),\n context.getClass(),\n ]) ?? { type: AuthType.Session, subtypes: [] };\n\n switch (requirement.type) {\n // Signed server-to-server calls return before CSRF is ever reached. Neither carries a\n // cookie for CSRF to protect, and that holds on REST too — which a transport-level\n // exemption would not cover.\n case AuthType.App:\n return this.handleAppAuth(request, requirement.subtypes, route);\n\n case AuthType.Cloud:\n return this.handleCloudAuth(request, route);\n\n case AuthType.Public: {\n if (!skipCsrf) {\n await this.validateCsrf(request, reply);\n }\n this.logger.debug(`${route} — public endpoint, skipping auth`);\n return true;\n }\n\n case AuthType.Session: {\n // SSE authenticates via the refresh cookie — EventSource cannot send Authorization headers\n const isSseEndpoint = this.reflector.get<boolean>(SSE_METADATA, context.getHandler());\n if (isSseEndpoint) {\n this.logger.debug(`${route} — SSE endpoint, authenticating via refresh cookie`);\n return this.handleSseAuth(request, requirement.subtypes);\n }\n\n const sessionType = await this.handleHttpAuth(request, requirement.subtypes);\n\n const csrfExemptSessionTypes = this.config.guard.csrfExemptSessionTypes ?? [];\n if (!skipCsrf && !csrfExemptSessionTypes.includes(sessionType)) {\n await this.validateCsrf(request, reply);\n }\n\n return true;\n }\n }\n }\n\n /**\n * Authenticates a signed request from the control plane.\n *\n * Same shape as the app branch and for the same reason: verifying the signature needs a key\n * the consuming server holds, so this delegates to `guard.onAuthenticated` and keeps only\n * what this side can do — recognising the decorator and seeding the context.\n *\n * There is no subtype filter. Cloud is one caller, not a family of them.\n */\n private async handleCloudAuth(request: FastifyRequest, route: string): Promise<boolean> {\n const onAuthenticated = this.config.guard.onAuthenticated;\n if (!onAuthenticated) {\n // Fail closed. Without the hook nothing verifies the signature, and treating the\n // request as authenticated would leave the endpoint open to anyone who found it.\n this.logger.error(`${route} — @Require(AuthType.Cloud) requires guard.onAuthenticated to be configured`);\n throw new UnauthorizedException('Cloud authentication is not configured');\n }\n\n const auth = { kind: 'cloud' } as NonNullable<FastifyRequest['auth']>;\n request.auth = auth;\n\n await onAuthenticated(this.requestService, auth);\n\n this.logger.debug(`${route} — authenticated cloud request`);\n return true;\n }\n\n /**\n * Authenticates a signed request from an external app.\n *\n * The credential lookup and the signature check are the consuming server's job —\n * it is the only side with a database — so this delegates to\n * `guard.onAuthenticated`, the same hook the session path already uses to resolve\n * organization and workspace context. That server fills in `organizationId`,\n * `appType` and whatever else it knows.\n *\n * What stays here is the part only this side can do: reading the decorator's\n * metadata and enforcing the app-type filter against what the server resolved.\n *\n * An empty list means \"any type\" — the caller is still authenticated. That makes\n * `@RequireApp()` with no arguments mean what it reads like.\n */\n private async handleAppAuth(request: FastifyRequest, requiredAppTypes: string[], route: string): Promise<boolean> {\n const onAuthenticated = this.config.guard.onAuthenticated;\n if (!onAuthenticated) {\n // Fail closed. Without the hook nothing can verify the signature, and\n // treating the request as authenticated would leave the endpoint wide open.\n this.logger.error(`${route} — @RequireApp() requires guard.onAuthenticated to be configured`);\n throw new UnauthorizedException('App authentication is not configured');\n }\n\n // Seeded so the hook has something to populate, matching how the session path\n // hands it an object to mutate.\n const auth = { kind: 'app' } as NonNullable<FastifyRequest['auth']>;\n request.auth = auth;\n\n await onAuthenticated(this.requestService, auth);\n\n const appType = auth.kind === 'app' ? auth.appType : undefined;\n if (requiredAppTypes.length && !requiredAppTypes.includes(appType ?? '')) {\n this.logger.warn(`${route} — app type ${appType ?? 'unknown'} not in allowed: [${requiredAppTypes.join(', ')}]`);\n // Deliberately the same error the server raises for an unknown client, a\n // revoked one or a bad signature. Distinguishing them would tell whoever is\n // probing which of the four they hit.\n throw new UnauthorizedException('This client is not recognised.');\n }\n\n // No organization in this message on purpose: which field holds the tenant is\n // the consuming server's augmentation, not something this side knows about.\n this.logger.debug(`${route} — authenticated app (${appType})`);\n return true;\n }\n\n // Authenticates standard HTTP requests using the access token from Authorization header\n private async handleHttpAuth(request: FastifyRequest, requiredSessionTypes?: string[]): Promise<string> {\n const route = `${request.method} ${request.url}`;\n\n const accessToken = this.requestService.getAccessToken();\n if (!accessToken) {\n this.logger.warn(`${route} — no access token found`);\n throw new UnauthorizedException('Access token not found');\n }\n\n const decoded = this.tokenService.validateAccessToken(accessToken);\n\n const refreshTokenBindingExemptSessionTypes = this.config.guard.refreshTokenBindingExemptSessionTypes ?? [];\n\n if (!refreshTokenBindingExemptSessionTypes.includes(decoded.sessionType)) {\n const refreshToken = this.requestService.getRefreshToken();\n if (!refreshToken) {\n throw new UnauthorizedException('Session validation failed');\n }\n this.tokenService.validateTokenBinding(decoded, refreshToken);\n }\n\n // Validate session type access (only if @RequireSession specifies types)\n if (requiredSessionTypes?.length && !requiredSessionTypes.includes(decoded.sessionType)) {\n this.logger.warn(\n `${route} — session type ${decoded.sessionType} not in allowed: [${requiredSessionTypes.join(', ')}]`,\n );\n throw new UnauthorizedException(`${decoded.sessionType} sessions cannot access this endpoint`);\n }\n\n // Attach auth to request — spread full decoded token (includes metadata fields)\n const { tokenType: _tokenType, refreshTokenHash: _hash, exp: _exp, iat: _iat, ...claims } = decoded;\n const auth = { kind: 'session', ...claims } as NonNullable<FastifyRequest['auth']>;\n request.auth = auth;\n\n // Call onAuthenticated callback if configured\n const onAuthenticated = this.config.guard.onAuthenticated;\n if (onAuthenticated) {\n await onAuthenticated(this.requestService, auth);\n }\n\n this.logger.debug(`${route} — authenticated user: ${decoded.userId} (${decoded.sessionType})`);\n return decoded.sessionType;\n }\n\n // Authenticates SSE connections using the refresh token httpOnly cookie\n private async handleSseAuth(request: FastifyRequest, requiredSessionTypes?: string[]): Promise<boolean> {\n const refreshToken = this.requestService.getRefreshToken();\n if (!refreshToken) {\n this.logger.warn(`SSE ${request.url} — no refresh token cookie`);\n throw new UnauthorizedException('Authentication required');\n }\n\n const decoded = this.tokenService.validateRefreshToken(refreshToken);\n\n if (requiredSessionTypes?.length && !requiredSessionTypes.includes(decoded.sessionType)) {\n this.logger.warn(`SSE ${request.url} — session type ${decoded.sessionType} not allowed`);\n throw new UnauthorizedException(`${decoded.sessionType} sessions cannot access this endpoint`);\n }\n\n const { tokenType: _tokenType, exp: _exp, iat: _iat, ...claims } = decoded;\n const auth = { kind: 'session', ...claims } as NonNullable<FastifyRequest['auth']>;\n request.auth = auth;\n\n // Same authorization hook the HTTP path runs. SSE carries its tenant context in the query string\n // (EventSource cannot set headers), and that is no more trustworthy than a header — so it has to be\n // checked against the session here too, or every @Sse() route silently skips the consuming server's\n // scoping. The refresh token carries the same session metadata the access token does, so the hook has\n // everything it needs.\n const onAuthenticated = this.config.guard.onAuthenticated;\n if (onAuthenticated) {\n await onAuthenticated(this.requestService, auth);\n }\n\n this.logger.debug(`SSE ${request.url} — authenticated user: ${decoded.userId} (${decoded.sessionType})`);\n return true;\n }\n\n // Validates CSRF token for state-changing requests\n private async validateCsrf(request: FastifyRequest, reply: FastifyReply): Promise<void> {\n const safeMethods = ['GET', 'HEAD', 'OPTIONS'];\n if (safeMethods.includes(request.method)) return;\n\n try {\n const fastifyInstance = request.server as unknown as FastifyInstanceWithCsrf;\n const csrfProtection = fastifyInstance.csrfProtection;\n if (!csrfProtection) {\n throw new ForbiddenException('CSRF protection not configured');\n }\n\n await new Promise<void>((resolve, reject) => {\n const originalSend = reply.send.bind(reply);\n (reply as PatchableReply).send = () => {\n (reply as PatchableReply).send = originalSend as PatchableReply['send'];\n reject(new Error('CSRF validation failed'));\n return reply;\n };\n\n csrfProtection(request, reply, (err?: Error) => {\n (reply as PatchableReply).send = originalSend as PatchableReply['send'];\n if (err) reject(err);\n else resolve();\n });\n });\n } catch (_error: unknown) {\n this.logger.warn(`${request.method} ${request.url} — CSRF validation failed`);\n throw new ForbiddenException({\n errors: [{ field: 'csrf', message: 'Invalid or missing CSRF token' }],\n message: 'CSRF validation failed',\n });\n }\n }\n}\n","import type { ArgumentsHost } from '@nestjs/common';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { RequestExtractor } from '../context.types';\n\ninterface GqlContext {\n req: FastifyRequest;\n reply?: FastifyReply;\n}\n\nexport const graphqlExtractor: RequestExtractor = {\n getRequest: (host: ArgumentsHost) => host.getArgByIndex<GqlContext>(2).req,\n getResponse: (host: ArgumentsHost) => host.getArgByIndex<GqlContext>(2).reply as FastifyReply,\n};\n","import type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { RequestExtractor } from '../context.types';\n\n// Default transport: standard HTTP via the Fastify adapter.\nexport const httpExtractor: RequestExtractor = {\n getRequest: (host) => host.switchToHttp().getRequest<FastifyRequest>(),\n getResponse: (host) => host.switchToHttp().getResponse<FastifyReply>(),\n};\n","import type { RequestExtractor, TransportType } from './context.types';\nimport { graphqlExtractor } from './extractors/graphql.extractor';\nimport { httpExtractor } from './extractors/http.extractor';\n\nconst registry = new Map<TransportType, RequestExtractor>([\n ['http', httpExtractor],\n ['graphql', graphqlExtractor],\n]);\n\n// Registers (or overrides) the extractor for a transport type without modifying the SDK\nexport function registerTransport(type: TransportType, extractor: RequestExtractor): void {\n registry.set(type, extractor);\n}\n\n// Resolves the extractor for the host's reported transport, falling back to HTTP.\nexport function resolveExtractor(type: string): RequestExtractor {\n return registry.get(type as TransportType) ?? httpExtractor;\n}\n","import type { ArgumentsHost } from '@nestjs/common';\nimport type { FastifyRequest } from 'fastify';\nimport { resolveExtractor } from './context.registry';\n\n// Returns the underlying Fastify request for any registered transport, via the transport registry.\nexport function getRequestFromContext(host: ArgumentsHost): FastifyRequest {\n return resolveExtractor(host.getType()).getRequest(host);\n}\n","import type { ArgumentsHost } from '@nestjs/common';\nimport type { FastifyReply } from 'fastify';\nimport { resolveExtractor } from './context.registry';\n\n// Returns the underlying Fastify reply for any registered transport (HTTP, GraphQL, ...).\nexport function getResponseFromContext(host: ArgumentsHost): FastifyReply {\n return resolveExtractor(host.getType()).getResponse(host);\n}\n","import { SetMetadata } from '@nestjs/common';\n\nexport const REQUIRE_AUTH_KEY = 'requireAuth';\n\nexport enum AuthType {\n Session = 'session',\n App = 'app',\n Cloud = 'cloud',\n Public = 'public',\n}\n\nexport interface AuthRequirement {\n type: AuthType;\n subtypes: string[];\n}\n\n// Declares how a route authenticates, and which subtypes of that caller may reach it.\n//\n// One decorator for every caller kind, so the branch the guard takes is stated at the call\n// site rather than inferred from which of three decorators happens to be present:\n//\n// @Require(AuthType.Session, SessionTypeValues.WEB) session types WEB\n// @Require(AuthType.App, AppTypeValues.GRAPHQL) app credentials of type GRAPHQL\n// @Require(AuthType.Cloud) signed control-plane calls\n// @Require(AuthType.Public) no authentication\n//\n// Subtypes are compared as strings — the enums belong to the consuming server's schema and\n// this only ever compares. Passing none means \"any subtype\", so the caller is still\n// authenticated but unrestricted.\nexport const Require = (type: AuthType, ...subtypes: string[]) =>\n SetMetadata<string, AuthRequirement>(REQUIRE_AUTH_KEY, { type, subtypes });\n","import { SetMetadata } from '@nestjs/common';\n\nexport const SKIP_CSRF_KEY = 'skipCsrf';\n\nexport const SkipCsrf = () => SetMetadata(SKIP_CSRF_KEY, true);\n","import { Inject, Injectable, Logger, UnauthorizedException } from '@nestjs/common';\nimport { JwtService, type JwtSignOptions } from '@nestjs/jwt';\nimport type { VrittiSessionAuth } from 'fastify';\nimport '../../types/fastify-augmentation';\nimport { parseExpiryToMs } from '../../utils/time.utils';\nimport {\n AUTH_CONFIG,\n type AuthConfig,\n type DecodedAccessToken,\n type DecodedRefreshToken,\n TokenType,\n} from '../auth.config';\nimport { hashToken, verifyTokenHash } from '../utils/token-hash.util';\n\nexport type { DecodedAccessToken, DecodedRefreshToken };\n\n// The claims a session token carries. Derived from the request-side session auth minus its\n// discriminator — the guard re-adds `kind` when it decodes, so storing it would be redundant.\ntype SessionInfo = Omit<VrittiSessionAuth, 'kind'>;\n\ninterface TokenError extends Error {\n name: 'TokenExpiredError' | 'JsonWebTokenError' | 'NotBeforeError';\n}\n\n// Handles all token operations — generation, validation, and binding verification\n@Injectable()\nexport class TokenService {\n private readonly logger = new Logger(TokenService.name);\n\n constructor(\n private readonly jwtService: JwtService,\n @Inject(AUTH_CONFIG) private readonly config: AuthConfig,\n ) {}\n\n // ---- Generation ----\n\n // Generates an access token bound to the given refresh token\n generateAccessToken(sessionInfo: SessionInfo, refreshToken: string): string {\n const { userId, sessionId, sessionType, ...metadata } = sessionInfo;\n return this.jwtService.sign(\n {\n sessionType,\n tokenType: TokenType.ACCESS,\n userId,\n sessionId,\n refreshTokenHash: hashToken(refreshToken),\n ...metadata,\n },\n { expiresIn: this.config.tokenExpiry.access },\n );\n }\n\n // Generates a refresh token for session persistence\n generateRefreshToken(sessionInfo: SessionInfo): string {\n const { userId, sessionId, sessionType, ...metadata } = sessionInfo;\n return this.jwtService.sign(\n { sessionType, tokenType: TokenType.REFRESH, userId, sessionId, ...metadata },\n { expiresIn: this.config.tokenExpiry.refresh },\n );\n }\n\n // Signs an arbitrary payload with optional JWT options\n sign(payload: object, options?: JwtSignOptions): string {\n return this.jwtService.sign(payload, options);\n }\n\n // Verifies a token and ensures it matches the expected token type\n verify(\n token: string,\n expectedType: TokenType,\n ): { userId: string; sessionId: string; sessionType: string; tokenType: TokenType } {\n try {\n const payload = this.jwtService.verify(token);\n\n if (payload.tokenType !== expectedType) {\n throw new Error(`Expected ${expectedType} token, got ${payload.tokenType}`);\n }\n\n return payload;\n } catch (error) {\n this.logger.error(`Failed to verify ${expectedType} token`, error);\n throw error;\n }\n }\n\n // Returns the expiry as a Date for the given token type\n getExpiryTime(type: TokenType): Date {\n return new Date(Date.now() + parseExpiryToMs(this.config.tokenExpiry[type]));\n }\n\n // Returns the token lifetime in seconds for the given type\n getExpiryInSeconds(type: TokenType): number {\n return Math.floor(parseExpiryToMs(this.config.tokenExpiry[type]) / 1000);\n }\n\n // ---- Validation ----\n\n // Decodes and validates an access token JWT\n validateAccessToken(token: string): DecodedAccessToken {\n try {\n const decoded = this.jwtService.verify<DecodedAccessToken>(token);\n\n if (decoded.tokenType !== TokenType.ACCESS) {\n throw new UnauthorizedException('Invalid token type');\n }\n\n return decoded;\n } catch (error) {\n if (error instanceof UnauthorizedException) throw error;\n\n const jwtError = error as TokenError;\n switch (jwtError.name) {\n case 'TokenExpiredError':\n throw new UnauthorizedException('Access token has expired');\n case 'JsonWebTokenError':\n throw new UnauthorizedException('Invalid access token');\n case 'NotBeforeError':\n throw new UnauthorizedException('Access token not yet valid');\n default:\n throw new UnauthorizedException('Access token validation failed');\n }\n }\n }\n\n // Decodes and validates a refresh token JWT\n validateRefreshToken(token: string): DecodedRefreshToken {\n try {\n const decoded = this.jwtService.verify<DecodedRefreshToken>(token);\n\n if (decoded.tokenType !== TokenType.REFRESH) {\n throw new UnauthorizedException('Invalid token type');\n }\n\n return decoded;\n } catch (error) {\n if (error instanceof UnauthorizedException) throw error;\n throw new UnauthorizedException('Invalid or expired session');\n }\n }\n\n // Validates that the access token is bound to the refresh token\n validateTokenBinding(accessToken: DecodedAccessToken, refreshToken: string): void {\n if (!verifyTokenHash(refreshToken, accessToken.refreshTokenHash)) {\n throw new UnauthorizedException('Session validation failed');\n }\n }\n}\n","// Parses a duration string (e.g. '10m', '1h', '30s', '7d') to milliseconds\nexport function parseExpiryToMs(expiry: string): number {\n const match = expiry.match(/^(\\d+)([smhdwy])$/);\n if (!match) throw new Error(`Invalid expiry format: ${expiry}`);\n\n const [, digits = '', unit = ''] = match;\n const value = Number.parseInt(digits, 10);\n const multipliers: Record<string, number> = {\n s: 1000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n w: 604_800_000,\n y: 31_536_000_000,\n };\n\n const multiplier = multipliers[unit];\n if (multiplier === undefined) throw new Error(`Invalid expiry format: ${expiry}`);\n\n return value * multiplier;\n}\n","import * as crypto from 'node:crypto';\n\n// Hashes a token using SHA-256\nexport function hashToken(token: string): string {\n return crypto.createHash('sha256').update(token).digest('hex');\n}\n\n// Verifies a token against its stored hash\nexport function verifyTokenHash(token: string, expectedHash: string): boolean {\n const computedHash = hashToken(token);\n if (computedHash.length !== expectedHash.length) return false;\n return crypto.timingSafeEqual(Buffer.from(computedHash, 'hex'), Buffer.from(expectedHash, 'hex'));\n}\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the bearer token from the Authorization header\nexport const AccessToken = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n const authHeader = request.headers.authorization;\n return authHeader?.replace('Bearer ', '') || '';\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the client IP from the request, working across HTTP and GraphQL transports\nexport const ClientIp = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n return getRequestFromContext(ctx).ip;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { AUTH_CONFIG_DEFAULTS } from '../auth.config';\n\n// Extracts the cookie domain from x-forwarded-host (injected by proxy), validated against baseDomain, falls back to baseDomain if invalid\nexport const CookieDomain = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n const domain = hostStr.split(':')[0] ?? hostStr;\n const baseDomain =\n request.authConfig?.cookie.refreshCookieDomain ?? AUTH_CONFIG_DEFAULTS.cookie.refreshCookieDomain ?? '';\n return domain.endsWith(`.${baseDomain}`) ? domain : baseDomain;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { AUTH_CONFIG_DEFAULTS } from '../auth.config';\n\n// Returns the configured refresh cookie name from request.authConfig\nexport const CookieName = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n return request.authConfig?.cookie.refreshCookieName ?? AUTH_CONFIG_DEFAULTS.cookie.refreshCookieName;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the request hostname from x-forwarded-host (set by reverse proxies and dev proxy) with fallback to request.hostname\nexport const Hostname = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const request = getRequestFromContext(ctx);\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n return hostStr.split(':')[0] ?? hostStr;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { UnauthorizedException } from '../../exceptions';\nimport { AUTH_CONFIG_DEFAULTS, type CookieConfig, type CookieSerializeOptions } from '../auth.config';\n\n// Builds cookie serialize options from the given cookie config\nfunction buildCookieOptionsForHost(cookieConfig: CookieConfig, hostname: string): CookieSerializeOptions {\n const baseDomain = cookieConfig.refreshCookieDomain;\n\n if (!baseDomain) {\n throw new Error('refreshCookieDomain must be configured before using @RefreshCookieOptions()');\n }\n\n if (!hostname.endsWith(`.${baseDomain}`)) {\n throw new UnauthorizedException('Invalid request host.');\n }\n\n return {\n httpOnly: true,\n secure: cookieConfig.refreshCookieSecure,\n sameSite: cookieConfig.refreshCookieSameSite,\n path: cookieConfig.refreshCookiePath,\n maxAge: cookieConfig.refreshCookieMaxAge,\n domain: hostname,\n };\n}\n\n// Returns refresh cookie options with domain scoped to the request subdomain (reads x-forwarded-host injected by proxy)\nexport const RefreshCookieOptions = createParamDecorator(\n (_data: unknown, ctx: ExecutionContext): CookieSerializeOptions => {\n const request = getRequestFromContext(ctx);\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n const domain = hostStr.split(':')[0] ?? hostStr;\n const cookieConfig: CookieConfig = request.authConfig?.cookie ?? AUTH_CONFIG_DEFAULTS.cookie;\n return buildCookieOptionsForHost(cookieConfig, domain);\n },\n);\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadGatewayException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Gateway', HttpStatus.BAD_GATEWAY);\n }\n}\n","import { HttpException, HttpStatus } from '@nestjs/common';\nimport type { FieldError } from '../types/error-response.types';\n\n// Re-export FieldError for backwards compatibility\nexport type { FieldError } from '../types/error-response.types';\n\nexport interface ProblemOptions {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\nexport abstract class HttpProblemException extends HttpException {\n constructor(detailOrOptions: string | ProblemOptions, httpStatus: HttpStatus) {\n const options = typeof detailOrOptions === 'string' ? { detail: detailOrOptions } : detailOrOptions;\n\n super(\n {\n type: options.type ?? 'about:blank',\n label: options.label,\n detail: options.detail,\n errors: options.errors ?? [],\n },\n httpStatus,\n );\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadRequestException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Request', HttpStatus.BAD_REQUEST);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ConflictException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Conflict', HttpStatus.CONFLICT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ForbiddenException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Forbidden', HttpStatus.FORBIDDEN);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class GoneException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Gone', HttpStatus.GONE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class InternalServerErrorException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Internal Server Error', HttpStatus.INTERNAL_SERVER_ERROR);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class MethodNotAllowedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Method Not Allowed', HttpStatus.METHOD_NOT_ALLOWED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotAcceptableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Acceptable', HttpStatus.NOT_ACCEPTABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotFoundException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Found', HttpStatus.NOT_FOUND);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotImplementedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Implemented', HttpStatus.NOT_IMPLEMENTED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class PayloadTooLargeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Payload Too Large', HttpStatus.PAYLOAD_TOO_LARGE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class RequestTimeoutException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Request Timeout', HttpStatus.REQUEST_TIMEOUT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ServiceUnavailableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Service Unavailable', HttpStatus.SERVICE_UNAVAILABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class TooManyRequestsException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Too Many Requests', HttpStatus.TOO_MANY_REQUESTS);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnauthorizedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unauthorized', HttpStatus.UNAUTHORIZED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnprocessableEntityException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unprocessable Entity', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnsupportedMediaTypeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unsupported Media Type', HttpStatus.UNSUPPORTED_MEDIA_TYPE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ValidationException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Validation Failed', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport { AUTH_CONFIG_DEFAULTS } from '../auth.config';\n\nexport const RefreshTokenCookie = createParamDecorator((_data: unknown, ctx: ExecutionContext): string | undefined => {\n const request = getRequestFromContext(ctx);\n const cookies = request.cookies ?? {};\n const cookieName = request.authConfig?.cookie.refreshCookieName ?? AUTH_CONFIG_DEFAULTS.cookie.refreshCookieName;\n return cookies[cookieName] as string | undefined;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport '../../types/fastify-augmentation';\n\nexport interface SessionInfo {\n userId: string;\n sessionId: string;\n sessionType: string;\n}\n\n// Returns the session's own claims from request.auth (set by VrittiAuthGuard). Session-only,\n// for the same reason as @UserId() — an app or cloud request has no session to describe.\nexport const SessionData = createParamDecorator((_data: unknown, ctx: ExecutionContext): SessionInfo => {\n const auth = getRequestFromContext(ctx).auth;\n\n if (auth?.kind !== 'session') {\n throw new Error(`No session on this request (auth: ${auth?.kind ?? 'none'}). @SessionData() is session-only.`);\n }\n\n return {\n userId: auth.userId,\n sessionId: auth.sessionId,\n sessionType: auth.sessionType,\n };\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the subdomain from the request host, resolved the same way as @Hostname() — x-forwarded-host\n// (set by dev and reverse proxies) with a fallback to request.hostname. Origin is deliberately not used:\n// it is client-controlled and can disagree with the Host header, which would mint a session for one\n// subdomain that the auth guard's host check then rejects against another.\nexport const Subdomain = createParamDecorator((_data: unknown, ctx: ExecutionContext): string | undefined => {\n const request = getRequestFromContext(ctx);\n\n const forwarded = request.headers['x-forwarded-host'];\n const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n const hostStr = raw ?? request.hostname;\n if (!hostStr) return undefined;\n\n const host = hostStr.split(':')[0] ?? hostStr;\n return host.split('.')[0] || undefined;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\n\n// Extracts the User-Agent header from the request, working across HTTP and GraphQL transports\nexport const UserAgent = createParamDecorator((_data: unknown, ctx: ExecutionContext): string | undefined => {\n const userAgent = getRequestFromContext(ctx).headers['user-agent'];\n return Array.isArray(userAgent) ? userAgent[0] : userAgent;\n});\n","import { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { getRequestFromContext } from '../../context';\nimport '../../types/fastify-augmentation';\n\n// Extracts userId from request.auth (set by VrittiAuthGuard).\n//\n// Only a session has one. An app request acts for a credential and a cloud request for the\n// control plane, so asking for a user on either is a mistake at the call site — it throws\n// rather than returning a stand-in that would be recorded as if a person had acted.\nexport const UserId = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {\n const auth = getRequestFromContext(ctx).auth;\n\n if (auth?.kind !== 'session') {\n throw new Error(`No user on this request (auth: ${auth?.kind ?? 'none'}). @UserId() is session-only.`);\n }\n\n return auth.userId;\n});\n"],"mappings":"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","names":["WORKSPACE_HEADER_ORDER","CLIENT_ID_HEADER","PARTY_ID_HEADER","MAX_CLOCK_SKEW_SECONDS","AUTH_CONFIG","Symbol","AUTH_CONFIG_DEFAULTS","cookie","refreshCookieName","refreshCookieMaxAge","refreshCookiePath","refreshCookieSecure","process","env","NODE_ENV","refreshCookieSameSite","refreshCookieDomain","guard","authHeaderName","tokenPrefix","csrfExemptSessionTypes","csrfExemptTransports","refreshTokenBindingExemptSessionTypes","TokenType","import_common","import_core","import_jwt","import_common","resolveInjectedRequest","injected","candidate","headers","undefined","req","RequestService","injectedRequest","config","request","resolveInjectedRequest","getAccessToken","authHeader","headers","guard","authHeaderName","type","token","split","tokenPrefix","getRefreshToken","cookies","refreshToken","cookie","refreshCookieName","_error","getHeader","key","getHostname","hostname","getAllHeaders","getMethod","method","getPath","url","getQuery","index","indexOf","slice","getQueryParam","URLSearchParams","get","undefined","getRawBody","rawBody","scope","Scope","REQUEST","RequestModule","providers","RequestService","exports","import_common","import_core","graphqlExtractor","getRequest","host","getArgByIndex","req","getResponse","reply","httpExtractor","getRequest","host","switchToHttp","getResponse","registry","Map","httpExtractor","graphqlExtractor","resolveExtractor","type","registry","get","httpExtractor","getRequestFromContext","host","resolveExtractor","getType","getRequest","getResponseFromContext","host","resolveExtractor","getType","getResponse","import_common","REQUIRE_AUTH_KEY","AuthType","Require","type","subtypes","SetMetadata","import_common","SKIP_CSRF_KEY","SkipCsrf","SetMetadata","import_common","parseExpiryToMs","expiry","match","Error","digits","unit","value","Number","parseInt","multipliers","s","m","h","d","w","y","multiplier","undefined","hashToken","token","createHash","update","digest","verifyTokenHash","expectedHash","computedHash","length","timingSafeEqual","Buffer","from","TokenService","logger","Logger","name","jwtService","config","generateAccessToken","sessionInfo","refreshToken","userId","sessionId","sessionType","metadata","sign","tokenType","TokenType","ACCESS","refreshTokenHash","hashToken","expiresIn","tokenExpiry","access","generateRefreshToken","REFRESH","refresh","payload","options","verify","token","expectedType","Error","error","getExpiryTime","type","Date","now","parseExpiryToMs","getExpiryInSeconds","Math","floor","validateAccessToken","decoded","UnauthorizedException","jwtError","validateRefreshToken","validateTokenBinding","accessToken","verifyTokenHash","VrittiAuthGuard","logger","Logger","name","reflector","requestService","tokenService","config","canActivate","context","request","getRequestFromContext","reply","getResponseFromContext","route","method","url","authConfig","csrfExemptTransports","guard","skipCsrf","getAllAndOverride","SKIP_CSRF_KEY","getHandler","getClass","includes","getType","requirement","REQUIRE_AUTH_KEY","type","AuthType","Session","subtypes","App","handleAppAuth","Cloud","handleCloudAuth","Public","validateCsrf","debug","isSseEndpoint","get","SSE_METADATA","handleSseAuth","sessionType","handleHttpAuth","csrfExemptSessionTypes","onAuthenticated","error","UnauthorizedException","auth","kind","requiredAppTypes","appType","undefined","length","warn","join","requiredSessionTypes","accessToken","getAccessToken","decoded","validateAccessToken","refreshTokenBindingExemptSessionTypes","refreshToken","getRefreshToken","validateTokenBinding","tokenType","_tokenType","refreshTokenHash","_hash","exp","_exp","iat","_iat","claims","userId","validateRefreshToken","safeMethods","fastifyInstance","server","csrfProtection","ForbiddenException","Promise","resolve","reject","originalSend","send","bind","Error","err","_error","errors","field","message","scope","Scope","REQUEST","mergeWithDefaults","input","tokenExpiry","cookie","AUTH_CONFIG_DEFAULTS","guard","AuthConfigModule","forRootAsync","options","module","imports","ConfigModule","RequestModule","JwtModule","registerAsync","inject","ConfigService","useFactory","config","secret","getOrThrow","signOptions","algorithm","providers","provide","Reflector","useClass","APP_GUARD","VrittiAuthGuard","AUTH_CONFIG","args","TokenService","exports","import_common","AccessToken","createParamDecorator","_data","ctx","request","getRequestFromContext","authHeader","headers","authorization","replace","import_common","ClientIp","createParamDecorator","_data","ctx","getRequestFromContext","ip","import_common","CookieDomain","createParamDecorator","_data","ctx","request","getRequestFromContext","forwarded","headers","raw","Array","isArray","hostStr","hostname","domain","split","baseDomain","authConfig","cookie","refreshCookieDomain","AUTH_CONFIG_DEFAULTS","endsWith","import_common","CookieName","createParamDecorator","_data","ctx","request","getRequestFromContext","authConfig","cookie","refreshCookieName","AUTH_CONFIG_DEFAULTS","import_common","Hostname","createParamDecorator","_data","ctx","request","getRequestFromContext","forwarded","headers","raw","Array","isArray","hostStr","hostname","split","import_common","import_common","import_common","HttpProblemException","HttpException","detailOrOptions","httpStatus","options","detail","type","label","errors","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","UnauthorizedException","HttpProblemException","detailOrOptions","HttpStatus","UNAUTHORIZED","import_common","import_common","import_common","buildCookieOptionsForHost","cookieConfig","hostname","baseDomain","refreshCookieDomain","Error","endsWith","UnauthorizedException","httpOnly","secure","refreshCookieSecure","sameSite","refreshCookieSameSite","path","refreshCookiePath","maxAge","refreshCookieMaxAge","domain","RefreshCookieOptions","createParamDecorator","_data","ctx","request","getRequestFromContext","forwarded","headers","raw","Array","isArray","hostStr","split","authConfig","cookie","AUTH_CONFIG_DEFAULTS","import_common","RefreshTokenCookie","createParamDecorator","_data","ctx","request","getRequestFromContext","cookies","cookieName","authConfig","cookie","refreshCookieName","AUTH_CONFIG_DEFAULTS","import_common","SessionData","createParamDecorator","_data","ctx","auth","getRequestFromContext","kind","Error","userId","sessionId","sessionType","import_common","Subdomain","createParamDecorator","_data","ctx","request","getRequestFromContext","forwarded","headers","raw","Array","isArray","hostStr","hostname","undefined","host","split","import_common","UserAgent","createParamDecorator","_data","ctx","userAgent","getRequestFromContext","headers","Array","isArray","import_common","UserId","createParamDecorator","_data","ctx","auth","getRequestFromContext","kind","Error","userId"]}
package/dist/auth.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { T as TokenExpiry, C as CookieConfig, G as GuardConfig, A as AuthConfig, a as TokenType, D as DecodedAccessToken, b as DecodedRefreshToken, R as RequestService } from './index.cjs';
2
- export { d as AUTH_CONFIG, e as AUTH_CONFIG_DEFAULTS, c as AccessTokenPayload, f as CookieSerializeOptions, O as OnAuthenticatedCallback, g as RefreshTokenPayload, h as TokenExpiryString } from './index.cjs';
2
+ export { c as AUTH_CONFIG, d as AUTH_CONFIG_DEFAULTS, e as AccessTokenPayload, f as CookieSerializeOptions, O as OnAuthenticatedCallback, g as RefreshTokenPayload, h as TokenExpiryString } from './index.cjs';
3
3
  import * as _nestjs_common from '@nestjs/common';
4
4
  import { InjectionToken, ModuleMetadata, DynamicModule, CanActivate, ExecutionContext } from '@nestjs/common';
5
5
  import { Reflector } from '@nestjs/core';
package/dist/auth.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { T as TokenExpiry, C as CookieConfig, G as GuardConfig, A as AuthConfig, a as TokenType, D as DecodedAccessToken, b as DecodedRefreshToken, R as RequestService } from './index.js';
2
- export { d as AUTH_CONFIG, e as AUTH_CONFIG_DEFAULTS, c as AccessTokenPayload, f as CookieSerializeOptions, O as OnAuthenticatedCallback, g as RefreshTokenPayload, h as TokenExpiryString } from './index.js';
2
+ export { c as AUTH_CONFIG, d as AUTH_CONFIG_DEFAULTS, e as AccessTokenPayload, f as CookieSerializeOptions, O as OnAuthenticatedCallback, g as RefreshTokenPayload, h as TokenExpiryString } from './index.js';
3
3
  import * as _nestjs_common from '@nestjs/common';
4
4
  import { InjectionToken, ModuleMetadata, DynamicModule, CanActivate, ExecutionContext } from '@nestjs/common';
5
5
  import { Reflector } from '@nestjs/core';