@turystack/nestjs-oauth 1.0.1

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 (42) hide show
  1. package/README.md +34 -0
  2. package/dist/cookies.d.ts +15 -0
  3. package/dist/cookies.d.ts.map +1 -0
  4. package/dist/cookies.js +57 -0
  5. package/dist/cookies.js.map +1 -0
  6. package/dist/exceptions/oauth.exceptions.d.ts +8 -0
  7. package/dist/exceptions/oauth.exceptions.d.ts.map +1 -0
  8. package/dist/exceptions/oauth.exceptions.js +14 -0
  9. package/dist/exceptions/oauth.exceptions.js.map +1 -0
  10. package/dist/index.d.ts +10 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +9 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/oauth.constants.d.ts +8 -0
  15. package/dist/oauth.constants.d.ts.map +1 -0
  16. package/dist/oauth.constants.js +8 -0
  17. package/dist/oauth.constants.js.map +1 -0
  18. package/dist/oauth.controller.d.ts +52 -0
  19. package/dist/oauth.controller.d.ts.map +1 -0
  20. package/dist/oauth.controller.js +236 -0
  21. package/dist/oauth.controller.js.map +1 -0
  22. package/dist/oauth.module.d.ts +9 -0
  23. package/dist/oauth.module.d.ts.map +1 -0
  24. package/dist/oauth.module.js +81 -0
  25. package/dist/oauth.module.js.map +1 -0
  26. package/dist/oauth.service.d.ts +23 -0
  27. package/dist/oauth.service.d.ts.map +1 -0
  28. package/dist/oauth.service.js +138 -0
  29. package/dist/oauth.service.js.map +1 -0
  30. package/dist/oauth.types.d.ts +66 -0
  31. package/dist/oauth.types.d.ts.map +1 -0
  32. package/dist/oauth.types.js +2 -0
  33. package/dist/oauth.types.js.map +1 -0
  34. package/dist/pkce.d.ts +7 -0
  35. package/dist/pkce.d.ts.map +1 -0
  36. package/dist/pkce.js +28 -0
  37. package/dist/pkce.js.map +1 -0
  38. package/dist/store/memory-code-store.d.ts +12 -0
  39. package/dist/store/memory-code-store.d.ts.map +1 -0
  40. package/dist/store/memory-code-store.js +59 -0
  41. package/dist/store/memory-code-store.js.map +1 -0
  42. package/package.json +67 -0
