@nestjs/authentication 0.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/LICENSE +23 -0
- package/README.md +50 -0
- package/dist/account/email-verification.handler.d.ts +23 -0
- package/dist/account/email-verification.handler.js +17 -0
- package/dist/account/email-verification.service.d.ts +46 -0
- package/dist/account/email-verification.service.js +111 -0
- package/dist/account/email.util.d.ts +14 -0
- package/dist/account/email.util.js +28 -0
- package/dist/account/in-memory-email-token.store.d.ts +12 -0
- package/dist/account/in-memory-email-token.store.js +60 -0
- package/dist/account/password-reset.handler.d.ts +19 -0
- package/dist/account/password-reset.handler.js +9 -0
- package/dist/account/password-reset.service.d.ts +69 -0
- package/dist/account/password-reset.service.js +204 -0
- package/dist/authentication.constants.d.ts +13 -0
- package/dist/authentication.constants.js +13 -0
- package/dist/authentication.module-definition.d.ts +21 -0
- package/dist/authentication.module-definition.js +147 -0
- package/dist/authentication.module.d.ts +34 -0
- package/dist/authentication.module.js +139 -0
- package/dist/context/authentication-scope.service.d.ts +39 -0
- package/dist/context/authentication-scope.service.js +64 -0
- package/dist/context/authentication.context.d.ts +28 -0
- package/dist/context/authentication.context.js +58 -0
- package/dist/context/index.d.ts +2 -0
- package/dist/context/index.js +2 -0
- package/dist/decorators/authenticate.decorator.d.ts +10 -0
- package/dist/decorators/authenticate.decorator.js +18 -0
- package/dist/decorators/current-session.decorator.d.ts +2 -0
- package/dist/decorators/current-session.decorator.js +6 -0
- package/dist/decorators/current-user.decorator.d.ts +6 -0
- package/dist/decorators/current-user.decorator.js +11 -0
- package/dist/decorators/index.d.ts +4 -0
- package/dist/decorators/index.js +4 -0
- package/dist/decorators/public.decorator.d.ts +5 -0
- package/dist/decorators/public.decorator.js +6 -0
- package/dist/errors/authentication.error.d.ts +27 -0
- package/dist/errors/authentication.error.js +33 -0
- package/dist/errors/index.d.ts +7 -0
- package/dist/errors/index.js +7 -0
- package/dist/errors/jwt.error.d.ts +9 -0
- package/dist/errors/jwt.error.js +12 -0
- package/dist/errors/magic-link.error.d.ts +18 -0
- package/dist/errors/magic-link.error.js +21 -0
- package/dist/errors/mfa-already-enrolled.error.d.ts +14 -0
- package/dist/errors/mfa-already-enrolled.error.js +17 -0
- package/dist/errors/oidc.error.d.ts +15 -0
- package/dist/errors/oidc.error.js +13 -0
- package/dist/errors/refresh-token.error.d.ts +10 -0
- package/dist/errors/refresh-token.error.js +12 -0
- package/dist/errors/secret-decryption.error.d.ts +3 -0
- package/dist/errors/secret-decryption.error.js +6 -0
- package/dist/events/authentication-events.interface.d.ts +121 -0
- package/dist/events/authentication-events.interface.js +1 -0
- package/dist/events/authentication-events.service.d.ts +19 -0
- package/dist/events/authentication-events.service.js +39 -0
- package/dist/events/authentication.channels.d.ts +4 -0
- package/dist/events/authentication.channels.js +11 -0
- package/dist/events/index.d.ts +3 -0
- package/dist/events/index.js +3 -0
- package/dist/guards/authentication.guard.d.ts +57 -0
- package/dist/guards/authentication.guard.js +171 -0
- package/dist/guards/index.d.ts +1 -0
- package/dist/guards/index.js +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +52 -0
- package/dist/interceptors/authentication-scope.interceptor.d.ts +20 -0
- package/dist/interceptors/authentication-scope.interceptor.js +48 -0
- package/dist/interceptors/index.d.ts +1 -0
- package/dist/interceptors/index.js +1 -0
- package/dist/interfaces/api-key.interface.d.ts +41 -0
- package/dist/interfaces/api-key.interface.js +1 -0
- package/dist/interfaces/authenticate-options.interface.d.ts +31 -0
- package/dist/interfaces/authenticate-options.interface.js +1 -0
- package/dist/interfaces/authentication-error-options.interface.d.ts +8 -0
- package/dist/interfaces/authentication-error-options.interface.js +1 -0
- package/dist/interfaces/authentication-module-options.interface.d.ts +58 -0
- package/dist/interfaces/authentication-module-options.interface.js +1 -0
- package/dist/interfaces/authentication-registry.interface.d.ts +41 -0
- package/dist/interfaces/authentication-registry.interface.js +1 -0
- package/dist/interfaces/authentication-result.interface.d.ts +25 -0
- package/dist/interfaces/authentication-result.interface.js +1 -0
- package/dist/interfaces/authentication-storage.interface.d.ts +29 -0
- package/dist/interfaces/authentication-storage.interface.js +1 -0
- package/dist/interfaces/duration.interface.d.ts +2 -0
- package/dist/interfaces/duration.interface.js +1 -0
- package/dist/interfaces/email-token-store.interface.d.ts +52 -0
- package/dist/interfaces/email-token-store.interface.js +1 -0
- package/dist/interfaces/email-verification.interface.d.ts +18 -0
- package/dist/interfaces/email-verification.interface.js +1 -0
- package/dist/interfaces/index.d.ts +27 -0
- package/dist/interfaces/index.js +27 -0
- package/dist/interfaces/jwt-options.interface.d.ts +68 -0
- package/dist/interfaces/jwt-options.interface.js +1 -0
- package/dist/interfaces/jwt.interface.d.ts +36 -0
- package/dist/interfaces/jwt.interface.js +1 -0
- package/dist/interfaces/magic-link-store.interface.d.ts +36 -0
- package/dist/interfaces/magic-link-store.interface.js +1 -0
- package/dist/interfaces/magic-link.interface.d.ts +55 -0
- package/dist/interfaces/magic-link.interface.js +1 -0
- package/dist/interfaces/mfa-options.interface.d.ts +44 -0
- package/dist/interfaces/mfa-options.interface.js +1 -0
- package/dist/interfaces/mfa-store.interface.d.ts +70 -0
- package/dist/interfaces/mfa-store.interface.js +1 -0
- package/dist/interfaces/oidc-provider.interface.d.ts +56 -0
- package/dist/interfaces/oidc-provider.interface.js +1 -0
- package/dist/interfaces/oidc-state-store.interface.d.ts +25 -0
- package/dist/interfaces/oidc-state-store.interface.js +1 -0
- package/dist/interfaces/oidc.interface.d.ts +84 -0
- package/dist/interfaces/oidc.interface.js +1 -0
- package/dist/interfaces/password-hasher-options.interface.d.ts +17 -0
- package/dist/interfaces/password-hasher-options.interface.js +1 -0
- package/dist/interfaces/password-reset.interface.d.ts +49 -0
- package/dist/interfaces/password-reset.interface.js +1 -0
- package/dist/interfaces/refresh-token-options.interface.d.ts +10 -0
- package/dist/interfaces/refresh-token-options.interface.js +1 -0
- package/dist/interfaces/refresh-token-store.interface.d.ts +61 -0
- package/dist/interfaces/refresh-token-store.interface.js +1 -0
- package/dist/interfaces/session-store.interface.d.ts +53 -0
- package/dist/interfaces/session-store.interface.js +1 -0
- package/dist/interfaces/session.interface.d.ts +50 -0
- package/dist/interfaces/session.interface.js +1 -0
- package/dist/interfaces/sign-in-options.interface.d.ts +9 -0
- package/dist/interfaces/sign-in-options.interface.js +1 -0
- package/dist/interfaces/token.interface.d.ts +30 -0
- package/dist/interfaces/token.interface.js +1 -0
- package/dist/jwt/amr.util.d.ts +9 -0
- package/dist/jwt/amr.util.js +11 -0
- package/dist/jwt/in-memory-refresh-token.store.d.ts +29 -0
- package/dist/jwt/in-memory-refresh-token.store.js +67 -0
- package/dist/jwt/jwks.client.d.ts +34 -0
- package/dist/jwt/jwks.client.js +154 -0
- package/dist/jwt/jws.util.d.ts +45 -0
- package/dist/jwt/jws.util.js +282 -0
- package/dist/jwt/jwt-bearer.provider.d.ts +48 -0
- package/dist/jwt/jwt-bearer.provider.js +126 -0
- package/dist/jwt/jwt-signer.service.d.ts +19 -0
- package/dist/jwt/jwt-signer.service.js +36 -0
- package/dist/jwt/jwt-verifier.service.d.ts +22 -0
- package/dist/jwt/jwt-verifier.service.js +109 -0
- package/dist/jwt/token.service.d.ts +81 -0
- package/dist/jwt/token.service.js +230 -0
- package/dist/magic-link/in-memory-magic-link.store.d.ts +12 -0
- package/dist/magic-link/in-memory-magic-link.store.js +45 -0
- package/dist/magic-link/magic-link.handler.d.ts +25 -0
- package/dist/magic-link/magic-link.handler.js +14 -0
- package/dist/magic-link/magic-link.service.d.ts +79 -0
- package/dist/magic-link/magic-link.service.js +215 -0
- package/dist/mfa/in-memory-mfa.store.d.ts +25 -0
- package/dist/mfa/in-memory-mfa.store.js +73 -0
- package/dist/mfa/mfa.service.d.ts +104 -0
- package/dist/mfa/mfa.service.js +346 -0
- package/dist/mfa/otp.util.d.ts +7 -0
- package/dist/mfa/otp.util.js +52 -0
- package/dist/mfa/secret-cipher.service.d.ts +28 -0
- package/dist/mfa/secret-cipher.service.js +106 -0
- package/dist/oidc/in-memory-oidc-state.store.d.ts +13 -0
- package/dist/oidc/in-memory-oidc-state.store.js +45 -0
- package/dist/oidc/oidc-account.resolver.d.ts +23 -0
- package/dist/oidc/oidc-account.resolver.js +16 -0
- package/dist/oidc/oidc.client.d.ts +44 -0
- package/dist/oidc/oidc.client.js +286 -0
- package/dist/oidc/oidc.presets.d.ts +24 -0
- package/dist/oidc/oidc.presets.js +53 -0
- package/dist/oidc/oidc.service.d.ts +88 -0
- package/dist/oidc/oidc.service.js +259 -0
- package/dist/providers/api-key.provider.d.ts +51 -0
- package/dist/providers/api-key.provider.js +93 -0
- package/dist/providers/authentication.provider.d.ts +40 -0
- package/dist/providers/authentication.provider.js +33 -0
- package/dist/providers/index.d.ts +2 -0
- package/dist/providers/index.js +2 -0
- package/dist/services/authentication-registry.service.d.ts +75 -0
- package/dist/services/authentication-registry.service.js +235 -0
- package/dist/services/index.d.ts +3 -0
- package/dist/services/index.js +3 -0
- package/dist/services/password-hasher.service.d.ts +47 -0
- package/dist/services/password-hasher.service.js +170 -0
- package/dist/services/ws-authenticator.service.d.ts +38 -0
- package/dist/services/ws-authenticator.service.js +92 -0
- package/dist/session/cookies.util.d.ts +34 -0
- package/dist/session/cookies.util.js +82 -0
- package/dist/session/in-memory-session.store.d.ts +33 -0
- package/dist/session/in-memory-session.store.js +60 -0
- package/dist/session/session-cookie.provider.d.ts +36 -0
- package/dist/session/session-cookie.provider.js +58 -0
- package/dist/session/session.service.d.ts +97 -0
- package/dist/session/session.service.js +208 -0
- package/dist/session/sign-in.service.d.ts +87 -0
- package/dist/session/sign-in.service.js +206 -0
- package/dist/storage/authentication.storage.d.ts +130 -0
- package/dist/storage/authentication.storage.js +350 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.js +1 -0
- package/dist/testing/index.d.ts +39 -0
- package/dist/testing/index.js +531 -0
- package/dist/utils/api-key.util.d.ts +10 -0
- package/dist/utils/api-key.util.js +10 -0
- package/dist/utils/auth-state.util.d.ts +54 -0
- package/dist/utils/auth-state.util.js +209 -0
- package/dist/utils/bearer.util.d.ts +9 -0
- package/dist/utils/bearer.util.js +21 -0
- package/dist/utils/cross-origin.util.d.ts +29 -0
- package/dist/utils/cross-origin.util.js +90 -0
- package/dist/utils/crypto.util.d.ts +19 -0
- package/dist/utils/crypto.util.js +38 -0
- package/dist/utils/duration.util.d.ts +9 -0
- package/dist/utils/duration.util.js +44 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/options.util.d.ts +20 -0
- package/dist/utils/options.util.js +44 -0
- package/dist/utils/transport-errors.util.d.ts +33 -0
- package/dist/utils/transport-errors.util.js +70 -0
- package/package.json +108 -0
|
@@ -0,0 +1,235 @@
|
|
|
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
|
+
var AuthenticationRegistry_1;
|
|
14
|
+
import { Inject, Injectable, Logger, Optional } from '@nestjs/common';
|
|
15
|
+
import { ModuleRef } from '@nestjs/core';
|
|
16
|
+
import { PROVIDER_INIT } from '../providers/authentication.provider.js';
|
|
17
|
+
import { AUTHENTICATION_MODULE_OPTIONS } from '../authentication.constants.js';
|
|
18
|
+
const HANDLERS = {
|
|
19
|
+
emailVerification: {
|
|
20
|
+
className: 'EmailVerificationHandler',
|
|
21
|
+
methods: ['send', 'markVerified'],
|
|
22
|
+
option: "emailVerification: { url: 'https://app.example.com/verify-email' }",
|
|
23
|
+
},
|
|
24
|
+
passwordReset: {
|
|
25
|
+
className: 'PasswordResetHandler',
|
|
26
|
+
methods: ['findUser', 'send', 'updatePassword'],
|
|
27
|
+
option: "passwordReset: { url: 'https://app.example.com/reset-password' }",
|
|
28
|
+
},
|
|
29
|
+
magicLink: {
|
|
30
|
+
className: 'MagicLinkHandler',
|
|
31
|
+
methods: ['send', 'resolveUser'],
|
|
32
|
+
option: "magicLink: { url: 'https://app.example.com/sign-in/magic' }",
|
|
33
|
+
},
|
|
34
|
+
oidc: {
|
|
35
|
+
className: 'OidcAccountResolver',
|
|
36
|
+
methods: ['resolveUser'],
|
|
37
|
+
option: "oidc: { callbackUrl: 'https://app.example.com/auth/oidc/:provider/callback', providers: { … } }",
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
const HANDLER_NAMES = Object.keys(HANDLERS);
|
|
41
|
+
/** @internal Locks the registry. `AuthenticationModule.onModuleInit()` and the first read call it. */
|
|
42
|
+
export const LOCK_REGISTRY = Symbol('AuthenticationRegistry.lock');
|
|
43
|
+
/**
|
|
44
|
+
* Where the app's credential providers and feature handlers register, from
|
|
45
|
+
* their own constructors, as stores do with `AuthenticationStorage`. They
|
|
46
|
+
* are ordinary providers of the app's modules, so they inject whatever
|
|
47
|
+
* those modules provide:
|
|
48
|
+
*
|
|
49
|
+
* ```ts
|
|
50
|
+
* @Injectable()
|
|
51
|
+
* export class SessionAuth extends SessionCookieProvider<User> {
|
|
52
|
+
* constructor(private readonly users: UsersRepository, registry: AuthenticationRegistry) {
|
|
53
|
+
* super();
|
|
54
|
+
* registry.registerProvider(this);
|
|
55
|
+
* }
|
|
56
|
+
* // ...
|
|
57
|
+
* }
|
|
58
|
+
*
|
|
59
|
+
* @Injectable()
|
|
60
|
+
* export class PasswordResetMailer extends PasswordResetHandler {
|
|
61
|
+
* constructor(private readonly users: UsersRepository, registry: AuthenticationRegistry) {
|
|
62
|
+
* super();
|
|
63
|
+
* registry.registerHandler('passwordReset', this);
|
|
64
|
+
* }
|
|
65
|
+
* // ...
|
|
66
|
+
* }
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* The registry locks in `AuthenticationModule`'s `onModuleInit` (every
|
|
70
|
+
* provider constructor has run by then), or at the first read if that is
|
|
71
|
+
* earlier. At the lock it fails when a feature has its option without its
|
|
72
|
+
* handler, or the reverse, and it logs the chain and the handlers.
|
|
73
|
+
*/
|
|
74
|
+
let AuthenticationRegistry = AuthenticationRegistry_1 = class AuthenticationRegistry {
|
|
75
|
+
constructor(options, moduleRef) {
|
|
76
|
+
this.options = options;
|
|
77
|
+
this.moduleRef = moduleRef;
|
|
78
|
+
this.registered = [];
|
|
79
|
+
this.handlers = new Map();
|
|
80
|
+
this.chain = [];
|
|
81
|
+
this.locked = false;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Adds a credential provider to the chain the guard runs, in ascending
|
|
85
|
+
* `order` (default `0`). Call it from the constructor of a singleton
|
|
86
|
+
* provider. Throws, registering nothing, for an object without
|
|
87
|
+
* `authenticate()`, an order another provider has (unless `replace` is
|
|
88
|
+
* set), a provider registered twice, and once the registry has locked.
|
|
89
|
+
*/
|
|
90
|
+
registerProvider(provider, { order = 0, replace = false } = {}) {
|
|
91
|
+
const where = 'AuthenticationRegistry.registerProvider()';
|
|
92
|
+
if (provider === null || typeof provider !== 'object' || typeof provider.authenticate !== 'function') {
|
|
93
|
+
throw new TypeError(`${where}: expected a credential provider with an authenticate() method, got ${nameOf(provider)}.`);
|
|
94
|
+
}
|
|
95
|
+
if (typeof order !== 'number' || !Number.isFinite(order)) {
|
|
96
|
+
throw new TypeError(`${where}: \`order\` must be a finite number, got ${String(order)}.`);
|
|
97
|
+
}
|
|
98
|
+
this.assertOpen(where, nameOf(provider));
|
|
99
|
+
if (this.registered.some((entry) => entry.provider === provider)) {
|
|
100
|
+
throw new Error(`${where}: ${nameOf(provider)} is already registered (the same instance, twice).`);
|
|
101
|
+
}
|
|
102
|
+
const taken = this.registered.find((entry) => entry.order === order);
|
|
103
|
+
if (taken && !replace) {
|
|
104
|
+
if (taken.provider.constructor === provider.constructor && provider.constructor !== Object) {
|
|
105
|
+
throw new Error(`${where}: another ${nameOf(provider)} is already registered at order ${order}. Is the class provided in ` +
|
|
106
|
+
'two modules? Provide it once, in the module that owns it.');
|
|
107
|
+
}
|
|
108
|
+
throw new Error(`${where}: ${nameOf(provider)} can't take order ${order}, ${nameOf(taken.provider)} has it. Providers run in ` +
|
|
109
|
+
'ascending `order` and the first that returns a user wins, so each needs its own: ' +
|
|
110
|
+
`\`registerProvider(this, { order: ${Math.max(...this.registered.map((entry) => entry.order)) + 1} })\`.`);
|
|
111
|
+
}
|
|
112
|
+
if (taken) {
|
|
113
|
+
this.registered.splice(this.registered.indexOf(taken), 1);
|
|
114
|
+
}
|
|
115
|
+
this.registered.push({ provider: provider, order });
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Registers the handler of a feature: `registerHandler('passwordReset', this)`.
|
|
119
|
+
* The feature also needs its module option. Throws, registering nothing,
|
|
120
|
+
* for an unknown feature, a handler missing one of its methods, a feature
|
|
121
|
+
* that already has one (unless `replace` is set), and once the registry
|
|
122
|
+
* has locked.
|
|
123
|
+
*/
|
|
124
|
+
registerHandler(feature, handler, { replace = false } = {}) {
|
|
125
|
+
const where = 'AuthenticationRegistry.registerHandler()';
|
|
126
|
+
if (!Object.hasOwn(HANDLERS, feature)) {
|
|
127
|
+
throw new TypeError(`${where}: unknown feature \`${String(feature)}\`. The features are ` +
|
|
128
|
+
`${HANDLER_NAMES.map((name) => `${name} (${HANDLERS[name].className})`).join(', ')}.`);
|
|
129
|
+
}
|
|
130
|
+
const { className, methods } = HANDLERS[feature];
|
|
131
|
+
if (handler === null || typeof handler !== 'object') {
|
|
132
|
+
throw new TypeError(`${where}: expected a ${className} as \`${feature}\`, got ${nameOf(handler)}.`);
|
|
133
|
+
}
|
|
134
|
+
const missing = methods.filter((method) => typeof handler[method] !== 'function');
|
|
135
|
+
if (missing.length > 0) {
|
|
136
|
+
throw new TypeError(`${where}: ${nameOf(handler)} doesn't implement ${className} for \`${feature}\`: ` +
|
|
137
|
+
`${missing.map((method) => `${method}()`).join(', ')} ${missing.length === 1 ? 'is' : 'are'} missing.`);
|
|
138
|
+
}
|
|
139
|
+
this.assertOpen(where, nameOf(handler));
|
|
140
|
+
const previous = this.handlers.get(feature);
|
|
141
|
+
if (previous && !replace) {
|
|
142
|
+
throw new Error(`${where}: ${nameOf(handler)} can't register \`${feature}\`, ` +
|
|
143
|
+
`${previous === handler ? 'it already did (the same instance, twice)' : `${nameOf(previous)} already did`}. ` +
|
|
144
|
+
'Register each feature once, or pass { replace: true } to replace it on purpose (tests, wrappers).');
|
|
145
|
+
}
|
|
146
|
+
this.handlers.set(feature, handler);
|
|
147
|
+
}
|
|
148
|
+
/** The credential providers, in the order the guard runs them. Reading them locks the registry. */
|
|
149
|
+
get providers() {
|
|
150
|
+
this[LOCK_REGISTRY]();
|
|
151
|
+
return this.chain;
|
|
152
|
+
}
|
|
153
|
+
/** The handler registered for a feature, or `undefined`. Reading it locks the registry. */
|
|
154
|
+
handler(feature) {
|
|
155
|
+
this[LOCK_REGISTRY]();
|
|
156
|
+
return this.handlers.get(feature);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* @internal Checks every feature has both its option and its handler,
|
|
160
|
+
* orders the chain, lets the providers check their configuration, and
|
|
161
|
+
* logs what registered. `log: false` for registries built outside a
|
|
162
|
+
* module (tests, the contract suite).
|
|
163
|
+
*/
|
|
164
|
+
[LOCK_REGISTRY]({ log = true } = {}) {
|
|
165
|
+
if (this.locked) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const options = this.options ?? {};
|
|
169
|
+
for (const feature of HANDLER_NAMES) {
|
|
170
|
+
const { className, option } = HANDLERS[feature];
|
|
171
|
+
const handler = this.handlers.get(feature);
|
|
172
|
+
if (options[feature] !== undefined && !handler) {
|
|
173
|
+
throw new Error(`AuthenticationModule: \`${feature}\` is configured, but no ${className} is registered. Write an ` +
|
|
174
|
+
`@Injectable() class that extends ${className}, provide it in one of your modules, and register it from ` +
|
|
175
|
+
`its constructor: \`registry.registerHandler('${feature}', this)\`, with \`registry: AuthenticationRegistry\` injected.`);
|
|
176
|
+
}
|
|
177
|
+
if (handler && options[feature] === undefined) {
|
|
178
|
+
throw new Error(`AuthenticationModule: ${nameOf(handler)} is registered as the \`${feature}\` handler, but the \`${feature}\` ` +
|
|
179
|
+
`option is missing. Configure it in the AuthenticationModule options: \`${option}\`.`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const chain = [...this.registered].sort((a, b) => a.order - b.order).map((entry) => entry.provider);
|
|
183
|
+
// Providers that read module options check them now, not at the first request.
|
|
184
|
+
const resolve = (token) => {
|
|
185
|
+
if (!this.moduleRef) {
|
|
186
|
+
throw new Error(`AuthenticationRegistry: no module to resolve ${String(token?.name ?? token)} from`);
|
|
187
|
+
}
|
|
188
|
+
return this.moduleRef.get(token, { strict: false });
|
|
189
|
+
};
|
|
190
|
+
for (const provider of chain) {
|
|
191
|
+
provider[PROVIDER_INIT]?.(resolve);
|
|
192
|
+
}
|
|
193
|
+
this.chain = Object.freeze(chain);
|
|
194
|
+
this.locked = true;
|
|
195
|
+
if (!log) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const logger = AuthenticationRegistry_1.logger;
|
|
199
|
+
const handlers = HANDLER_NAMES.filter((feature) => this.handlers.has(feature)).map((feature) => `${nameOf(this.handlers.get(feature))} (${feature})`);
|
|
200
|
+
if (chain.length === 0) {
|
|
201
|
+
logger.warn('AuthenticationRegistry: no credential provider is registered, so every route that is not @Public() answers ' +
|
|
202
|
+
'401. Register one from its constructor: `registry.registerProvider(this)`.');
|
|
203
|
+
}
|
|
204
|
+
logger.log(`AuthenticationRegistry: ${chain.length > 0 ? `providers ${chain.map(nameOf).join(', ')}` : 'no providers'}` +
|
|
205
|
+
(handlers.length > 0 ? `; handlers ${handlers.join(', ')}` : ''));
|
|
206
|
+
}
|
|
207
|
+
assertOpen(where, name) {
|
|
208
|
+
if (!this.locked) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
throw new Error(`${where}: ${name} registered after AuthenticationModule initialized (or after the registry was first read). ` +
|
|
212
|
+
'Register from the constructor of a singleton provider: providers of lazy-loaded modules, request-scoped and ' +
|
|
213
|
+
'transient providers, and lifecycle hooks run too late.');
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
AuthenticationRegistry.logger = new Logger('AuthenticationModule');
|
|
217
|
+
AuthenticationRegistry = AuthenticationRegistry_1 = __decorate([
|
|
218
|
+
Injectable(),
|
|
219
|
+
__param(0, Optional()),
|
|
220
|
+
__param(0, Inject(AUTHENTICATION_MODULE_OPTIONS)),
|
|
221
|
+
__param(1, Optional()),
|
|
222
|
+
__metadata("design:paramtypes", [Object, ModuleRef])
|
|
223
|
+
], AuthenticationRegistry);
|
|
224
|
+
export { AuthenticationRegistry };
|
|
225
|
+
/** How a message names a value: its class, or what it is instead of an instance. */
|
|
226
|
+
function nameOf(value) {
|
|
227
|
+
if (typeof value === 'function') {
|
|
228
|
+
return `the class ${value.name || '(anonymous)'} (pass an instance: \`this\`)`;
|
|
229
|
+
}
|
|
230
|
+
if (value === null || typeof value !== 'object') {
|
|
231
|
+
return String(value);
|
|
232
|
+
}
|
|
233
|
+
const name = value.constructor?.name;
|
|
234
|
+
return name && name !== 'Object' ? name : 'an object';
|
|
235
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { PasswordHasherOptions } from '../interfaces/password-hasher-options.interface.js';
|
|
2
|
+
/**
|
|
3
|
+
* @internal The bytes `PasswordHasher` derives from: the password NFKC-normalised, as UTF-8. Throws
|
|
4
|
+
* for anything but a string, for an empty one (which no account should have), and past 4 KiB,
|
|
5
|
+
* which caps what a request can make scrypt read.
|
|
6
|
+
*/
|
|
7
|
+
export declare function encodePassword(password: unknown): Buffer;
|
|
8
|
+
/** @internal Whether `PasswordHasher.hash()` takes `password` (see {@link encodePassword}). */
|
|
9
|
+
export declare function isHashablePassword(password: unknown): password is string;
|
|
10
|
+
/**
|
|
11
|
+
* scrypt password hashing (Node's `crypto.scrypt`, run on the libuv
|
|
12
|
+
* threadpool so it does not block the event loop).
|
|
13
|
+
*
|
|
14
|
+
* The encoded hash is self-describing (`$scrypt$ln=17,r=8,p=1$salt$hash`),
|
|
15
|
+
* so parameters can be raised later: `verify()` reads the parameters from
|
|
16
|
+
* the stored hash and `needsRehash()` tells the sign-in code to store a
|
|
17
|
+
* fresh hash while it has the plaintext.
|
|
18
|
+
*
|
|
19
|
+
* Passwords are NFKC-normalised (NIST SP 800-63B §5.1.1.2) so the same
|
|
20
|
+
* password typed on different keyboards verifies. `hash()` refuses an empty
|
|
21
|
+
* password and one over 4 KiB (a `RangeError`), and `verify()` answers
|
|
22
|
+
* `false` for them without any work; length and strength rules beyond that
|
|
23
|
+
* are the app's to check.
|
|
24
|
+
*
|
|
25
|
+
* The module provides one built from its `password` option. Tests replace
|
|
26
|
+
* it with a cheaper one: `new PasswordHasher({ logN: 10 })`.
|
|
27
|
+
*/
|
|
28
|
+
export declare class PasswordHasher {
|
|
29
|
+
private readonly params;
|
|
30
|
+
private readonly keyLength;
|
|
31
|
+
private readonly saltLength;
|
|
32
|
+
private dummy?;
|
|
33
|
+
constructor(options?: PasswordHasherOptions);
|
|
34
|
+
hash(password: string): Promise<string>;
|
|
35
|
+
/**
|
|
36
|
+
* Constant-time check. Pass `undefined` for an unknown user: a dummy hash
|
|
37
|
+
* is checked instead, so response time does not reveal which accounts
|
|
38
|
+
* exist. The first such call makes the dummy hash, which costs the same
|
|
39
|
+
* one derivation as every later check.
|
|
40
|
+
*/
|
|
41
|
+
verify(password: string, encoded: string | null | undefined): Promise<boolean>;
|
|
42
|
+
/** `true` when the stored hash uses weaker or different parameters than configured. */
|
|
43
|
+
needsRehash(encoded: string): boolean;
|
|
44
|
+
/** The one expensive step: scrypt on the libuv threadpool. */
|
|
45
|
+
private derive;
|
|
46
|
+
private encodeInput;
|
|
47
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { randomBytes, scrypt as scryptCb, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
// $scrypt$ln=17,r=8,p=1$<salt>$<hash>, unpadded standard base64 as in the PHC string format.
|
|
3
|
+
const FORMAT = /^\$scrypt\$ln=(\d{1,2}),r=(\d{1,3}),p=(\d{1,3})\$([A-Za-z0-9+/]{16,})\$([A-Za-z0-9+/]{16,})$/;
|
|
4
|
+
const MAX_PASSWORD_BYTES = 4096;
|
|
5
|
+
function scrypt(password, salt, keyLength, { logN, r, p }) {
|
|
6
|
+
const N = 2 ** logN;
|
|
7
|
+
const options = { N, r, p, maxmem: 256 * N * r + 1024 * 1024 };
|
|
8
|
+
return new Promise((resolve, reject) => scryptCb(password, salt, keyLength, options, (err, key) => (err ? reject(err) : resolve(key))));
|
|
9
|
+
}
|
|
10
|
+
const b64 = (buf) => buf.toString('base64').replace(/=+$/, '');
|
|
11
|
+
/**
|
|
12
|
+
* @internal The bytes `PasswordHasher` derives from: the password NFKC-normalised, as UTF-8. Throws
|
|
13
|
+
* for anything but a string, for an empty one (which no account should have), and past 4 KiB,
|
|
14
|
+
* which caps what a request can make scrypt read.
|
|
15
|
+
*/
|
|
16
|
+
export function encodePassword(password) {
|
|
17
|
+
if (typeof password !== 'string') {
|
|
18
|
+
throw new TypeError('password must be a string');
|
|
19
|
+
}
|
|
20
|
+
// Before normalising, which can multiply the length (U+FDFA is 18 characters in NFKC): a string
|
|
21
|
+
// longer than the limit in UTF-16 units is longer in UTF-8 bytes too.
|
|
22
|
+
if (password.length > MAX_PASSWORD_BYTES) {
|
|
23
|
+
throw new RangeError('password too long');
|
|
24
|
+
}
|
|
25
|
+
const input = Buffer.from(password.normalize('NFKC'), 'utf8');
|
|
26
|
+
if (input.length === 0) {
|
|
27
|
+
throw new RangeError('password is empty');
|
|
28
|
+
}
|
|
29
|
+
if (input.length > MAX_PASSWORD_BYTES) {
|
|
30
|
+
throw new RangeError('password too long');
|
|
31
|
+
}
|
|
32
|
+
return input;
|
|
33
|
+
}
|
|
34
|
+
/** @internal Whether `PasswordHasher.hash()` takes `password` (see {@link encodePassword}). */
|
|
35
|
+
export function isHashablePassword(password) {
|
|
36
|
+
try {
|
|
37
|
+
encodePassword(password);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* scrypt password hashing (Node's `crypto.scrypt`, run on the libuv
|
|
46
|
+
* threadpool so it does not block the event loop).
|
|
47
|
+
*
|
|
48
|
+
* The encoded hash is self-describing (`$scrypt$ln=17,r=8,p=1$salt$hash`),
|
|
49
|
+
* so parameters can be raised later: `verify()` reads the parameters from
|
|
50
|
+
* the stored hash and `needsRehash()` tells the sign-in code to store a
|
|
51
|
+
* fresh hash while it has the plaintext.
|
|
52
|
+
*
|
|
53
|
+
* Passwords are NFKC-normalised (NIST SP 800-63B §5.1.1.2) so the same
|
|
54
|
+
* password typed on different keyboards verifies. `hash()` refuses an empty
|
|
55
|
+
* password and one over 4 KiB (a `RangeError`), and `verify()` answers
|
|
56
|
+
* `false` for them without any work; length and strength rules beyond that
|
|
57
|
+
* are the app's to check.
|
|
58
|
+
*
|
|
59
|
+
* The module provides one built from its `password` option. Tests replace
|
|
60
|
+
* it with a cheaper one: `new PasswordHasher({ logN: 10 })`.
|
|
61
|
+
*/
|
|
62
|
+
export class PasswordHasher {
|
|
63
|
+
constructor(options = {}) {
|
|
64
|
+
this.params = { logN: options.logN ?? 17, r: options.r ?? 8, p: options.p ?? 1 };
|
|
65
|
+
this.keyLength = options.keyLength ?? 32;
|
|
66
|
+
this.saltLength = options.saltLength ?? 16;
|
|
67
|
+
if (!isSane(this.params)) {
|
|
68
|
+
throw new RangeError('PasswordHasher: unsupported scrypt parameters');
|
|
69
|
+
}
|
|
70
|
+
// What `parse()` reads back: a hash made with anything else would never verify.
|
|
71
|
+
for (const [name, value] of [['keyLength', this.keyLength], ['saltLength', this.saltLength]]) {
|
|
72
|
+
if (!Number.isInteger(value) || value < 16 || value > 64) {
|
|
73
|
+
throw new RangeError(`PasswordHasher: \`${name}\` must be an integer from 16 to 64 bytes, got ${String(value)}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async hash(password) {
|
|
78
|
+
const input = this.encodeInput(password);
|
|
79
|
+
const salt = randomBytes(this.saltLength);
|
|
80
|
+
const key = await this.derive(input, salt, this.keyLength, this.params);
|
|
81
|
+
const { logN, r, p } = this.params;
|
|
82
|
+
return `$scrypt$ln=${logN},r=${r},p=${p}$${b64(salt)}$${b64(key)}`;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Constant-time check. Pass `undefined` for an unknown user: a dummy hash
|
|
86
|
+
* is checked instead, so response time does not reveal which accounts
|
|
87
|
+
* exist. The first such call makes the dummy hash, which costs the same
|
|
88
|
+
* one derivation as every later check.
|
|
89
|
+
*/
|
|
90
|
+
async verify(password, encoded) {
|
|
91
|
+
if (!encoded) {
|
|
92
|
+
if (!this.dummy) {
|
|
93
|
+
// A failure (a cancelled worker, memory) is not kept: the next unknown user tries again.
|
|
94
|
+
this.dummy = this.hash('dummy password for timing equalisation').catch((error) => {
|
|
95
|
+
this.dummy = undefined;
|
|
96
|
+
throw error;
|
|
97
|
+
});
|
|
98
|
+
await this.dummy;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
await this.verify(password, await this.dummy);
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
const parsed = parse(encoded);
|
|
106
|
+
if (!parsed) {
|
|
107
|
+
// A disabled account (`!`), a hash from another scheme: the same work as an unknown user, so
|
|
108
|
+
// the answer's timing does not single these accounts out.
|
|
109
|
+
return this.verify(password, undefined);
|
|
110
|
+
}
|
|
111
|
+
let input;
|
|
112
|
+
try {
|
|
113
|
+
input = this.encodeInput(password);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
const actual = await this.derive(input, parsed.salt, parsed.hash.length, parsed.params);
|
|
119
|
+
return timingSafeEqual(actual, parsed.hash);
|
|
120
|
+
}
|
|
121
|
+
/** `true` when the stored hash uses weaker or different parameters than configured. */
|
|
122
|
+
needsRehash(encoded) {
|
|
123
|
+
const parsed = parse(encoded);
|
|
124
|
+
if (!parsed) {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
const { logN, r, p } = parsed.params;
|
|
128
|
+
return (logN !== this.params.logN ||
|
|
129
|
+
r !== this.params.r ||
|
|
130
|
+
p !== this.params.p ||
|
|
131
|
+
parsed.hash.length !== this.keyLength ||
|
|
132
|
+
parsed.salt.length < this.saltLength);
|
|
133
|
+
}
|
|
134
|
+
/** The one expensive step: scrypt on the libuv threadpool. */
|
|
135
|
+
derive(input, salt, keyLength, params) {
|
|
136
|
+
return scrypt(input, salt, keyLength, params);
|
|
137
|
+
}
|
|
138
|
+
encodeInput(password) {
|
|
139
|
+
return encodePassword(password);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function isSane({ logN, r, p }) {
|
|
143
|
+
// Bounds also cap what a tampered stored hash can make us compute: 1 GiB of memory (128·N·r),
|
|
144
|
+
// and 16 times the work of the default parameters (N·r·p).
|
|
145
|
+
return ([logN, r, p].every(Number.isInteger) &&
|
|
146
|
+
logN >= 10 &&
|
|
147
|
+
logN <= 22 &&
|
|
148
|
+
r >= 1 &&
|
|
149
|
+
r <= 32 &&
|
|
150
|
+
p >= 1 &&
|
|
151
|
+
p <= 16 &&
|
|
152
|
+
2 ** logN * r <= 2 ** 23 &&
|
|
153
|
+
2 ** logN * r * p <= 2 ** 24);
|
|
154
|
+
}
|
|
155
|
+
function parse(encoded) {
|
|
156
|
+
const match = FORMAT.exec(encoded);
|
|
157
|
+
if (!match) {
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
const params = { logN: Number(match[1]), r: Number(match[2]), p: Number(match[3]) };
|
|
161
|
+
if (!isSane(params)) {
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
const salt = Buffer.from(match[4], 'base64');
|
|
165
|
+
const hash = Buffer.from(match[5], 'base64');
|
|
166
|
+
if (hash.length < 16 || hash.length > 64 || salt.length > 64) {
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
return { params, salt, hash };
|
|
170
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { IncomingMessage } from 'node:http';
|
|
2
|
+
import type { AuthenticationResult } from '../interfaces/authentication-result.interface.js';
|
|
3
|
+
import { AuthenticationRegistry } from './authentication-registry.service.js';
|
|
4
|
+
interface ClosableClient {
|
|
5
|
+
close?(code?: number, reason?: string): void;
|
|
6
|
+
disconnect?(close?: boolean): void;
|
|
7
|
+
[key: PropertyKey]: any;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Handshake authentication for gateways. Nest does not run guards on
|
|
11
|
+
* `handleConnection`, so the gateway calls this:
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* async handleConnection(client: WebSocket, request: IncomingMessage) {
|
|
15
|
+
* await this.wsAuth.authenticateConnection(client, request);
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* It stores the upgrade request on `client.request` (the `ws` library does
|
|
20
|
+
* not), so providers can read headers again for each message; the global
|
|
21
|
+
* guard re-authenticates every message, so a revoked session stops working
|
|
22
|
+
* mid-connection.
|
|
23
|
+
*
|
|
24
|
+
* It never rejects: Nest drops the promise `handleConnection` returns, so a
|
|
25
|
+
* rejection would be unhandled, which ends the Node.js process. A provider
|
|
26
|
+
* or store that fails (a Redis outage) is logged, and the connection is
|
|
27
|
+
* closed with 1011.
|
|
28
|
+
*/
|
|
29
|
+
export declare class WsAuthenticator {
|
|
30
|
+
private readonly registry;
|
|
31
|
+
private static readonly logger;
|
|
32
|
+
constructor(registry: AuthenticationRegistry);
|
|
33
|
+
authenticateConnection(client: ClosableClient, request?: IncomingMessage, { required }?: {
|
|
34
|
+
required?: boolean;
|
|
35
|
+
}): Promise<AuthenticationResult<unknown, unknown> | null>;
|
|
36
|
+
private run;
|
|
37
|
+
}
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,92 @@
|
|
|
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 WsAuthenticator_1;
|
|
11
|
+
import { Injectable, Logger } from '@nestjs/common';
|
|
12
|
+
import { ExecutionContextHost } from '@nestjs/core/internal';
|
|
13
|
+
import { setAuthState } from '../utils/auth-state.util.js';
|
|
14
|
+
import { AuthenticationError } from '../errors/authentication.error.js';
|
|
15
|
+
import { AuthenticationRegistry } from './authentication-registry.service.js';
|
|
16
|
+
class WsHandshake {
|
|
17
|
+
handleConnection() { }
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Handshake authentication for gateways. Nest does not run guards on
|
|
21
|
+
* `handleConnection`, so the gateway calls this:
|
|
22
|
+
*
|
|
23
|
+
* ```ts
|
|
24
|
+
* async handleConnection(client: WebSocket, request: IncomingMessage) {
|
|
25
|
+
* await this.wsAuth.authenticateConnection(client, request);
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* It stores the upgrade request on `client.request` (the `ws` library does
|
|
30
|
+
* not), so providers can read headers again for each message; the global
|
|
31
|
+
* guard re-authenticates every message, so a revoked session stops working
|
|
32
|
+
* mid-connection.
|
|
33
|
+
*
|
|
34
|
+
* It never rejects: Nest drops the promise `handleConnection` returns, so a
|
|
35
|
+
* rejection would be unhandled, which ends the Node.js process. A provider
|
|
36
|
+
* or store that fails (a Redis outage) is logged, and the connection is
|
|
37
|
+
* closed with 1011.
|
|
38
|
+
*/
|
|
39
|
+
let WsAuthenticator = WsAuthenticator_1 = class WsAuthenticator {
|
|
40
|
+
constructor(registry) {
|
|
41
|
+
this.registry = registry;
|
|
42
|
+
}
|
|
43
|
+
async authenticateConnection(client, request, { required = true } = {}) {
|
|
44
|
+
if (request && !client.handshake) {
|
|
45
|
+
client.request = request;
|
|
46
|
+
}
|
|
47
|
+
const context = new ExecutionContextHost([client, undefined], WsHandshake, WsHandshake.prototype.handleConnection);
|
|
48
|
+
context.setType('ws');
|
|
49
|
+
let result = null;
|
|
50
|
+
try {
|
|
51
|
+
result = await this.run(context);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (!(error instanceof AuthenticationError)) {
|
|
55
|
+
WsAuthenticator_1.logger.error('Authenticating a WebSocket connection failed', error?.stack);
|
|
56
|
+
close(client, 1011, 'Internal Error'); // RFC 6455 §7.4.1
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (result?.mfa === 'pending') {
|
|
61
|
+
result = null;
|
|
62
|
+
}
|
|
63
|
+
setAuthState(context, result);
|
|
64
|
+
if (!result && required) {
|
|
65
|
+
close(client, 1008, 'Unauthorized'); // policy violation
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
async run(context) {
|
|
70
|
+
for (const provider of this.registry.providers) {
|
|
71
|
+
const result = await provider.authenticate(context);
|
|
72
|
+
if (result?.user) {
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
WsAuthenticator.logger = new Logger('WsAuthenticator');
|
|
80
|
+
WsAuthenticator = WsAuthenticator_1 = __decorate([
|
|
81
|
+
Injectable(),
|
|
82
|
+
__metadata("design:paramtypes", [AuthenticationRegistry])
|
|
83
|
+
], WsAuthenticator);
|
|
84
|
+
export { WsAuthenticator };
|
|
85
|
+
function close(client, code, reason) {
|
|
86
|
+
if (client.close) {
|
|
87
|
+
client.close(code, reason);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
client.disconnect?.(true); // socket.io
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads one cookie from a raw `Cookie` header (RFC 6265 §5.4), so neither
|
|
3
|
+
* `cookie-parser` nor `@fastify/cookie` is needed. The first occurrence
|
|
4
|
+
* wins, as browsers send the most specific path first.
|
|
5
|
+
*/
|
|
6
|
+
export declare function readCookie(header: string | undefined, name: string): string | undefined;
|
|
7
|
+
export interface CookieAttributes {
|
|
8
|
+
/** Defaults to `true`. Turn off only for plain-HTTP development on a host other than `localhost`. */
|
|
9
|
+
secure?: boolean;
|
|
10
|
+
/** Defaults to `lax`. */
|
|
11
|
+
sameSite?: 'lax' | 'strict' | 'none';
|
|
12
|
+
/** Defaults to `/`. */
|
|
13
|
+
path?: string;
|
|
14
|
+
domain?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Whether browsers accept a `__Host-` cookie with these attributes: Secure,
|
|
18
|
+
* `Path=/` and no `Domain`. Such a cookie can only be set by the host
|
|
19
|
+
* itself, so a sibling subdomain cannot plant one (cookie tossing).
|
|
20
|
+
*/
|
|
21
|
+
export declare function allowsHostPrefix(attributes?: CookieAttributes): boolean;
|
|
22
|
+
/** `__Host-<name>` when the attributes allow it, `<name>` otherwise. */
|
|
23
|
+
export declare function defaultCookieName(name: string, attributes?: CookieAttributes): string;
|
|
24
|
+
/**
|
|
25
|
+
* Fails at startup on a cookie browsers would silently drop: a prefixed name
|
|
26
|
+
* its attributes break, or `SameSite=None` without `Secure` (rejected by
|
|
27
|
+
* every browser: the cookie is never stored, so every sign-in looks like it
|
|
28
|
+
* worked and nothing stays signed in). `option` is the options object the
|
|
29
|
+
* message names (`session`).
|
|
30
|
+
*/
|
|
31
|
+
export declare function assertCookieAttributes(name: string, attributes: CookieAttributes | undefined, option: string): void;
|
|
32
|
+
export declare function serializeCookie(name: string, value: string, attributes: CookieAttributes & {
|
|
33
|
+
maxAge: number;
|
|
34
|
+
}): string;
|