@nestjs/authentication 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -6
- package/dist/account/password-reset.service.d.ts +1 -1
- package/dist/account/password-reset.service.js +1 -1
- package/dist/authentication.constants.d.ts +9 -0
- package/dist/authentication.constants.js +9 -0
- package/dist/authentication.module-definition.js +13 -4
- package/dist/decorators/authenticate.decorator.js +4 -1
- package/dist/decorators/current-user.decorator.d.ts +3 -1
- package/dist/decorators/current-user.decorator.js +2 -0
- package/dist/decorators/public.decorator.d.ts +3 -0
- package/dist/decorators/public.decorator.js +3 -0
- package/dist/errors/authentication.error.d.ts +4 -2
- package/dist/errors/authentication.error.js +6 -2
- package/dist/errors/magic-link.error.d.ts +1 -1
- package/dist/errors/magic-link.error.js +1 -1
- package/dist/events/authentication-events.interface.d.ts +16 -1
- package/dist/events/authentication-events.service.d.ts +2 -1
- package/dist/events/authentication-events.service.js +2 -1
- package/dist/guards/authentication.guard.d.ts +4 -2
- package/dist/guards/authentication.guard.js +9 -6
- package/dist/index.d.ts +7 -2
- package/dist/interceptors/authentication-scope.interceptor.js +2 -2
- package/dist/interfaces/authentication-error-options.interface.d.ts +12 -2
- package/dist/interfaces/authentication-module-options.interface.d.ts +13 -1
- package/dist/interfaces/authentication-result.interface.d.ts +7 -0
- package/dist/interfaces/jwt-options.interface.d.ts +13 -7
- package/dist/interfaces/jwt.interface.d.ts +4 -3
- package/dist/interfaces/session-store.interface.d.ts +29 -4
- package/dist/interfaces/token.interface.d.ts +31 -4
- package/dist/jwt/jwks.client.js +10 -3
- package/dist/jwt/jws.util.d.ts +10 -1
- package/dist/jwt/jws.util.js +108 -16
- package/dist/jwt/jwt-signer.service.js +2 -2
- package/dist/jwt/jwt-verifier.service.js +6 -4
- package/dist/jwt/token.service.d.ts +29 -8
- package/dist/jwt/token.service.js +48 -8
- package/dist/mfa/mfa.service.d.ts +11 -1
- package/dist/mfa/mfa.service.js +19 -9
- package/dist/oidc/oidc.client.d.ts +1 -1
- package/dist/oidc/oidc.client.js +1 -1
- package/dist/session/in-memory-session.store.d.ts +2 -0
- package/dist/session/in-memory-session.store.js +3 -1
- package/dist/session/session-cookie.provider.d.ts +17 -1
- package/dist/session/session-cookie.provider.js +16 -1
- package/dist/session/session.service.d.ts +18 -2
- package/dist/session/session.service.js +43 -8
- package/dist/session/sign-in.service.d.ts +6 -5
- package/dist/session/sign-in.service.js +6 -5
- package/dist/storage/authentication.storage.d.ts +25 -16
- package/dist/storage/authentication.storage.js +25 -16
- package/dist/testing/index.js +13 -5
- package/dist/utils/transport-errors.util.d.ts +23 -12
- package/dist/utils/transport-errors.util.js +34 -22
- package/package.json +25 -25
|
@@ -1,33 +1,44 @@
|
|
|
1
1
|
import { type ExecutionContext } from '@nestjs/common';
|
|
2
2
|
import type { HttpAdapterHost } from '@nestjs/core';
|
|
3
|
+
import type { AuthenticationError } from '../errors/authentication.error.js';
|
|
3
4
|
export interface Refusal {
|
|
4
5
|
/** Default 401. 403 for a user the route refuses (an unverified address), 409 for a conflict (`MfaAlreadyEnrolledError`). */
|
|
5
6
|
status?: 401 | 403 | 409;
|
|
6
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Without one, Nest's bare body (`new UnauthorizedException()`): the status
|
|
9
|
+
* phrase as `message`, and no `error`.
|
|
10
|
+
*/
|
|
7
11
|
message?: string;
|
|
8
|
-
/** Machine-readable reason (`mfa_required`)
|
|
12
|
+
/** Machine-readable reason (`mfa_required`): Nest's `errorCode`, and the body's `error` when there is a message. */
|
|
9
13
|
code?: string;
|
|
14
|
+
/** Machine-readable data the app chose to send (`AuthenticationError`'s `details`): the body's `details`. */
|
|
15
|
+
details?: Record<string, unknown>;
|
|
10
16
|
/** The error behind the refusal, kept as the exception's `cause` for logs. */
|
|
11
17
|
cause?: unknown;
|
|
12
18
|
}
|
|
19
|
+
/** The refusal for an `AuthenticationError` a provider or a handler threw. */
|
|
20
|
+
export declare function refusalOf(error: AuthenticationError): Refusal;
|
|
13
21
|
/**
|
|
14
22
|
* The one place this package's refusals become responses, for the guard and
|
|
15
23
|
* for `AuthenticationError`s thrown by handlers: the error each transport's
|
|
16
24
|
* exception filter understands, for the refusal's status.
|
|
17
25
|
*
|
|
18
26
|
* - HTTP and GraphQL: Nest's own `UnauthorizedException` (or
|
|
19
|
-
* `ForbiddenException` for a 403, `ConflictException` for a 409),
|
|
20
|
-
* `{"message":"Unauthorized","statusCode":401}`, or with a
|
|
21
|
-
* `{"message":"Refresh token reused","error":"Unauthorized","statusCode":401}`.
|
|
22
|
-
* A `code`
|
|
23
|
-
* `
|
|
27
|
+
* `ForbiddenException` for a 403, `ConflictException` for a 409), with
|
|
28
|
+
* Nest's own body: `{"message":"Unauthorized","statusCode":401}`, or with a
|
|
29
|
+
* message, `{"message":"Refresh token reused","error":"Unauthorized","statusCode":401}`.
|
|
30
|
+
* A `code` is Nest's `errorCode` (on the exception, and in the body), and
|
|
31
|
+
* the description in `error` when there is a message; `details` follow.
|
|
32
|
+
* Over HTTP, `challenge` becomes the `WWW-Authenticate` header of a 401.
|
|
24
33
|
* - ws and rpc: a `WsException` / `RpcException` carrying
|
|
25
|
-
* `{ statusCode, message }` (plus `status: 'error'` for ws, and `error
|
|
26
|
-
*
|
|
27
|
-
* server error", hence their own classes,
|
|
28
|
-
* are installed.
|
|
34
|
+
* `{ statusCode, message }` (plus `status: 'error'` for ws, and `error`,
|
|
35
|
+
* `errorCode` and `details` as above). Those filters report
|
|
36
|
+
* `HttpException`s as "Internal server error", hence their own classes,
|
|
37
|
+
* loaded only when the packages are installed.
|
|
38
|
+
*
|
|
39
|
+
* Nothing else reaches the body: `cause` stays on the exception, for logs.
|
|
29
40
|
*/
|
|
30
|
-
export declare function refusal(context: ExecutionContext, { status, message, code, cause }?: Refusal, { challenge, adapterHost }?: {
|
|
41
|
+
export declare function refusal(context: ExecutionContext, { status, message, code, details, cause }?: Refusal, { challenge, adapterHost }?: {
|
|
31
42
|
challenge?: string;
|
|
32
43
|
adapterHost?: HttpAdapterHost;
|
|
33
44
|
}): Promise<Error>;
|
|
@@ -10,25 +10,42 @@ function load(pkg, name) {
|
|
|
10
10
|
return pending;
|
|
11
11
|
}
|
|
12
12
|
const DEFAULT_MESSAGE = 'Unauthorized';
|
|
13
|
+
const PHRASES = { 401: 'Unauthorized', 403: 'Forbidden', 409: 'Conflict' };
|
|
14
|
+
/** The refusal for an `AuthenticationError` a provider or a handler threw. */
|
|
15
|
+
export function refusalOf(error) {
|
|
16
|
+
const { status, message, code, details } = error;
|
|
17
|
+
return { status, message, code, details, cause: error };
|
|
18
|
+
}
|
|
13
19
|
/**
|
|
14
20
|
* The one place this package's refusals become responses, for the guard and
|
|
15
21
|
* for `AuthenticationError`s thrown by handlers: the error each transport's
|
|
16
22
|
* exception filter understands, for the refusal's status.
|
|
17
23
|
*
|
|
18
24
|
* - HTTP and GraphQL: Nest's own `UnauthorizedException` (or
|
|
19
|
-
* `ForbiddenException` for a 403, `ConflictException` for a 409),
|
|
20
|
-
* `{"message":"Unauthorized","statusCode":401}`, or with a
|
|
21
|
-
* `{"message":"Refresh token reused","error":"Unauthorized","statusCode":401}`.
|
|
22
|
-
* A `code`
|
|
23
|
-
* `
|
|
25
|
+
* `ForbiddenException` for a 403, `ConflictException` for a 409), with
|
|
26
|
+
* Nest's own body: `{"message":"Unauthorized","statusCode":401}`, or with a
|
|
27
|
+
* message, `{"message":"Refresh token reused","error":"Unauthorized","statusCode":401}`.
|
|
28
|
+
* A `code` is Nest's `errorCode` (on the exception, and in the body), and
|
|
29
|
+
* the description in `error` when there is a message; `details` follow.
|
|
30
|
+
* Over HTTP, `challenge` becomes the `WWW-Authenticate` header of a 401.
|
|
24
31
|
* - ws and rpc: a `WsException` / `RpcException` carrying
|
|
25
|
-
* `{ statusCode, message }` (plus `status: 'error'` for ws, and `error
|
|
26
|
-
*
|
|
27
|
-
* server error", hence their own classes,
|
|
28
|
-
* are installed.
|
|
32
|
+
* `{ statusCode, message }` (plus `status: 'error'` for ws, and `error`,
|
|
33
|
+
* `errorCode` and `details` as above). Those filters report
|
|
34
|
+
* `HttpException`s as "Internal server error", hence their own classes,
|
|
35
|
+
* loaded only when the packages are installed.
|
|
36
|
+
*
|
|
37
|
+
* Nothing else reaches the body: `cause` stays on the exception, for logs.
|
|
29
38
|
*/
|
|
30
|
-
export async function refusal(context, { status = 401, message
|
|
31
|
-
|
|
39
|
+
export async function refusal(context, { status = 401, message, code, details, cause } = {}, { challenge, adapterHost } = {}) {
|
|
40
|
+
// The default message without a code gives the body of a bare `new UnauthorizedException()`.
|
|
41
|
+
const bare = message === undefined || (status === 401 && message === DEFAULT_MESSAGE && !code);
|
|
42
|
+
const payload = {
|
|
43
|
+
statusCode: status,
|
|
44
|
+
...(!bare && code && { error: code }),
|
|
45
|
+
message: message ?? PHRASES[status],
|
|
46
|
+
...(code && { errorCode: code }),
|
|
47
|
+
...(details !== undefined && { details }),
|
|
48
|
+
};
|
|
32
49
|
switch (context.getType()) {
|
|
33
50
|
case 'ws': {
|
|
34
51
|
const WsException = await load('@nestjs/websockets', 'WsException');
|
|
@@ -50,17 +67,12 @@ export async function refusal(context, { status = 401, message = DEFAULT_MESSAGE
|
|
|
50
67
|
}
|
|
51
68
|
break;
|
|
52
69
|
}
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
// The default message gives the body of a bare `new UnauthorizedException()`.
|
|
61
|
-
return message === DEFAULT_MESSAGE && !code
|
|
62
|
-
? new UnauthorizedException(undefined, options)
|
|
63
|
-
: new UnauthorizedException(message, options);
|
|
70
|
+
const Exception = status === 409 ? ConflictException : status === 403 ? ForbiddenException : UnauthorizedException;
|
|
71
|
+
// Nest builds the body: `error` is the code (the description) or the status phrase, then `errorCode`.
|
|
72
|
+
const options = { cause, ...(code && { errorCode: code, ...(!bare && { description: code }) }) };
|
|
73
|
+
const exception = new Exception(bare ? undefined : message, options);
|
|
74
|
+
// `details` are not Nest's: its body, with them.
|
|
75
|
+
return details === undefined ? exception : new Exception({ ...exception.getResponse(), details }, options);
|
|
64
76
|
}
|
|
65
77
|
function withCause(error, cause) {
|
|
66
78
|
if (cause !== undefined) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nestjs/authentication",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Nest - modern, fast, powerful node.js web framework (@authentication)",
|
|
5
5
|
"author": "Kamil Mysliwiec",
|
|
6
6
|
"license": "MIT",
|
|
@@ -46,41 +46,41 @@
|
|
|
46
46
|
"@electric-sql/pglite": "0.5.8",
|
|
47
47
|
"@electric-sql/pglite-socket": "0.2.11",
|
|
48
48
|
"@nestjs/apollo": "14.0.1",
|
|
49
|
-
"@nestjs/authorization": "
|
|
50
|
-
"@nestjs/common": "12.1.
|
|
51
|
-
"@nestjs/core": "12.1.
|
|
49
|
+
"@nestjs/authorization": "0.0.1",
|
|
50
|
+
"@nestjs/common": "12.1.2",
|
|
51
|
+
"@nestjs/core": "12.1.2",
|
|
52
52
|
"@nestjs/drizzle": "0.0.1",
|
|
53
53
|
"@nestjs/graphql": "14.0.1",
|
|
54
54
|
"@nestjs/idempotency": "0.0.1",
|
|
55
|
-
"@nestjs/mail": "
|
|
56
|
-
"@nestjs/microservices": "12.1.
|
|
57
|
-
"@nestjs/platform-express": "12.1.
|
|
58
|
-
"@nestjs/platform-fastify": "12.1.
|
|
59
|
-
"@nestjs/platform-ws": "12.1.
|
|
55
|
+
"@nestjs/mail": "0.0.1",
|
|
56
|
+
"@nestjs/microservices": "12.1.2",
|
|
57
|
+
"@nestjs/platform-express": "12.1.2",
|
|
58
|
+
"@nestjs/platform-fastify": "12.1.2",
|
|
59
|
+
"@nestjs/platform-ws": "12.1.2",
|
|
60
60
|
"@nestjs/resilience": "0.0.1",
|
|
61
|
-
"@nestjs/testing": "12.1.
|
|
62
|
-
"@nestjs/throttler": "6.7.
|
|
63
|
-
"@nestjs/websockets": "12.1.
|
|
64
|
-
"@types/node": "24.
|
|
61
|
+
"@nestjs/testing": "12.1.2",
|
|
62
|
+
"@nestjs/throttler": "6.7.1",
|
|
63
|
+
"@nestjs/websockets": "12.1.2",
|
|
64
|
+
"@types/node": "24.19.1",
|
|
65
65
|
"@types/pg": "8.23.1",
|
|
66
|
-
"@types/supertest": "
|
|
67
|
-
"@types/ws": "8.18.
|
|
66
|
+
"@types/supertest": "7.2.1",
|
|
67
|
+
"@types/ws": "8.18.2",
|
|
68
68
|
"drizzle-orm": "0.45.3",
|
|
69
69
|
"graphql": "16.14.2",
|
|
70
70
|
"husky": "9.1.7",
|
|
71
|
-
"lint-staged": "17.
|
|
72
|
-
"oxlint": "1.
|
|
73
|
-
"pg": "8.23.
|
|
74
|
-
"prettier": "3.9.
|
|
71
|
+
"lint-staged": "17.6.0",
|
|
72
|
+
"oxlint": "1.87.0",
|
|
73
|
+
"pg": "8.23.1",
|
|
74
|
+
"prettier": "3.9.9",
|
|
75
75
|
"reflect-metadata": "0.2.2",
|
|
76
|
-
"release-it": "21.1.
|
|
76
|
+
"release-it": "21.1.1",
|
|
77
77
|
"rxjs": "7.8.2",
|
|
78
|
-
"supertest": "7.
|
|
79
|
-
"typeorm": "1.1.
|
|
78
|
+
"supertest": "7.3.1",
|
|
79
|
+
"typeorm": "1.1.2",
|
|
80
80
|
"typescript": "7.0.2",
|
|
81
|
-
"vite": "8.3.
|
|
82
|
-
"vitest": "5.0.
|
|
83
|
-
"ws": "8.
|
|
81
|
+
"vite": "8.3.3",
|
|
82
|
+
"vitest": "5.0.3",
|
|
83
|
+
"ws": "8.22.0"
|
|
84
84
|
},
|
|
85
85
|
"peerDependencies": {
|
|
86
86
|
"@nestjs/common": "^12.0.0",
|