package/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # @turystack/nestjs-oauth
2
+
3
+ Authorization Code + PKCE authorization server for NestJS. Issues the session as
4
+ httpOnly cookies, so no token ever reaches the page.
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ pnpm add @turystack/nestjs-oauth
10
+ ```
11
+
12
+ ### Peer dependencies
13
+
14
+ The host application provides these:
15
+
16
+ ```bash
17
+ pnpm add @nestjs/common @nestjs/core @turystack/nestjs-config @turystack/nestjs-iam @turystack/nestjs-server reflect-metadata zod
18
+ ```
19
+
20
+ ## Documentation
21
+
22
+ Options, API reference and examples:
23
+
24
+ **https://tury.dev/libs/nestjs-oauth**
25
+
26
+ ## Development
27
+
28
+ ```bash
29
+ pnpm install
30
+ pnpm typecheck
31
+ pnpm check
32
+ pnpm test
33
+ pnpm build
34
+ ```
@@ -0,0 +1,15 @@
1
+ import type { OAuthCookieOptions, OAuthSession } from './oauth.types.js';
2
+ type CookieWritable = {
3
+ getHeader?: (name: string) => number | string | string[] | undefined;
4
+ setHeader: (name: string, value: string | string[]) => unknown;
5
+ };
6
+ type SerializeOptions = OAuthCookieOptions & {
7
+ maxAge: number;
8
+ };
9
+ export declare function serializeCookie(name: string, value: string, options: SerializeOptions): string;
10
+ export declare function writeSessionCookies(response: CookieWritable, session: OAuthSession, options: OAuthCookieOptions & {
11
+ refreshMaxAge: number;
12
+ }): void;
13
+ export declare function clearSessionCookies(response: CookieWritable, options: OAuthCookieOptions): void;
14
+ export {};
15
+ //# sourceMappingURL=cookies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../src/cookies.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAExE,KAAK,cAAc,GAAG;IACrB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAA;IACpE,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,OAAO,CAAA;CAC9D,CAAA;AAED,KAAK,gBAAgB,GAAG,kBAAkB,GAAG;IAE5C,MAAM,EAAE,MAAM,CAAA;CACd,CAAA;AAED,wBAAgB,eAAe,CAC9B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,gBAAgB,GACvB,MAAM,CAqBR;AAyBD,wBAAgB,mBAAmB,CAClC,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,kBAAkB,GAAG;IAC7B,aAAa,EAAE,MAAM,CAAA;CACrB,GACC,IAAI,CAwBN;AAGD,wBAAgB,mBAAmB,CAClC,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,kBAAkB,GACzB,IAAI,CAWN"}
@@ -0,0 +1,57 @@
1
+ import { DEFAULT_ACCESS_COOKIE, DEFAULT_REFRESH_COOKIE, } from './oauth.constants.js';
2
+ export function serializeCookie(name, value, options) {
3
+ const parts = [
4
+ `${name}=${encodeURIComponent(value)}`,
5
+ `Path=${options.path ?? '/'}`,
6
+ `Max-Age=${Math.max(0, Math.floor(options.maxAge))}`,
7
+ `SameSite=${(options.sameSite ?? 'lax').replace(/^./u, (first) => first.toUpperCase())}`,
8
+ 'HttpOnly',
9
+ ];
10
+ if (options.domain) {
11
+ parts.push(`Domain=${options.domain}`);
12
+ }
13
+ if (options.secure !== false) {
14
+ parts.push('Secure');
15
+ }
16
+ return parts.join('; ');
17
+ }
18
+ function append(response, cookies) {
19
+ const existing = response.getHeader?.('Set-Cookie');
20
+ const previous = Array.isArray(existing)
21
+ ? existing
22
+ : typeof existing === 'string'
23
+ ? [
24
+ existing,
25
+ ]
26
+ : [];
27
+ response.setHeader('Set-Cookie', [
28
+ ...previous,
29
+ ...cookies,
30
+ ]);
31
+ }
32
+ export function writeSessionCookies(response, session, options) {
33
+ const accessMaxAge = Math.max(0, Math.floor((session.expiresAt - Date.now()) / 1000));
34
+ append(response, [
35
+ serializeCookie(options.accessName ?? DEFAULT_ACCESS_COOKIE, session.accessToken, {
36
+ ...options,
37
+ maxAge: accessMaxAge,
38
+ }),
39
+ serializeCookie(options.refreshName ?? DEFAULT_REFRESH_COOKIE, session.refreshToken, {
40
+ ...options,
41
+ maxAge: options.refreshMaxAge,
42
+ }),
43
+ ]);
44
+ }
45
+ export function clearSessionCookies(response, options) {
46
+ append(response, [
47
+ serializeCookie(options.accessName ?? DEFAULT_ACCESS_COOKIE, '', {
48
+ ...options,
49
+ maxAge: 0,
50
+ }),
51
+ serializeCookie(options.refreshName ?? DEFAULT_REFRESH_COOKIE, '', {
52
+ ...options,
53
+ maxAge: 0,
54
+ }),
55
+ ]);
56
+ }
57
+ //# sourceMappingURL=cookies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookies.js","sourceRoot":"","sources":["../src/cookies.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,qBAAqB,EACrB,sBAAsB,GACtB,MAAM,sBAAsB,CAAA;AAa7B,MAAM,UAAU,eAAe,CAC9B,IAAY,EACZ,KAAa,EACb,OAAyB;IAEzB,MAAM,KAAK,GAAG;QACb,GAAG,IAAI,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE;QACtC,QAAQ,OAAO,CAAC,IAAI,IAAI,GAAG,EAAE;QAC7B,WAAW,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE;QACpD,YAAY,CAAC,OAAO,CAAC,QAAQ,IAAI,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,EAAE;QAIxF,UAAU;KACV,CAAA;IAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,UAAU,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IACvC,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IACrB,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACxB,CAAC;AAED,SAAS,MAAM,CAAC,QAAwB,EAAE,OAAiB;IAC1D,MAAM,QAAQ,GAAG,QAAQ,CAAC,SAAS,EAAE,CAAC,YAAY,CAAC,CAAA;IACnD,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;QACvC,CAAC,CAAC,QAAQ;QACV,CAAC,CAAC,OAAO,QAAQ,KAAK,QAAQ;YAC7B,CAAC,CAAC;gBACA,QAAQ;aACR;YACF,CAAC,CAAC,EAAE,CAAA;IAEN,QAAQ,CAAC,SAAS,CAAC,YAAY,EAAE;QAChC,GAAG,QAAQ;QACX,GAAG,OAAO;KACV,CAAC,CAAA;AACH,CAAC;AASD,MAAM,UAAU,mBAAmB,CAClC,QAAwB,EACxB,OAAqB,EACrB,OAEC;IAED,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAC5B,CAAC,EACD,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CACnD,CAAA;IAED,MAAM,CAAC,QAAQ,EAAE;QAChB,eAAe,CACd,OAAO,CAAC,UAAU,IAAI,qBAAqB,EAC3C,OAAO,CAAC,WAAW,EACnB;YACC,GAAG,OAAO;YACV,MAAM,EAAE,YAAY;SACpB,CACD;QACD,eAAe,CACd,OAAO,CAAC,WAAW,IAAI,sBAAsB,EAC7C,OAAO,CAAC,YAAY,EACpB;YACC,GAAG,OAAO;YACV,MAAM,EAAE,OAAO,CAAC,aAAa;SAC7B,CACD;KACD,CAAC,CAAA;AACH,CAAC;AAGD,MAAM,UAAU,mBAAmB,CAClC,QAAwB,EACxB,OAA2B;IAE3B,MAAM,CAAC,QAAQ,EAAE;QAChB,eAAe,CAAC,OAAO,CAAC,UAAU,IAAI,qBAAqB,EAAE,EAAE,EAAE;YAChE,GAAG,OAAO;YACV,MAAM,EAAE,CAAC;SACT,CAAC;QACF,eAAe,CAAC,OAAO,CAAC,WAAW,IAAI,sBAAsB,EAAE,EAAE,EAAE;YAClE,GAAG,OAAO;YACV,MAAM,EAAE,CAAC;SACT,CAAC;KACF,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { BadRequestException, UnauthorizedException } from '@nestjs/common';
2
+ export declare class OAuthInvalidRequestException extends BadRequestException {
3
+ constructor(message?: string);
4
+ }
5
+ export declare class OAuthInvalidGrantException extends UnauthorizedException {
6
+ constructor(message?: string);
7
+ }
8
+ //# sourceMappingURL=oauth.exceptions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.exceptions.d.ts","sourceRoot":"","sources":["../../src/exceptions/oauth.exceptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAU3E,qBAAa,4BAA6B,SAAQ,mBAAmB;IACpE,YAAY,OAAO,SAAmC,EAGrD;CACD;AAGD,qBAAa,0BAA2B,SAAQ,qBAAqB;IACpE,YAAY,OAAO,SAA8B,EAGhD;CACD"}
@@ -0,0 +1,14 @@
1
+ import { BadRequestException, UnauthorizedException } from '@nestjs/common';
2
+ export class OAuthInvalidRequestException extends BadRequestException {
3
+ constructor(message = 'Invalid authorization request.') {
4
+ super(message);
5
+ this.name = 'OAuthInvalidRequestException';
6
+ }
7
+ }
8
+ export class OAuthInvalidGrantException extends UnauthorizedException {
9
+ constructor(message = 'Invalid or expired grant.') {
10
+ super(message);
11
+ this.name = 'OAuthInvalidGrantException';
12
+ }
13
+ }
14
+ //# sourceMappingURL=oauth.exceptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.exceptions.js","sourceRoot":"","sources":["../../src/exceptions/oauth.exceptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAU3E,MAAM,OAAO,4BAA6B,SAAQ,mBAAmB;IACpE,YAAY,OAAO,GAAG,gCAAgC;QACrD,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,8BAA8B,CAAA;IAC3C,CAAC;CACD;AAGD,MAAM,OAAO,0BAA2B,SAAQ,qBAAqB;IACpE,YAAY,OAAO,GAAG,2BAA2B;QAChD,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAA;IACzC,CAAC;CACD"}
@@ -0,0 +1,10 @@
1
+ export { clearSessionCookies, serializeCookie, writeSessionCookies, } from './cookies.js';
2
+ export { DEFAULT_ACCESS_COOKIE, DEFAULT_CODE_TTL, DEFAULT_REFRESH_COOKIE, DEFAULT_REFRESH_TTL, DEFAULT_TRANSACTION_TTL, OAUTH_CODE_STORE, OAUTH_OPTIONS, } from './oauth.constants.js';
3
+ export { OAuthController } from './oauth.controller.js';
4
+ export { OAuthModule } from './oauth.module.js';
5
+ export { OAuthService } from './oauth.service.js';
6
+ export type { OAuthAuthorizationCode, OAuthAuthorizeRequest, OAuthClient, OAuthCodeStore, OAuthCookieOptions, OAuthModuleFactoryOptions, OAuthModuleOptions, OAuthOptions, OAuthSession, OAuthTransaction, } from './oauth.types.js';
7
+ export { CODE_CHALLENGE_METHOD, deriveChallenge, isValidCodeChallenge, isValidCodeVerifier, randomToken, verifyChallenge, } from './pkce.js';
8
+ export { OAuthInvalidGrantException, OAuthInvalidRequestException, } from './exceptions/oauth.exceptions.js';
9
+ export { MemoryOAuthCodeStore } from './store/memory-code-store.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,mBAAmB,EACnB,eAAe,EACf,mBAAmB,GACnB,MAAM,cAAc,CAAA;AACrB,OAAO,EACN,qBAAqB,EACrB,gBAAgB,EAChB,sBAAsB,EACtB,mBAAmB,EACnB,uBAAuB,EACvB,gBAAgB,EAChB,aAAa,GACb,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,YAAY,EACX,sBAAsB,EACtB,qBAAqB,EACrB,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,yBAAyB,EACzB,kBAAkB,EAClB,YAAY,EACZ,YAAY,EACZ,gBAAgB,GAChB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACN,qBAAqB,EACrB,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,WAAW,EACX,eAAe,GACf,MAAM,WAAW,CAAA;AAElB,OAAO,EACN,0BAA0B,EAC1B,4BAA4B,GAC5B,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export { clearSessionCookies, serializeCookie, writeSessionCookies, } from './cookies.js';
2
+ export { DEFAULT_ACCESS_COOKIE, DEFAULT_CODE_TTL, DEFAULT_REFRESH_COOKIE, DEFAULT_REFRESH_TTL, DEFAULT_TRANSACTION_TTL, OAUTH_CODE_STORE, OAUTH_OPTIONS, } from './oauth.constants.js';
3
+ export { OAuthController } from './oauth.controller.js';
4
+ export { OAuthModule } from './oauth.module.js';
5
+ export { OAuthService } from './oauth.service.js';
6
+ export { CODE_CHALLENGE_METHOD, deriveChallenge, isValidCodeChallenge, isValidCodeVerifier, randomToken, verifyChallenge, } from './pkce.js';
7
+ export { OAuthInvalidGrantException, OAuthInvalidRequestException, } from './exceptions/oauth.exceptions.js';
8
+ export { MemoryOAuthCodeStore } from './store/memory-code-store.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,mBAAmB,EACnB,eAAe,EACf,mBAAmB,GACnB,MAAM,cAAc,CAAA;AACrB,OAAO,EACN,qBAAqB,EACrB,gBAAgB,EAChB,sBAAsB,EACtB,mBAAmB,EACnB,uBAAuB,EACvB,gBAAgB,EAChB,aAAa,GACb,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAajD,OAAO,EACN,qBAAqB,EACrB,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,WAAW,EACX,eAAe,GACf,MAAM,WAAW,CAAA;AAElB,OAAO,EACN,0BAA0B,EAC1B,4BAA4B,GAC5B,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA"}
@@ -0,0 +1,8 @@
1
+ export declare const OAUTH_OPTIONS: unique symbol;
2
+ export declare const OAUTH_CODE_STORE: unique symbol;
3
+ export declare const DEFAULT_CODE_TTL = 60;
4
+ export declare const DEFAULT_TRANSACTION_TTL = 600;
5
+ export declare const DEFAULT_REFRESH_TTL = 604800;
6
+ export declare const DEFAULT_ACCESS_COOKIE = "session";
7
+ export declare const DEFAULT_REFRESH_COOKIE = "refresh";
8
+ //# sourceMappingURL=oauth.constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.constants.d.ts","sourceRoot":"","sources":["../src/oauth.constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,eAA0B,CAAA;AACpD,eAAO,MAAM,gBAAgB,eAA6B,CAAA;AAG1D,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAGlC,eAAO,MAAM,uBAAuB,MAAM,CAAA;AAG1C,eAAO,MAAM,mBAAmB,SAAS,CAAA;AAEzC,eAAO,MAAM,qBAAqB,YAAY,CAAA;AAC9C,eAAO,MAAM,sBAAsB,YAAY,CAAA"}
@@ -0,0 +1,8 @@
1
+ export const OAUTH_OPTIONS = Symbol('OAUTH_OPTIONS');
2
+ export const OAUTH_CODE_STORE = Symbol('OAUTH_CODE_STORE');
3
+ export const DEFAULT_CODE_TTL = 60;
4
+ export const DEFAULT_TRANSACTION_TTL = 600;
5
+ export const DEFAULT_REFRESH_TTL = 604800;
6
+ export const DEFAULT_ACCESS_COOKIE = 'session';
7
+ export const DEFAULT_REFRESH_COOKIE = 'refresh';
8
+ //# sourceMappingURL=oauth.constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.constants.js","sourceRoot":"","sources":["../src/oauth.constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,eAAe,CAAC,CAAA;AACpD,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,kBAAkB,CAAC,CAAA;AAG1D,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAA;AAGlC,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,CAAA;AAG1C,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAA;AAEzC,MAAM,CAAC,MAAM,qBAAqB,GAAG,SAAS,CAAA;AAC9C,MAAM,CAAC,MAAM,sBAAsB,GAAG,SAAS,CAAA"}
@@ -0,0 +1,52 @@
1
+ import { z } from 'zod';
2
+ import { OAuthService } from './oauth.service.js';
3
+ import type { OAuthOptions } from './oauth.types.js';
4
+ declare const authorizeQuery: z.ZodObject<{
5
+ client_id: z.ZodString;
6
+ code_challenge: z.ZodString;
7
+ code_challenge_method: z.ZodString;
8
+ redirect_uri: z.ZodString;
9
+ scope: z.ZodOptional<z.ZodString>;
10
+ state: z.ZodOptional<z.ZodString>;
11
+ }, z.core.$strip>;
12
+ declare const transactionQuery: z.ZodObject<{
13
+ tx: z.ZodString;
14
+ }, z.core.$strip>;
15
+ declare const transactionResponse: z.ZodObject<{
16
+ clientId: z.ZodString;
17
+ redirectUri: z.ZodString;
18
+ scopes: z.ZodOptional<z.ZodArray<z.ZodString>>;
19
+ }, z.core.$strip>;
20
+ declare const tokenBody: z.ZodObject<{
21
+ client_id: z.ZodString;
22
+ code: z.ZodString;
23
+ code_verifier: z.ZodString;
24
+ redirect_uri: z.ZodString;
25
+ }, z.core.$strip>;
26
+ declare const sessionResponse: z.ZodObject<{
27
+ expiresAt: z.ZodNumber;
28
+ }, z.core.$strip>;
29
+ type ResponseLike = {
30
+ getHeader?: (name: string) => number | string | string[] | undefined;
31
+ redirect: (url: string) => void;
32
+ setHeader: (name: string, value: string | string[]) => unknown;
33
+ status: (code: number) => ResponseLike;
34
+ send: (body?: unknown) => unknown;
35
+ };
36
+ type RequestLike = {
37
+ cookies?: Record<string, string | undefined>;
38
+ headers?: Record<string, string | string[] | undefined>;
39
+ };
40
+ export declare class OAuthController {
41
+ private readonly options;
42
+ private readonly oauth;
43
+ constructor(options: OAuthOptions, oauth: OAuthService);
44
+ authorize(query: z.infer<typeof authorizeQuery>, response: ResponseLike): Promise<void>;
45
+ transaction(query: z.infer<typeof transactionQuery>): Promise<z.infer<typeof transactionResponse>>;
46
+ token(body: z.infer<typeof tokenBody>, response: ResponseLike): Promise<z.infer<typeof sessionResponse>>;
47
+ refresh(request: RequestLike, response: ResponseLike): Promise<z.infer<typeof sessionResponse>>;
48
+ signOut(response: ResponseLike): Promise<void>;
49
+ private deliver;
50
+ }
51
+ export {};
52
+ //# sourceMappingURL=oauth.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.controller.d.ts","sourceRoot":"","sources":["../src/oauth.controller.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAQvB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,KAAK,EAAE,YAAY,EAAgB,MAAM,kBAAkB,CAAA;AAIlE,QAAA,MAAM,cAAc;;;;;;;iBAOlB,CAAA;AAEF,QAAA,MAAM,gBAAgB;;iBAEpB,CAAA;AAEF,QAAA,MAAM,mBAAmB;;;;iBAIvB,CAAA;AAEF,QAAA,MAAM,SAAS;;;;;iBAKb,CAAA;AAUF,QAAA,MAAM,eAAe;;iBAEnB,CAAA;AAEF,KAAK,YAAY,GAAG;IACnB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAA;IACpE,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,OAAO,CAAA;IAC9D,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,YAAY,CAAA;IACtC,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,KAAK,OAAO,CAAA;CACjC,CAAA;AAED,KAAK,WAAW,GAAG;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;CACvD,CAAA;AAyCD,qBAIa,eAAe;IAG1B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAExB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAJvB,YAEkB,OAAO,EAAE,YAAY,EAErB,KAAK,EAAE,YAAY,EACjC;IAYE,SAAS,CACL,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,EACvC,QAAQ,EAAE,YAAY,GAC3B,OAAO,CAAC,IAAI,CAAC,CAmBf;IAeK,WAAW,CACP,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,GAC9C,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAY9C;IAeK,KAAK,CACF,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,EAIvC,QAAQ,EAAE,YAAY,GACpB,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC,CAS1C;IAYK,OAAO,CACL,OAAO,EAAE,WAAW,EAI3B,QAAQ,EAAE,YAAY,GACpB,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC,CAW1C;IAcK,OAAO,CAIZ,QAAQ,EAAE,YAAY,GACpB,OAAO,CAAC,IAAI,CAAC,CAEf;IAED,OAAO,CAAC,OAAO;CAef"}
@@ -0,0 +1,236 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ 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;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { Body, Inject, Query, Req, Res } from '@nestjs/common';
14
+ import { Controller, Route } from '@turystack/nestjs-server';
15
+ import { z } from 'zod';
16
+ import { clearSessionCookies, writeSessionCookies } from './cookies.js';
17
+ import { DEFAULT_REFRESH_COOKIE, DEFAULT_REFRESH_TTL, OAUTH_OPTIONS, } from './oauth.constants.js';
18
+ import { OAuthService } from './oauth.service.js';
19
+ import { OAuthInvalidGrantException } from './exceptions/oauth.exceptions.js';
20
+ const authorizeQuery = z.object({
21
+ client_id: z.string().min(1),
22
+ code_challenge: z.string().min(1),
23
+ code_challenge_method: z.string().min(1),
24
+ redirect_uri: z.string().url(),
25
+ scope: z.string().optional(),
26
+ state: z.string().optional(),
27
+ });
28
+ const transactionQuery = z.object({
29
+ tx: z.string().min(1),
30
+ });
31
+ const transactionResponse = z.object({
32
+ clientId: z.string(),
33
+ redirectUri: z.string(),
34
+ scopes: z.array(z.string()).optional(),
35
+ });
36
+ const tokenBody = z.object({
37
+ client_id: z.string().min(1),
38
+ code: z.string().min(1),
39
+ code_verifier: z.string().min(1),
40
+ redirect_uri: z.string().url(),
41
+ });
42
+ const sessionResponse = z.object({
43
+ expiresAt: z.number(),
44
+ });
45
+ function readCookie(request, name) {
46
+ const parsed = request.cookies?.[name];
47
+ if (parsed) {
48
+ return parsed;
49
+ }
50
+ const header = request.headers?.cookie;
51
+ if (typeof header !== 'string') {
52
+ return undefined;
53
+ }
54
+ for (const part of header.split(';')) {
55
+ const separator = part.indexOf('=');
56
+ if (separator === -1) {
57
+ continue;
58
+ }
59
+ if (part.slice(0, separator).trim() !== name) {
60
+ continue;
61
+ }
62
+ const value = part.slice(separator + 1).trim();
63
+ return value.length > 0 ? decodeURIComponent(value) : undefined;
64
+ }
65
+ return undefined;
66
+ }
67
+ let OAuthController = class OAuthController {
68
+ options;
69
+ oauth;
70
+ constructor(options, oauth) {
71
+ this.options = options;
72
+ this.oauth = oauth;
73
+ }
74
+ async authorize(query, response) {
75
+ const target = await this.oauth.authorize({
76
+ clientId: query.client_id,
77
+ codeChallenge: query.code_challenge,
78
+ codeChallengeMethod: query.code_challenge_method,
79
+ redirectUri: query.redirect_uri,
80
+ ...(query.scope === undefined
81
+ ? {}
82
+ : {
83
+ scope: query.scope,
84
+ }),
85
+ ...(query.state === undefined
86
+ ? {}
87
+ : {
88
+ state: query.state,
89
+ }),
90
+ });
91
+ response.redirect(target);
92
+ }
93
+ async transaction(query) {
94
+ const transaction = await this.oauth.readTransaction(query.tx);
95
+ return {
96
+ clientId: transaction.clientId,
97
+ redirectUri: transaction.redirectUri,
98
+ ...(transaction.scopes === undefined
99
+ ? {}
100
+ : {
101
+ scopes: transaction.scopes,
102
+ }),
103
+ };
104
+ }
105
+ async token(body, response) {
106
+ const session = await this.oauth.exchangeCode({
107
+ clientId: body.client_id,
108
+ code: body.code,
109
+ codeVerifier: body.code_verifier,
110
+ redirectUri: body.redirect_uri,
111
+ });
112
+ return this.deliver(response, session);
113
+ }
114
+ async refresh(request, response) {
115
+ const refreshToken = readCookie(request, this.options.cookie?.refreshName ?? DEFAULT_REFRESH_COOKIE);
116
+ if (!refreshToken) {
117
+ throw new OAuthInvalidGrantException('No session to refresh.');
118
+ }
119
+ return this.deliver(response, await this.oauth.refresh(refreshToken));
120
+ }
121
+ async signOut(response) {
122
+ clearSessionCookies(response, this.options.cookie ?? {});
123
+ }
124
+ deliver(response, session) {
125
+ writeSessionCookies(response, session, {
126
+ ...(this.options.cookie ?? {}),
127
+ refreshMaxAge: this.options.refreshTtl ?? DEFAULT_REFRESH_TTL,
128
+ });
129
+ return {
130
+ expiresAt: session.expiresAt,
131
+ };
132
+ }
133
+ };
134
+ __decorate([
135
+ Route({
136
+ description: 'Validates the client, the redirect target and the PKCE challenge, then sends the browser to the sign-in application.',
137
+ method: 'GET',
138
+ parameters: {
139
+ query: authorizeQuery,
140
+ },
141
+ path: 'authorize',
142
+ summary: 'Start an authorization',
143
+ }),
144
+ __param(0, Query()),
145
+ __param(1, Res()),
146
+ __metadata("design:type", Function),
147
+ __metadata("design:paramtypes", [Object, Object]),
148
+ __metadata("design:returntype", Promise)
149
+ ], OAuthController.prototype, "authorize", null);
150
+ __decorate([
151
+ Route({
152
+ description: 'What the sign-in application shows: which client is asking, and where the browser goes next.',
153
+ method: 'GET',
154
+ parameters: {
155
+ query: transactionQuery,
156
+ },
157
+ path: 'transaction',
158
+ responses: {
159
+ 200: transactionResponse,
160
+ },
161
+ summary: 'Read a pending authorization',
162
+ }),
163
+ __param(0, Query()),
164
+ __metadata("design:type", Function),
165
+ __metadata("design:paramtypes", [Object]),
166
+ __metadata("design:returntype", Promise)
167
+ ], OAuthController.prototype, "transaction", null);
168
+ __decorate([
169
+ Route({
170
+ description: 'Exchanges a single-use authorization code for a session, delivered as httpOnly cookies.',
171
+ method: 'POST',
172
+ parameters: {
173
+ body: tokenBody,
174
+ },
175
+ path: 'token',
176
+ responses: {
177
+ 200: sessionResponse,
178
+ },
179
+ summary: 'Exchange an authorization code',
180
+ }),
181
+ __param(0, Body()),
182
+ __param(1, Res({
183
+ passthrough: true,
184
+ })),
185
+ __metadata("design:type", Function),
186
+ __metadata("design:paramtypes", [Object, Object]),
187
+ __metadata("design:returntype", Promise)
188
+ ], OAuthController.prototype, "token", null);
189
+ __decorate([
190
+ Route({
191
+ description: 'Rotates the refresh cookie into a new session. The browser sends the cookie on its own.',
192
+ method: 'POST',
193
+ path: 'refresh',
194
+ responses: {
195
+ 200: sessionResponse,
196
+ },
197
+ summary: 'Refresh the session',
198
+ }),
199
+ __param(0, Req()),
200
+ __param(1, Res({
201
+ passthrough: true,
202
+ })),
203
+ __metadata("design:type", Function),
204
+ __metadata("design:paramtypes", [Object, Object]),
205
+ __metadata("design:returntype", Promise)
206
+ ], OAuthController.prototype, "refresh", null);
207
+ __decorate([
208
+ Route({
209
+ description: 'Expires both session cookies in the browser. Signing out has to reach the browser, not only the server.',
210
+ method: 'POST',
211
+ path: 'sign-out',
212
+ responses: {
213
+ 204: {
214
+ description: 'The session cookies were expired.',
215
+ },
216
+ },
217
+ summary: 'Sign out',
218
+ }),
219
+ __param(0, Res({
220
+ passthrough: true,
221
+ })),
222
+ __metadata("design:type", Function),
223
+ __metadata("design:paramtypes", [Object]),
224
+ __metadata("design:returntype", Promise)
225
+ ], OAuthController.prototype, "signOut", null);
226
+ OAuthController = __decorate([
227
+ Controller({
228
+ prefix: 'auth',
229
+ tag: 'Auth',
230
+ }),
231
+ __param(0, Inject(OAUTH_OPTIONS)),
232
+ __param(1, Inject(OAuthService)),
233
+ __metadata("design:paramtypes", [Object, OAuthService])
234
+ ], OAuthController);
235
+ export { OAuthController };
236
+ //# sourceMappingURL=oauth.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.controller.js","sourceRoot":"","sources":["../src/oauth.controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { type DynamicModule } from '@nestjs/common';
2
+ import { ConfigService } from '@turystack/nestjs-config';
3
+ import type { OAuthModuleFactoryOptions, OAuthModuleOptions } from './oauth.types.js';
4
+ export declare class OAuthModule {
5
+ static register(options: OAuthModuleOptions | ((config: ConfigService) => OAuthModuleFactoryOptions)): DynamicModule;
6
+ private static _resolveOptions;
7
+ private static _resolveProviders;
8
+ }
9
+ //# sourceMappingURL=oauth.module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.module.d.ts","sourceRoot":"","sources":["../src/oauth.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAyB,MAAM,gBAAgB,CAAA;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAKxD,OAAO,KAAK,EACX,yBAAyB,EACzB,kBAAkB,EAElB,MAAM,kBAAkB,CAAA;AAMzB,qBACa,WAAW;IAUvB,MAAM,CAAC,QAAQ,CACd,OAAO,EACJ,kBAAkB,GAClB,CAAC,CAAC,MAAM,EAAE,aAAa,KAAK,yBAAyB,CAAC,GACvD,aAAa,CAef;IAED,OAAO,CAAC,MAAM,CAAC,eAAe;IAmB9B,OAAO,CAAC,MAAM,CAAC,iBAAiB;CA2ChC"}
@@ -0,0 +1,81 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ 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;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var OAuthModule_1;
8
+ import { Module } from '@nestjs/common';
9
+ import { ConfigService } from '@turystack/nestjs-config';
10
+ import { OAUTH_CODE_STORE, OAUTH_OPTIONS } from './oauth.constants.js';
11
+ import { OAuthController } from './oauth.controller.js';
12
+ import { OAuthService } from './oauth.service.js';
13
+ import { MemoryOAuthCodeStore } from './store/memory-code-store.js';
14
+ const OAUTH_RESOLVED_OPTIONS = Symbol('OAUTH_RESOLVED_OPTIONS');
15
+ let OAuthModule = OAuthModule_1 = class OAuthModule {
16
+ static register(options) {
17
+ return {
18
+ controllers: [
19
+ OAuthController,
20
+ ],
21
+ exports: [
22
+ OAUTH_OPTIONS,
23
+ OAUTH_CODE_STORE,
24
+ OAuthService,
25
+ ],
26
+ global: true,
27
+ imports: typeof options === 'function' ? [] : (options.imports ?? []),
28
+ module: OAuthModule_1,
29
+ providers: OAuthModule_1._resolveProviders(options),
30
+ };
31
+ }
32
+ static _resolveOptions(optionsOrFactory, config) {
33
+ if (typeof optionsOrFactory !== 'function') {
34
+ return optionsOrFactory;
35
+ }
36
+ if (!config) {
37
+ throw new Error('[OAuthModule] register((config) => ...) requires ConfigModule (@turystack/nestjs-config) to be registered');
38
+ }
39
+ return optionsOrFactory(config);
40
+ }
41
+ static _resolveProviders(optionsOrFactory) {
42
+ const storeProvider = typeof optionsOrFactory !== 'function' && optionsOrFactory.store
43
+ ? {
44
+ provide: OAUTH_CODE_STORE,
45
+ useClass: optionsOrFactory.store,
46
+ }
47
+ : {
48
+ provide: OAUTH_CODE_STORE,
49
+ useClass: MemoryOAuthCodeStore,
50
+ };
51
+ return [
52
+ {
53
+ inject: [
54
+ {
55
+ optional: true,
56
+ token: ConfigService,
57
+ },
58
+ ],
59
+ provide: OAUTH_RESOLVED_OPTIONS,
60
+ useFactory: (config) => OAuthModule_1._resolveOptions(optionsOrFactory, config),
61
+ },
62
+ {
63
+ inject: [
64
+ OAUTH_RESOLVED_OPTIONS,
65
+ ],
66
+ provide: OAUTH_OPTIONS,
67
+ useFactory: (options) => {
68
+ const { imports, store, ...oauthOptions } = options;
69
+ return oauthOptions;
70
+ },
71
+ },
72
+ storeProvider,
73
+ OAuthService,
74
+ ];
75
+ }
76
+ };
77
+ OAuthModule = OAuthModule_1 = __decorate([
78
+ Module({})
79
+ ], OAuthModule);
80
+ export { OAuthModule };
81
+ //# sourceMappingURL=oauth.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.module.js","sourceRoot":"","sources":["../src/oauth.module.ts"],"names":[],"mappings":";;;;;;;AAAA,OAAO,EAAsB,MAAM,EAAiB,MAAM,gBAAgB,CAAA;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAExD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAOjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AAEnE,MAAM,sBAAsB,GAAG,MAAM,CAAC,wBAAwB,CAAC,CAAA;AAGxD,IAAM,WAAW,mBAAjB,MAAM,WAAW;IAUhB,AAAP,MAAM,CAAC,QAAQ,CACd,OAEyD;QAEzD,OAAO;YACN,WAAW,EAAE;gBACZ,eAAe;aACf;YACD,OAAO,EAAE;gBACR,aAAa;gBACb,gBAAgB;gBAChB,YAAY;aACZ;YACD,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;YACrE,MAAM,eAAa;YACnB,SAAS,EAAE,cAAY,iBAAiB,CAAC,OAAO,CAAC;SACjD,CAAA;IACF,CAAC;IAEO,MAAM,CAAC,eAAe,CAC7B,gBAEyD,EACzD,MAAsB;QAEtB,IAAI,OAAO,gBAAgB,KAAK,UAAU,EAAE,CAAC;YAC5C,OAAO,gBAAgB,CAAA;QACxB,CAAC;QAED,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACd,2GAA2G,CAC3G,CAAA;QACF,CAAC;QAED,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAA;IAChC,CAAC;IAEc,AAAP,MAAM,CAAC,iBAAiB,CAC/B,gBAEyD;QAEzD,MAAM,aAAa,GAClB,OAAO,gBAAgB,KAAK,UAAU,IAAI,gBAAgB,CAAC,KAAK;YAC/D,CAAC,CAAC;gBACA,OAAO,EAAE,gBAAgB;gBACzB,QAAQ,EAAE,gBAAgB,CAAC,KAAK;aAChC;YACF,CAAC,CAAC;gBACA,OAAO,EAAE,gBAAgB;gBACzB,QAAQ,EAAE,oBAAoB;aAC9B,CAAA;QAEJ,OAAO;YACN;gBACC,MAAM,EAAE;oBACP;wBACC,QAAQ,EAAE,IAAI;wBACd,KAAK,EAAE,aAAa;qBACpB;iBACD;gBACD,OAAO,EAAE,sBAAsB;gBAC/B,UAAU,EAAE,CAAC,MAAsB,EAAE,EAAE,CACtC,cAAY,eAAe,CAAC,gBAAgB,EAAE,MAAM,CAAC;aACtD;YACD;gBACC,MAAM,EAAE;oBACP,sBAAsB;iBACtB;gBACD,OAAO,EAAE,aAAa;gBACtB,UAAU,EAAE,CAAC,OAA2B,EAAgB,EAAE;oBACzD,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,YAAY,EAAE,GAAG,OAAO,CAAA;oBAEnD,OAAO,YAAY,CAAA;gBACpB,CAAC;aACD;YACD,aAAa;YACb,YAAY;SACZ,CAAA;IACF,CAAC;CACD,CAAA;AA7FY,WAAW;IADvB,MAAM,CAAC,EAAE,CAAC;GACE,WAAW,CA6FvB"}
@@ -0,0 +1,23 @@
1
+ import { IamTokenService } from '@turystack/nestjs-iam';
2
+ import type { OAuthAuthorizeRequest, OAuthCodeStore, OAuthOptions, OAuthSession, OAuthTransaction } from './oauth.types.js';
3
+ export declare class OAuthService {
4
+ private readonly options;
5
+ private readonly store;
6
+ private readonly tokens;
7
+ constructor(options: OAuthOptions, store: OAuthCodeStore, tokens: IamTokenService);
8
+ authorize(request: OAuthAuthorizeRequest): Promise<string>;
9
+ readTransaction(id: string): Promise<OAuthTransaction>;
10
+ completeAuthorization(transactionId: string, userId: string, options?: {
11
+ workspaceId?: string;
12
+ }): Promise<string>;
13
+ exchangeCode(input: {
14
+ clientId: string;
15
+ code: string;
16
+ codeVerifier: string;
17
+ redirectUri: string;
18
+ }): Promise<OAuthSession>;
19
+ refresh(refreshToken: string): Promise<OAuthSession>;
20
+ private issue;
21
+ private findClient;
22
+ }
23
+ //# sourceMappingURL=oauth.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.service.d.ts","sourceRoot":"","sources":["../src/oauth.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAQvD,OAAO,KAAK,EACX,qBAAqB,EAErB,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,MAAM,kBAAkB,CAAA;AAuBzB,qBACa,YAAY;IAGvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAExB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAKtB,OAAO,CAAC,QAAQ,CAAC,MAAM;IATxB,YAEkB,OAAO,EAAE,YAAY,EAErB,KAAK,EAAE,cAAc,EAKrB,MAAM,EAAE,eAAe,EACrC;IASE,SAAS,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CA6C/D;IAGK,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAU3D;IAMK,qBAAqB,CAC1B,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;QACT,WAAW,CAAC,EAAE,MAAM,CAAA;KACpB,GACC,OAAO,CAAC,MAAM,CAAC,CA6BjB;IAQK,YAAY,CAAC,KAAK,EAAE;QACzB,QAAQ,EAAE,MAAM,CAAA;QAChB,IAAI,EAAE,MAAM,CAAA;QACZ,YAAY,EAAE,MAAM,CAAA;QACpB,WAAW,EAAE,MAAM,CAAA;KACnB,GAAG,OAAO,CAAC,YAAY,CAAC,CAmBxB;IAGK,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAKzD;YAEa,KAAK;IAoBnB,OAAO,CAAC,UAAU;CAWlB"}
@@ -0,0 +1,138 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ 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;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { Inject, Injectable } from '@nestjs/common';
14
+ import { IamTokenService } from '@turystack/nestjs-iam';
15
+ import { DEFAULT_CODE_TTL, DEFAULT_TRANSACTION_TTL, OAUTH_CODE_STORE, OAUTH_OPTIONS, } from './oauth.constants.js';
16
+ import { CODE_CHALLENGE_METHOD, isValidCodeChallenge, randomToken, verifyChallenge, } from './pkce.js';
17
+ import { OAuthInvalidGrantException, OAuthInvalidRequestException, } from './exceptions/oauth.exceptions.js';
18
+ let OAuthService = class OAuthService {
19
+ options;
20
+ store;
21
+ tokens;
22
+ constructor(options, store, tokens) {
23
+ this.options = options;
24
+ this.store = store;
25
+ this.tokens = tokens;
26
+ }
27
+ async authorize(request) {
28
+ if (request.codeChallengeMethod !== CODE_CHALLENGE_METHOD) {
29
+ throw new OAuthInvalidRequestException(`code_challenge_method must be ${CODE_CHALLENGE_METHOD}`);
30
+ }
31
+ if (!isValidCodeChallenge(request.codeChallenge)) {
32
+ throw new OAuthInvalidRequestException('code_challenge is malformed');
33
+ }
34
+ const client = this.findClient(request.clientId);
35
+ if (!client.redirectUris.includes(request.redirectUri)) {
36
+ throw new OAuthInvalidRequestException('redirect_uri is not registered for this client');
37
+ }
38
+ const transaction = {
39
+ clientId: client.id,
40
+ codeChallenge: request.codeChallenge,
41
+ expiresAt: Date.now() +
42
+ (this.options.transactionTtl ?? DEFAULT_TRANSACTION_TTL) * 1000,
43
+ id: randomToken(),
44
+ redirectUri: request.redirectUri,
45
+ ...(request.state === undefined
46
+ ? {}
47
+ : {
48
+ state: request.state,
49
+ }),
50
+ ...(request.scope
51
+ ? {
52
+ scopes: request.scope.split(' ').filter(Boolean),
53
+ }
54
+ : {}),
55
+ };
56
+ await this.store.saveTransaction(transaction);
57
+ const url = new URL(this.options.signInUrl);
58
+ url.searchParams.set('tx', transaction.id);
59
+ return url.toString();
60
+ }
61
+ async readTransaction(id) {
62
+ const transaction = await this.store.readTransaction(id);
63
+ if (!transaction) {
64
+ throw new OAuthInvalidGrantException('This sign-in request expired. Start again from the application.');
65
+ }
66
+ return transaction;
67
+ }
68
+ async completeAuthorization(transactionId, userId, options) {
69
+ const transaction = await this.readTransaction(transactionId);
70
+ const code = randomToken();
71
+ await this.store.saveCode({
72
+ clientId: transaction.clientId,
73
+ code,
74
+ codeChallenge: transaction.codeChallenge,
75
+ expiresAt: Date.now() + (this.options.codeTtl ?? DEFAULT_CODE_TTL) * 1000,
76
+ redirectUri: transaction.redirectUri,
77
+ userId,
78
+ ...(options?.workspaceId === undefined
79
+ ? {}
80
+ : {
81
+ workspaceId: options.workspaceId,
82
+ }),
83
+ });
84
+ await this.store.deleteTransaction(transaction.id);
85
+ const url = new URL(transaction.redirectUri);
86
+ url.searchParams.set('code', code);
87
+ if (transaction.state !== undefined) {
88
+ url.searchParams.set('state', transaction.state);
89
+ }
90
+ return url.toString();
91
+ }
92
+ async exchangeCode(input) {
93
+ const grant = await this.store.takeCode(input.code);
94
+ if (!grant) {
95
+ throw new OAuthInvalidGrantException();
96
+ }
97
+ if (grant.clientId !== input.clientId ||
98
+ grant.redirectUri !== input.redirectUri) {
99
+ throw new OAuthInvalidGrantException();
100
+ }
101
+ if (!verifyChallenge(input.codeVerifier, grant.codeChallenge)) {
102
+ throw new OAuthInvalidGrantException();
103
+ }
104
+ return this.issue(grant.userId, grant.workspaceId);
105
+ }
106
+ async refresh(refreshToken) {
107
+ const { userId, workspaceId } = await this.tokens.verifyRefreshToken(refreshToken);
108
+ return this.issue(userId, workspaceId);
109
+ }
110
+ async issue(userId, workspaceId) {
111
+ const pair = await this.tokens.issueTokens(userId, workspaceId === undefined
112
+ ? undefined
113
+ : {
114
+ workspaceId,
115
+ });
116
+ return {
117
+ accessToken: pair.accessToken,
118
+ expiresAt: Date.now() + pair.expiresIn * 1000,
119
+ refreshToken: pair.refreshToken,
120
+ };
121
+ }
122
+ findClient(clientId) {
123
+ const client = this.options.clients.find((candidate) => candidate.id === clientId);
124
+ if (!client) {
125
+ throw new OAuthInvalidRequestException('Unknown client_id');
126
+ }
127
+ return client;
128
+ }
129
+ };
130
+ OAuthService = __decorate([
131
+ Injectable(),
132
+ __param(0, Inject(OAUTH_OPTIONS)),
133
+ __param(1, Inject(OAUTH_CODE_STORE)),
134
+ __param(2, Inject(IamTokenService)),
135
+ __metadata("design:paramtypes", [Object, Object, IamTokenService])
136
+ ], OAuthService);
137
+ export { OAuthService };
138
+ //# sourceMappingURL=oauth.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.service.js","sourceRoot":"","sources":["../src/oauth.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ import type { ModuleMetadata, Type } from '@nestjs/common';
2
+ export type OAuthClient = {
3
+ id: string;
4
+ redirectUris: string[];
5
+ scopes?: string[];
6
+ };
7
+ export type OAuthCookieOptions = {
8
+ accessName?: string;
9
+ refreshName?: string;
10
+ domain?: string;
11
+ path?: string;
12
+ sameSite?: 'lax' | 'none' | 'strict';
13
+ secure?: boolean;
14
+ };
15
+ export type OAuthOptions = {
16
+ clients: OAuthClient[];
17
+ signInUrl: string;
18
+ codeTtl?: number;
19
+ transactionTtl?: number;
20
+ refreshTtl?: number;
21
+ cookie?: OAuthCookieOptions;
22
+ };
23
+ export type OAuthModuleOptions = OAuthOptions & {
24
+ store?: Type<OAuthCodeStore>;
25
+ imports?: ModuleMetadata['imports'];
26
+ };
27
+ export type OAuthModuleFactoryOptions = Omit<OAuthModuleOptions, 'imports'>;
28
+ export type OAuthTransaction = {
29
+ id: string;
30
+ clientId: string;
31
+ redirectUri: string;
32
+ codeChallenge: string;
33
+ state?: string;
34
+ scopes?: string[];
35
+ expiresAt: number;
36
+ };
37
+ export type OAuthAuthorizationCode = {
38
+ code: string;
39
+ clientId: string;
40
+ redirectUri: string;
41
+ codeChallenge: string;
42
+ userId: string;
43
+ workspaceId?: string;
44
+ expiresAt: number;
45
+ };
46
+ export interface OAuthCodeStore {
47
+ saveTransaction(transaction: OAuthTransaction): Promise<void>;
48
+ readTransaction(id: string): Promise<OAuthTransaction | null>;
49
+ deleteTransaction(id: string): Promise<void>;
50
+ saveCode(code: OAuthAuthorizationCode): Promise<void>;
51
+ takeCode(code: string): Promise<OAuthAuthorizationCode | null>;
52
+ }
53
+ export type OAuthAuthorizeRequest = {
54
+ clientId: string;
55
+ redirectUri: string;
56
+ codeChallenge: string;
57
+ codeChallengeMethod: string;
58
+ state?: string;
59
+ scope?: string;
60
+ };
61
+ export type OAuthSession = {
62
+ accessToken: string;
63
+ refreshToken: string;
64
+ expiresAt: number;
65
+ };
66
+ //# sourceMappingURL=oauth.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.types.d.ts","sourceRoot":"","sources":["../src/oauth.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAA;AAU1D,MAAM,MAAM,WAAW,GAAG;IACzB,EAAE,EAAE,MAAM,CAAA;IAEV,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAEhC,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf,IAAI,CAAC,EAAE,MAAM,CAAA;IAEb,QAAQ,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAA;IAEpC,MAAM,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IAC1B,OAAO,EAAE,WAAW,EAAE,CAAA;IAKtB,SAAS,EAAE,MAAM,CAAA;IAEjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAEhB,cAAc,CAAC,EAAE,MAAM,CAAA;IAMvB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,MAAM,CAAC,EAAE,kBAAkB,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG;IAE/C,KAAK,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,CAAA;IAC5B,OAAO,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,CAAA;CACnC,CAAA;AAED,MAAM,MAAM,yBAAyB,GAAG,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAA;AAG3E,MAAM,MAAM,gBAAgB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IAEjB,SAAS,EAAE,MAAM,CAAA;CACjB,CAAA;AAGD,MAAM,MAAM,sBAAsB,GAAG;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IAEpB,SAAS,EAAE,MAAM,CAAA;CACjB,CAAA;AAUD,MAAM,WAAW,cAAc;IAC9B,eAAe,CAAC,WAAW,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE7D,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAA;IAC7D,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5C,QAAQ,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAErD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAA;CAC9D;AAED,MAAM,MAAM,qBAAqB,GAAG;IACnC,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IAC1B,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IAEpB,SAAS,EAAE,MAAM,CAAA;CACjB,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=oauth.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.types.js","sourceRoot":"","sources":["../src/oauth.types.ts"],"names":[],"mappings":""}
package/dist/pkce.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export declare const CODE_CHALLENGE_METHOD = "S256";
2
+ export declare function isValidCodeVerifier(verifier: string): boolean;
3
+ export declare function isValidCodeChallenge(challenge: string): boolean;
4
+ export declare function deriveChallenge(verifier: string): string;
5
+ export declare function verifyChallenge(verifier: string, challenge: string): boolean;
6
+ export declare function randomToken(bytes?: number): string;
7
+ //# sourceMappingURL=pkce.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pkce.d.ts","sourceRoot":"","sources":["../src/pkce.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,qBAAqB,SAAS,CAAA;AAK3C,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExD;AAUD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAa5E;AAGD,wBAAgB,WAAW,CAAC,KAAK,SAAK,GAAG,MAAM,CAE9C"}
package/dist/pkce.js ADDED
@@ -0,0 +1,28 @@
1
+ import { createHash, randomBytes, timingSafeEqual } from 'node:crypto';
2
+ export const CODE_CHALLENGE_METHOD = 'S256';
3
+ const VERIFIER_PATTERN = /^[A-Za-z0-9\-._~]{43,128}$/u;
4
+ const CHALLENGE_PATTERN = /^[A-Za-z0-9\-_]{43}$/u;
5
+ export function isValidCodeVerifier(verifier) {
6
+ return VERIFIER_PATTERN.test(verifier);
7
+ }
8
+ export function isValidCodeChallenge(challenge) {
9
+ return CHALLENGE_PATTERN.test(challenge);
10
+ }
11
+ export function deriveChallenge(verifier) {
12
+ return createHash('sha256').update(verifier).digest('base64url');
13
+ }
14
+ export function verifyChallenge(verifier, challenge) {
15
+ if (!isValidCodeVerifier(verifier) || !isValidCodeChallenge(challenge)) {
16
+ return false;
17
+ }
18
+ const derived = Buffer.from(deriveChallenge(verifier));
19
+ const expected = Buffer.from(challenge);
20
+ if (derived.length !== expected.length) {
21
+ return false;
22
+ }
23
+ return timingSafeEqual(derived, expected);
24
+ }
25
+ export function randomToken(bytes = 32) {
26
+ return randomBytes(bytes).toString('base64url');
27
+ }
28
+ //# sourceMappingURL=pkce.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pkce.js","sourceRoot":"","sources":["../src/pkce.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAGtE,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAA;AAE3C,MAAM,gBAAgB,GAAG,6BAA6B,CAAA;AACtD,MAAM,iBAAiB,GAAG,uBAAuB,CAAA;AAEjD,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IACnD,OAAO,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;AACvC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,SAAiB;IACrD,OAAO,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;AACzC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC/C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;AACjE,CAAC;AAUD,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,SAAiB;IAClE,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,EAAE,CAAC;QACxE,OAAO,KAAK,CAAA;IACb,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAA;IACtD,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IAEvC,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC;QACxC,OAAO,KAAK,CAAA;IACb,CAAC;IAED,OAAO,eAAe,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;AAC1C,CAAC;AAGD,MAAM,UAAU,WAAW,CAAC,KAAK,GAAG,EAAE;IACrC,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;AAChD,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { OAuthAuthorizationCode, OAuthCodeStore, OAuthTransaction } from '../oauth.types.js';
2
+ export declare class MemoryOAuthCodeStore implements OAuthCodeStore {
3
+ private readonly transactions;
4
+ private readonly codes;
5
+ saveTransaction(transaction: OAuthTransaction): Promise<void>;
6
+ readTransaction(id: string): Promise<OAuthTransaction | null>;
7
+ deleteTransaction(id: string): Promise<void>;
8
+ saveCode(code: OAuthAuthorizationCode): Promise<void>;
9
+ takeCode(code: string): Promise<OAuthAuthorizationCode | null>;
10
+ private sweep;
11
+ }
12
+ //# sourceMappingURL=memory-code-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-code-store.d.ts","sourceRoot":"","sources":["../../src/store/memory-code-store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,sBAAsB,EACtB,cAAc,EACd,gBAAgB,EAChB,MAAM,kBAAkB,CAAA;AAczB,qBACa,oBAAqB,YAAW,cAAc;IAC1D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsC;IACnE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4C;IAE5D,eAAe,CAAC,WAAW,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAGlE;IAEK,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAclE;IAEK,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjD;IAEK,QAAQ,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAG1D;IAEK,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAYnE;IAED,OAAO,CAAC,KAAK;CAeb"}
@@ -0,0 +1,59 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ 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;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Injectable } from '@nestjs/common';
8
+ let MemoryOAuthCodeStore = class MemoryOAuthCodeStore {
9
+ transactions = new Map();
10
+ codes = new Map();
11
+ async saveTransaction(transaction) {
12
+ this.sweep();
13
+ this.transactions.set(transaction.id, transaction);
14
+ }
15
+ async readTransaction(id) {
16
+ const transaction = this.transactions.get(id);
17
+ if (!transaction) {
18
+ return null;
19
+ }
20
+ if (transaction.expiresAt <= Date.now()) {
21
+ this.transactions.delete(id);
22
+ return null;
23
+ }
24
+ return transaction;
25
+ }
26
+ async deleteTransaction(id) {
27
+ this.transactions.delete(id);
28
+ }
29
+ async saveCode(code) {
30
+ this.sweep();
31
+ this.codes.set(code.code, code);
32
+ }
33
+ async takeCode(code) {
34
+ const found = this.codes.get(code);
35
+ this.codes.delete(code);
36
+ if (!found || found.expiresAt <= Date.now()) {
37
+ return null;
38
+ }
39
+ return found;
40
+ }
41
+ sweep() {
42
+ const now = Date.now();
43
+ for (const [id, transaction] of this.transactions) {
44
+ if (transaction.expiresAt <= now) {
45
+ this.transactions.delete(id);
46
+ }
47
+ }
48
+ for (const [value, code] of this.codes) {
49
+ if (code.expiresAt <= now) {
50
+ this.codes.delete(value);
51
+ }
52
+ }
53
+ }
54
+ };
55
+ MemoryOAuthCodeStore = __decorate([
56
+ Injectable()
57
+ ], MemoryOAuthCodeStore);
58
+ export { MemoryOAuthCodeStore };
59
+ //# sourceMappingURL=memory-code-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-code-store.js","sourceRoot":"","sources":["../../src/store/memory-code-store.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAqBpC,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IACf,YAAY,GAAG,IAAI,GAAG,EAA4B,CAAA;IAClD,KAAK,GAAG,IAAI,GAAG,EAAkC,CAAA;IAElE,KAAK,CAAC,eAAe,CAAC,WAA6B;QAClD,IAAI,CAAC,KAAK,EAAE,CAAA;QACZ,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,EAAE,WAAW,CAAC,CAAA;IACnD,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,EAAU;QAC/B,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QAE7C,IAAI,CAAC,WAAW,EAAE,CAAC;YAClB,OAAO,IAAI,CAAA;QACZ,CAAC;QAED,IAAI,WAAW,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YACzC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;YAE5B,OAAO,IAAI,CAAA;QACZ,CAAC;QAED,OAAO,WAAW,CAAA;IACnB,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,EAAU;QACjC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IAC7B,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAA4B;QAC1C,IAAI,CAAC,KAAK,EAAE,CAAA;QACZ,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IAChC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAAY;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAIlC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAEvB,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAC7C,OAAO,IAAI,CAAA;QACZ,CAAC;QAED,OAAO,KAAK,CAAA;IACb,CAAC;IAEO,KAAK;QACZ,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QAEtB,KAAK,MAAM,CAAC,EAAE,EAAE,WAAW,CAAC,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACnD,IAAI,WAAW,CAAC,SAAS,IAAI,GAAG,EAAE,CAAC;gBAClC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;YAC7B,CAAC;QACF,CAAC;QAED,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACxC,IAAI,IAAI,CAAC,SAAS,IAAI,GAAG,EAAE,CAAC;gBAC3B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACzB,CAAC;QACF,CAAC;IACF,CAAC;CACD,CAAA;AA/DY,oBAAoB;IADhC,UAAU,EAAE;GACA,oBAAoB,CA+DhC"}
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@turystack/nestjs-oauth",
3
+ "description": "Authorization Code + PKCE authorization server for NestJS, issuing sessions as httpOnly cookies.",
4
+ "private": false,
5
+ "version": "1.0.1",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js",
15
+ "default": "./dist/index.js"
16
+ }
17
+ },
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "engines": {
22
+ "node": ">=20"
23
+ },
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/turystack/nestjs-oauth.git"
30
+ },
31
+ "peerDependencies": {
32
+ "@nestjs/common": "^11.0.0",
33
+ "@nestjs/core": "^11.0.0",
34
+ "@turystack/nestjs-config": "^1.0.0",
35
+ "@turystack/nestjs-iam": "^1.0.0",
36
+ "@turystack/nestjs-server": "^1.0.0",
37
+ "reflect-metadata": "^0.2.0",
38
+ "zod": "^4.0.0"
39
+ },
40
+ "devDependencies": {
41
+ "@biomejs/biome": "2.5.4",
42
+ "@nestjs/common": "^11.0.0",
43
+ "@nestjs/core": "^11.0.0",
44
+ "@nestjs/testing": "^11.0.0",
45
+ "@turystack/nestjs-config": "^1.0.0",
46
+ "@turystack/nestjs-iam": "^1.0.0",
47
+ "@turystack/nestjs-server": "^1.0.0",
48
+ "@types/node": "^25.5.0",
49
+ "@vitest/coverage-v8": "^4.1.10",
50
+ "reflect-metadata": "^0.2.2",
51
+ "tsc-alias": "^1.9.1",
52
+ "typescript": "^7.0.2",
53
+ "vite": "^8.1.5",
54
+ "vitest": "^4.1.10",
55
+ "zod": "^4.0.0"
56
+ },
57
+ "scripts": {
58
+ "build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
59
+ "typecheck": "tsc --noEmit",
60
+ "test": "vitest run",
61
+ "test:cov": "vitest run --coverage",
62
+ "lint": "biome lint ./src",
63
+ "format": "biome format --write ./src",
64
+ "check": "biome check ./src",
65
+ "check:fix": "biome check --write ./src"
66
+ }
67
+ }