@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.
- package/README.md +34 -0
- package/dist/cookies.d.ts +15 -0
- package/dist/cookies.d.ts.map +1 -0
- package/dist/cookies.js +57 -0
- package/dist/cookies.js.map +1 -0
- package/dist/exceptions/oauth.exceptions.d.ts +8 -0
- package/dist/exceptions/oauth.exceptions.d.ts.map +1 -0
- package/dist/exceptions/oauth.exceptions.js +14 -0
- package/dist/exceptions/oauth.exceptions.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/oauth.constants.d.ts +8 -0
- package/dist/oauth.constants.d.ts.map +1 -0
- package/dist/oauth.constants.js +8 -0
- package/dist/oauth.constants.js.map +1 -0
- package/dist/oauth.controller.d.ts +52 -0
- package/dist/oauth.controller.d.ts.map +1 -0
- package/dist/oauth.controller.js +236 -0
- package/dist/oauth.controller.js.map +1 -0
- package/dist/oauth.module.d.ts +9 -0
- package/dist/oauth.module.d.ts.map +1 -0
- package/dist/oauth.module.js +81 -0
- package/dist/oauth.module.js.map +1 -0
- package/dist/oauth.service.d.ts +23 -0
- package/dist/oauth.service.d.ts.map +1 -0
- package/dist/oauth.service.js +138 -0
- package/dist/oauth.service.js.map +1 -0
- package/dist/oauth.types.d.ts +66 -0
- package/dist/oauth.types.d.ts.map +1 -0
- package/dist/oauth.types.js +2 -0
- package/dist/oauth.types.js.map +1 -0
- package/dist/pkce.d.ts +7 -0
- package/dist/pkce.d.ts.map +1 -0
- package/dist/pkce.js +28 -0
- package/dist/pkce.js.map +1 -0
- package/dist/store/memory-code-store.d.ts +12 -0
- package/dist/store/memory-code-store.d.ts.map +1 -0
- package/dist/store/memory-code-store.js +59 -0
- package/dist/store/memory-code-store.js.map +1 -0
- 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"}
|
package/dist/cookies.js
ADDED
|
@@ -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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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 @@
|
|
|
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
|
package/dist/pkce.js.map
ADDED
|
@@ -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
|
+
}
|