@fluojs/passport 1.0.0-beta.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 +21 -0
- package/README.ko.md +179 -0
- package/README.md +179 -0
- package/dist/account/account-linking.d.ts +91 -0
- package/dist/account/account-linking.d.ts.map +1 -0
- package/dist/account/account-linking.js +145 -0
- package/dist/adapters/passport-js.d.ts +77 -0
- package/dist/adapters/passport-js.d.ts.map +1 -0
- package/dist/adapters/passport-js.js +230 -0
- package/dist/cookie/cookie-auth-module.d.ts +65 -0
- package/dist/cookie/cookie-auth-module.d.ts.map +1 -0
- package/dist/cookie/cookie-auth-module.js +84 -0
- package/dist/cookie/cookie-auth.d.ts +40 -0
- package/dist/cookie/cookie-auth.d.ts.map +1 -0
- package/dist/cookie/cookie-auth.js +101 -0
- package/dist/cookie/cookie-manager.d.ts +34 -0
- package/dist/cookie/cookie-manager.d.ts.map +1 -0
- package/dist/cookie/cookie-manager.js +93 -0
- package/dist/decorators.d.ts +42 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +95 -0
- package/dist/errors.d.ts +26 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +48 -0
- package/dist/guard.d.ts +39 -0
- package/dist/guard.d.ts.map +1 -0
- package/dist/guard.js +124 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/internal-tokens.d.ts +3 -0
- package/dist/internal-tokens.d.ts.map +1 -0
- package/dist/internal-tokens.js +4 -0
- package/dist/metadata.d.ts +6 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +105 -0
- package/dist/module.d.ts +36 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +63 -0
- package/dist/refresh/jwt-refresh-token-adapter.d.ts +30 -0
- package/dist/refresh/jwt-refresh-token-adapter.d.ts.map +1 -0
- package/dist/refresh/jwt-refresh-token-adapter.js +103 -0
- package/dist/refresh/refresh-token.d.ts +96 -0
- package/dist/refresh/refresh-token.d.ts.map +1 -0
- package/dist/refresh/refresh-token.js +178 -0
- package/dist/scope.d.ts +5 -0
- package/dist/scope.d.ts.map +1 -0
- package/dist/scope.js +44 -0
- package/dist/status.d.ts +36 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +173 -0
- package/dist/types.d.ts +36 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { normalizeCookieAuthOptions } from './cookie-auth.js';
|
|
2
|
+
export const DEFAULT_COOKIE_OPTIONS = {
|
|
3
|
+
httpOnly: true,
|
|
4
|
+
secure: true,
|
|
5
|
+
sameSite: 'strict',
|
|
6
|
+
path: '/',
|
|
7
|
+
domain: undefined,
|
|
8
|
+
maxAge: undefined
|
|
9
|
+
};
|
|
10
|
+
function buildCookieHeader(name, value, options) {
|
|
11
|
+
const parts = [`${name}=${value}`];
|
|
12
|
+
if (options.maxAge !== undefined && options.maxAge >= 0) {
|
|
13
|
+
parts.push(`Max-Age=${options.maxAge}`);
|
|
14
|
+
}
|
|
15
|
+
if (options.path) {
|
|
16
|
+
parts.push(`Path=${options.path}`);
|
|
17
|
+
}
|
|
18
|
+
if (options.domain) {
|
|
19
|
+
parts.push(`Domain=${options.domain}`);
|
|
20
|
+
}
|
|
21
|
+
if (options.secure) {
|
|
22
|
+
parts.push('Secure');
|
|
23
|
+
}
|
|
24
|
+
if (options.httpOnly) {
|
|
25
|
+
parts.push('HttpOnly');
|
|
26
|
+
}
|
|
27
|
+
if (options.sameSite) {
|
|
28
|
+
parts.push(`SameSite=${options.sameSite.charAt(0).toUpperCase() + options.sameSite.slice(1)}`);
|
|
29
|
+
}
|
|
30
|
+
return parts.join('; ');
|
|
31
|
+
}
|
|
32
|
+
function buildClearCookieHeader(name, options) {
|
|
33
|
+
return buildCookieHeader(name, '', {
|
|
34
|
+
...options,
|
|
35
|
+
maxAge: 0
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
export class CookieManager {
|
|
39
|
+
options;
|
|
40
|
+
cookieOptions;
|
|
41
|
+
constructor(config) {
|
|
42
|
+
this.options = normalizeCookieAuthOptions(config);
|
|
43
|
+
this.cookieOptions = {
|
|
44
|
+
httpOnly: config?.cookieOptions?.httpOnly ?? DEFAULT_COOKIE_OPTIONS.httpOnly,
|
|
45
|
+
secure: config?.cookieOptions?.secure ?? DEFAULT_COOKIE_OPTIONS.secure,
|
|
46
|
+
sameSite: config?.cookieOptions?.sameSite ?? DEFAULT_COOKIE_OPTIONS.sameSite,
|
|
47
|
+
path: config?.cookieOptions?.path ?? DEFAULT_COOKIE_OPTIONS.path,
|
|
48
|
+
domain: config?.cookieOptions?.domain ?? DEFAULT_COOKIE_OPTIONS.domain,
|
|
49
|
+
maxAge: config?.cookieOptions?.maxAge ?? DEFAULT_COOKIE_OPTIONS.maxAge
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
setAccessTokenCookie(response, token, ttlSeconds) {
|
|
53
|
+
const cookie = buildCookieHeader(this.options.accessTokenCookieName, token, {
|
|
54
|
+
...this.cookieOptions,
|
|
55
|
+
maxAge: ttlSeconds ?? this.cookieOptions.maxAge
|
|
56
|
+
});
|
|
57
|
+
this.appendSetCookie(response, cookie);
|
|
58
|
+
}
|
|
59
|
+
setRefreshTokenCookie(response, token, ttlSeconds) {
|
|
60
|
+
const cookie = buildCookieHeader(this.options.refreshTokenCookieName, token, {
|
|
61
|
+
...this.cookieOptions,
|
|
62
|
+
maxAge: ttlSeconds ?? this.cookieOptions.maxAge
|
|
63
|
+
});
|
|
64
|
+
this.appendSetCookie(response, cookie);
|
|
65
|
+
}
|
|
66
|
+
clearAccessTokenCookie(response) {
|
|
67
|
+
const cookie = buildClearCookieHeader(this.options.accessTokenCookieName, this.cookieOptions);
|
|
68
|
+
this.appendSetCookie(response, cookie);
|
|
69
|
+
}
|
|
70
|
+
clearRefreshTokenCookie(response) {
|
|
71
|
+
const cookie = buildClearCookieHeader(this.options.refreshTokenCookieName, this.cookieOptions);
|
|
72
|
+
this.appendSetCookie(response, cookie);
|
|
73
|
+
}
|
|
74
|
+
clearAllCookies(response) {
|
|
75
|
+
this.clearAccessTokenCookie(response);
|
|
76
|
+
this.clearRefreshTokenCookie(response);
|
|
77
|
+
}
|
|
78
|
+
setAuthCookies(response, accessToken, accessTokenTtlSeconds, refreshToken, refreshTokenTtlSeconds) {
|
|
79
|
+
this.setAccessTokenCookie(response, accessToken, accessTokenTtlSeconds);
|
|
80
|
+
if (refreshToken) {
|
|
81
|
+
this.setRefreshTokenCookie(response, refreshToken, refreshTokenTtlSeconds);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
appendSetCookie(response, cookie) {
|
|
85
|
+
const existingCookies = response.headers['Set-Cookie'];
|
|
86
|
+
const cookies = Array.isArray(existingCookies) ? existingCookies : existingCookies ? [existingCookies] : [];
|
|
87
|
+
cookies.push(cookie);
|
|
88
|
+
response.setHeader('Set-Cookie', cookies.length === 1 ? cookies[0] : cookies);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export function createCookieManager(config) {
|
|
92
|
+
return new CookieManager(config);
|
|
93
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
type StandardClassDecoratorFn = (value: Function, context: ClassDecoratorContext) => void;
|
|
2
|
+
type StandardMethodDecoratorFn = (value: Function, context: ClassMethodDecoratorContext) => void;
|
|
3
|
+
type ClassOrMethodDecoratorLike = StandardClassDecoratorFn & StandardMethodDecoratorFn;
|
|
4
|
+
/**
|
|
5
|
+
* Declares which registered auth strategy should protect a controller or route handler.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* This decorator also applies {@link AuthGuard}, so callers do not need to add a
|
|
9
|
+
* separate `@UseGuards(AuthGuard)` in the common passport flow.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* @UseAuth('jwt')
|
|
14
|
+
* @Get('/profile')
|
|
15
|
+
* getProfile() {}
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @param strategy Strategy name previously registered through `PassportModule.forRoot(...)`.
|
|
19
|
+
* @returns A class-or-method decorator that stores the auth requirement metadata.
|
|
20
|
+
*/
|
|
21
|
+
export declare function UseAuth(strategy: string): ClassOrMethodDecoratorLike;
|
|
22
|
+
/**
|
|
23
|
+
* Declares scope requirements that `AuthGuard` must enforce after authentication succeeds.
|
|
24
|
+
*
|
|
25
|
+
* @remarks
|
|
26
|
+
* Scope requirements merge with any controller-level auth metadata, so method
|
|
27
|
+
* decorators can narrow access without redefining the base strategy.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* @UseAuth('jwt')
|
|
32
|
+
* @RequireScopes('profile:read')
|
|
33
|
+
* @Get('/profile')
|
|
34
|
+
* getProfile() {}
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @param scopes Scope values that must all be present on the resolved principal.
|
|
38
|
+
* @returns A class-or-method decorator that appends scope requirements to auth metadata.
|
|
39
|
+
*/
|
|
40
|
+
export declare function RequireScopes(...scopes: string[]): ClassOrMethodDecoratorLike;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=decorators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAQA,KAAK,wBAAwB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;AAC1F,KAAK,yBAAyB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,2BAA2B,KAAK,IAAI,CAAC;AACjG,KAAK,0BAA0B,GAAG,wBAAwB,GAAG,yBAAyB,CAAC;AAuEvF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,0BAA0B,CAEpE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,0BAA0B,CAE7E"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { UseGuards } from '@fluojs/http';
|
|
2
|
+
import { AuthGuard } from './guard.js';
|
|
3
|
+
import { getOwnAuthRequirement } from './metadata.js';
|
|
4
|
+
import { mergeAuthRequirements } from './scope.js';
|
|
5
|
+
const standardClassRequirementKey = Symbol.for('fluo.passport.standard.class-auth');
|
|
6
|
+
const standardMethodRequirementKey = Symbol.for('fluo.passport.standard.method-auth');
|
|
7
|
+
function isStandardClassContext(context) {
|
|
8
|
+
return typeof context === 'object' && context !== null && 'kind' in context && context.kind === 'class';
|
|
9
|
+
}
|
|
10
|
+
function getStandardMetadataBag(metadata) {
|
|
11
|
+
return metadata;
|
|
12
|
+
}
|
|
13
|
+
function defineStandardAuthRequirement(metadata, requirement, propertyKey) {
|
|
14
|
+
const bag = getStandardMetadataBag(metadata);
|
|
15
|
+
if (propertyKey === undefined) {
|
|
16
|
+
const merged = mergeAuthRequirements(bag[standardClassRequirementKey] ?? undefined, requirement);
|
|
17
|
+
if (merged) {
|
|
18
|
+
bag[standardClassRequirementKey] = merged;
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
delete bag[standardClassRequirementKey];
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const current = bag[standardMethodRequirementKey];
|
|
25
|
+
const map = current ?? new Map();
|
|
26
|
+
const merged = mergeAuthRequirements(map.get(propertyKey), requirement);
|
|
27
|
+
if (merged) {
|
|
28
|
+
map.set(propertyKey, merged);
|
|
29
|
+
} else {
|
|
30
|
+
map.delete(propertyKey);
|
|
31
|
+
}
|
|
32
|
+
bag[standardMethodRequirementKey] = map;
|
|
33
|
+
}
|
|
34
|
+
function applyAuthRequirement(targetOrValue, contextOrPropertyKey, patch) {
|
|
35
|
+
if (isStandardClassContext(contextOrPropertyKey)) {
|
|
36
|
+
defineStandardAuthRequirement(contextOrPropertyKey.metadata, mergeAuthRequirements(getOwnAuthRequirement(targetOrValue), patch));
|
|
37
|
+
UseGuards(AuthGuard)(targetOrValue, contextOrPropertyKey);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
defineStandardAuthRequirement(contextOrPropertyKey.metadata, patch, contextOrPropertyKey.name);
|
|
41
|
+
UseGuards(AuthGuard)(targetOrValue, contextOrPropertyKey);
|
|
42
|
+
}
|
|
43
|
+
function createAuthRequirementDecorator(patch) {
|
|
44
|
+
const decorator = (targetOrValue, contextOrPropertyKey) => {
|
|
45
|
+
applyAuthRequirement(targetOrValue, contextOrPropertyKey, patch);
|
|
46
|
+
};
|
|
47
|
+
return decorator;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Declares which registered auth strategy should protect a controller or route handler.
|
|
52
|
+
*
|
|
53
|
+
* @remarks
|
|
54
|
+
* This decorator also applies {@link AuthGuard}, so callers do not need to add a
|
|
55
|
+
* separate `@UseGuards(AuthGuard)` in the common passport flow.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* @UseAuth('jwt')
|
|
60
|
+
* @Get('/profile')
|
|
61
|
+
* getProfile() {}
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* @param strategy Strategy name previously registered through `PassportModule.forRoot(...)`.
|
|
65
|
+
* @returns A class-or-method decorator that stores the auth requirement metadata.
|
|
66
|
+
*/
|
|
67
|
+
export function UseAuth(strategy) {
|
|
68
|
+
return createAuthRequirementDecorator({
|
|
69
|
+
strategy
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Declares scope requirements that `AuthGuard` must enforce after authentication succeeds.
|
|
75
|
+
*
|
|
76
|
+
* @remarks
|
|
77
|
+
* Scope requirements merge with any controller-level auth metadata, so method
|
|
78
|
+
* decorators can narrow access without redefining the base strategy.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* @UseAuth('jwt')
|
|
83
|
+
* @RequireScopes('profile:read')
|
|
84
|
+
* @Get('/profile')
|
|
85
|
+
* getProfile() {}
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* @param scopes Scope values that must all be present on the resolved principal.
|
|
89
|
+
* @returns A class-or-method decorator that appends scope requirements to auth metadata.
|
|
90
|
+
*/
|
|
91
|
+
export function RequireScopes(...scopes) {
|
|
92
|
+
return createAuthRequirementDecorator({
|
|
93
|
+
scopes
|
|
94
|
+
});
|
|
95
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { FluoError, type FluoErrorOptions } from '@fluojs/core';
|
|
2
|
+
/**
|
|
3
|
+
* Error thrown when a requested authentication strategy cannot be resolved.
|
|
4
|
+
*/
|
|
5
|
+
export declare class AuthStrategyResolutionError extends FluoError {
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Error thrown when an anonymous user attempts to access a protected resource.
|
|
10
|
+
*/
|
|
11
|
+
export declare class AuthenticationRequiredError extends FluoError {
|
|
12
|
+
constructor(message?: string, options?: Omit<FluoErrorOptions, 'code'>);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Error thrown when authentication credentials are provided but invalid.
|
|
16
|
+
*/
|
|
17
|
+
export declare class AuthenticationFailedError extends FluoError {
|
|
18
|
+
constructor(message?: string, options?: Omit<FluoErrorOptions, 'code'>);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Error thrown when an authentication token is well-formed but expired.
|
|
22
|
+
*/
|
|
23
|
+
export declare class AuthenticationExpiredError extends FluoError {
|
|
24
|
+
constructor(message?: string, options?: Omit<FluoErrorOptions, 'code'>);
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhE;;GAEG;AACH,qBAAa,2BAA4B,SAAQ,SAAS;gBAC5C,OAAO,EAAE,MAAM;CAG5B;AAED;;GAEG;AACH,qBAAa,2BAA4B,SAAQ,SAAS;gBAC5C,OAAO,SAA6B,EAAE,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAM;CAG/F;AAED;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;gBAC1C,OAAO,SAA2B,EAAE,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAM;CAG7F;AAED;;GAEG;AACH,qBAAa,0BAA2B,SAAQ,SAAS;gBAC3C,OAAO,SAAsC,EAAE,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAM;CAGxG"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { FluoError } from '@fluojs/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Error thrown when a requested authentication strategy cannot be resolved.
|
|
5
|
+
*/
|
|
6
|
+
export class AuthStrategyResolutionError extends FluoError {
|
|
7
|
+
constructor(message) {
|
|
8
|
+
super(message, {
|
|
9
|
+
code: 'AUTH_STRATEGY_RESOLUTION_ERROR'
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Error thrown when an anonymous user attempts to access a protected resource.
|
|
16
|
+
*/
|
|
17
|
+
export class AuthenticationRequiredError extends FluoError {
|
|
18
|
+
constructor(message = 'Authentication required.', options = {}) {
|
|
19
|
+
super(message, {
|
|
20
|
+
...options,
|
|
21
|
+
code: 'AUTHENTICATION_REQUIRED'
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Error thrown when authentication credentials are provided but invalid.
|
|
28
|
+
*/
|
|
29
|
+
export class AuthenticationFailedError extends FluoError {
|
|
30
|
+
constructor(message = 'Authentication failed.', options = {}) {
|
|
31
|
+
super(message, {
|
|
32
|
+
...options,
|
|
33
|
+
code: 'AUTHENTICATION_FAILED'
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Error thrown when an authentication token is well-formed but expired.
|
|
40
|
+
*/
|
|
41
|
+
export class AuthenticationExpiredError extends FluoError {
|
|
42
|
+
constructor(message = 'Authentication token has expired.', options = {}) {
|
|
43
|
+
super(message, {
|
|
44
|
+
...options,
|
|
45
|
+
code: 'AUTHENTICATION_EXPIRED'
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
package/dist/guard.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type GuardContext } from '@fluojs/http';
|
|
2
|
+
import type { AuthGuardContract, AuthStrategyRegistry, PassportModuleOptions } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* HTTP guard that resolves the active auth strategy, authenticates the request,
|
|
5
|
+
* and writes the resulting principal back to `requestContext.principal`.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* `AuthGuard` preserves the public contract documented in `@fluojs/passport`:
|
|
9
|
+
* authentication failures become canonical `401 Unauthorized` responses, scope
|
|
10
|
+
* mismatches become `403 Forbidden`, and strategies may short-circuit the
|
|
11
|
+
* response by returning `{ handled: true }` after committing the response.
|
|
12
|
+
*/
|
|
13
|
+
export declare class AuthGuard implements AuthGuardContract {
|
|
14
|
+
private readonly strategies;
|
|
15
|
+
private readonly options;
|
|
16
|
+
constructor(strategies?: AuthStrategyRegistry, options?: PassportModuleOptions);
|
|
17
|
+
/**
|
|
18
|
+
* Executes the configured auth strategy for the current route and enforces any declared scopes.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* @Controller('/profile')
|
|
23
|
+
* class ProfileController {
|
|
24
|
+
* @Get('/')
|
|
25
|
+
* @UseAuth('jwt')
|
|
26
|
+
* @RequireScopes('profile:read')
|
|
27
|
+
* getProfile() {}
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @param context HTTP guard context for the active handler invocation.
|
|
32
|
+
* @returns `true` when the request may continue through the HTTP pipeline.
|
|
33
|
+
* @throws {AuthStrategyResolutionError} When no active strategy can be determined or resolved.
|
|
34
|
+
* @throws {UnauthorizedException} When the strategy reports missing, expired, or invalid authentication.
|
|
35
|
+
* @throws {ForbiddenException} When the authenticated principal is missing required scopes.
|
|
36
|
+
*/
|
|
37
|
+
canActivate(context: GuardContext): Promise<true>;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6C,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAa5F,OAAO,KAAK,EACV,iBAAiB,EAIjB,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAkCpB;;;;;;;;;GASG;AACH,qBACa,SAAU,YAAW,iBAAiB;IAE/C,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO;gBADP,UAAU,GAAE,oBAAyB,EACrC,OAAO,GAAE,qBAA0B;IAGtD;;;;;;;;;;;;;;;;;;;OAmBG;IACG,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CA0DxD"}
|
package/dist/guard.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
let _initClass;
|
|
2
|
+
function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 !== (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
|
|
3
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
4
|
+
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
5
|
+
function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
|
|
6
|
+
function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
|
|
7
|
+
import { ForbiddenException, UnauthorizedException } from '@fluojs/http';
|
|
8
|
+
import { Inject } from '@fluojs/core';
|
|
9
|
+
import { ContainerResolutionError } from '@fluojs/di';
|
|
10
|
+
import { AuthenticationExpiredError, AuthenticationFailedError, AuthenticationRequiredError, AuthStrategyResolutionError } from './errors.js';
|
|
11
|
+
import { AUTH_STRATEGY_REGISTRY, PASSPORT_OPTIONS } from './internal-tokens.js';
|
|
12
|
+
import { getAuthRequirement } from './metadata.js';
|
|
13
|
+
function isAuthHandledResult(result) {
|
|
14
|
+
return typeof result === 'object' && result !== null && 'handled' in result && result.handled === true;
|
|
15
|
+
}
|
|
16
|
+
function resolvePrincipal(result) {
|
|
17
|
+
if (isAuthHandledResult(result)) {
|
|
18
|
+
return result.principal;
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
function hasRequiredScopes(principal, scopes) {
|
|
23
|
+
return scopes.every(scope => principal.scopes?.includes(scope));
|
|
24
|
+
}
|
|
25
|
+
function isAuthenticationFailure(error) {
|
|
26
|
+
return error instanceof AuthenticationRequiredError || error instanceof AuthenticationExpiredError || error instanceof AuthenticationFailedError;
|
|
27
|
+
}
|
|
28
|
+
function hasRegisteredStrategy(registry, strategyName) {
|
|
29
|
+
return Object.hasOwn(registry, strategyName);
|
|
30
|
+
}
|
|
31
|
+
function toErrorMessage(error) {
|
|
32
|
+
return error instanceof Error ? error.message : String(error);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* HTTP guard that resolves the active auth strategy, authenticates the request,
|
|
37
|
+
* and writes the resulting principal back to `requestContext.principal`.
|
|
38
|
+
*
|
|
39
|
+
* @remarks
|
|
40
|
+
* `AuthGuard` preserves the public contract documented in `@fluojs/passport`:
|
|
41
|
+
* authentication failures become canonical `401 Unauthorized` responses, scope
|
|
42
|
+
* mismatches become `403 Forbidden`, and strategies may short-circuit the
|
|
43
|
+
* response by returning `{ handled: true }` after committing the response.
|
|
44
|
+
*/
|
|
45
|
+
let _AuthGuard;
|
|
46
|
+
class AuthGuard {
|
|
47
|
+
static {
|
|
48
|
+
[_AuthGuard, _initClass] = _applyDecs(this, [Inject(AUTH_STRATEGY_REGISTRY, PASSPORT_OPTIONS)], []).c;
|
|
49
|
+
}
|
|
50
|
+
constructor(strategies = {}, options = {}) {
|
|
51
|
+
this.strategies = strategies;
|
|
52
|
+
this.options = options;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Executes the configured auth strategy for the current route and enforces any declared scopes.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* @Controller('/profile')
|
|
61
|
+
* class ProfileController {
|
|
62
|
+
* @Get('/')
|
|
63
|
+
* @UseAuth('jwt')
|
|
64
|
+
* @RequireScopes('profile:read')
|
|
65
|
+
* getProfile() {}
|
|
66
|
+
* }
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* @param context HTTP guard context for the active handler invocation.
|
|
70
|
+
* @returns `true` when the request may continue through the HTTP pipeline.
|
|
71
|
+
* @throws {AuthStrategyResolutionError} When no active strategy can be determined or resolved.
|
|
72
|
+
* @throws {UnauthorizedException} When the strategy reports missing, expired, or invalid authentication.
|
|
73
|
+
* @throws {ForbiddenException} When the authenticated principal is missing required scopes.
|
|
74
|
+
*/
|
|
75
|
+
async canActivate(context) {
|
|
76
|
+
const requirement = getAuthRequirement(context.handler.controllerToken, context.handler.methodName);
|
|
77
|
+
const strategyName = requirement?.strategy ?? this.options.defaultStrategy;
|
|
78
|
+
if (!strategyName) {
|
|
79
|
+
if (requirement?.scopes?.length) {
|
|
80
|
+
throw new AuthStrategyResolutionError('Auth requirement exists without an active strategy.');
|
|
81
|
+
}
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
if (!hasRegisteredStrategy(this.strategies, strategyName)) {
|
|
85
|
+
throw new AuthStrategyResolutionError(`No auth strategy registered for ${strategyName}.`);
|
|
86
|
+
}
|
|
87
|
+
const strategyToken = this.strategies[strategyName];
|
|
88
|
+
const strategy = await context.requestContext.container.resolve(strategyToken).catch(error => {
|
|
89
|
+
if (error instanceof ContainerResolutionError) {
|
|
90
|
+
throw new AuthStrategyResolutionError(`Failed to resolve auth strategy "${strategyName}": ${toErrorMessage(error)}`);
|
|
91
|
+
}
|
|
92
|
+
throw error;
|
|
93
|
+
});
|
|
94
|
+
try {
|
|
95
|
+
const result = await strategy.authenticate(context);
|
|
96
|
+
const principal = resolvePrincipal(result);
|
|
97
|
+
if (isAuthHandledResult(result) && !principal) {
|
|
98
|
+
if (!context.requestContext.response.committed) {
|
|
99
|
+
throw new AuthenticationFailedError('Auth strategy returned handled:true without a principal but did not commit a response.');
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
if (!principal) {
|
|
104
|
+
throw new AuthenticationFailedError('Authentication strategy did not return a principal.');
|
|
105
|
+
}
|
|
106
|
+
if (requirement?.scopes?.length && !hasRequiredScopes(principal, requirement.scopes)) {
|
|
107
|
+
throw new ForbiddenException('Access denied.');
|
|
108
|
+
}
|
|
109
|
+
context.requestContext.principal = principal;
|
|
110
|
+
return true;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
if (isAuthenticationFailure(error)) {
|
|
113
|
+
throw new UnauthorizedException('Authentication required.', {
|
|
114
|
+
cause: error
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
static {
|
|
121
|
+
_initClass();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export { _AuthGuard as AuthGuard };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export * from './account/account-linking.js';
|
|
2
|
+
export * from './cookie/cookie-auth.js';
|
|
3
|
+
export * from './cookie/cookie-auth-module.js';
|
|
4
|
+
export * from './cookie/cookie-manager.js';
|
|
5
|
+
export * from './decorators.js';
|
|
6
|
+
export * from './errors.js';
|
|
7
|
+
export * from './guard.js';
|
|
8
|
+
export * from './refresh/jwt-refresh-token-adapter.js';
|
|
9
|
+
export * from './metadata.js';
|
|
10
|
+
export * from './module.js';
|
|
11
|
+
export * from './adapters/passport-js.js';
|
|
12
|
+
export * from './refresh/refresh-token.js';
|
|
13
|
+
export * from './status.js';
|
|
14
|
+
export * from './types.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,yBAAyB,CAAC;AACxC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,wCAAwC,CAAC;AACvD,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export * from './account/account-linking.js';
|
|
2
|
+
export * from './cookie/cookie-auth.js';
|
|
3
|
+
export * from './cookie/cookie-auth-module.js';
|
|
4
|
+
export * from './cookie/cookie-manager.js';
|
|
5
|
+
export * from './decorators.js';
|
|
6
|
+
export * from './errors.js';
|
|
7
|
+
export * from './guard.js';
|
|
8
|
+
export * from './refresh/jwt-refresh-token-adapter.js';
|
|
9
|
+
export * from './metadata.js';
|
|
10
|
+
export * from './module.js';
|
|
11
|
+
export * from './adapters/passport-js.js';
|
|
12
|
+
export * from './refresh/refresh-token.js';
|
|
13
|
+
export * from './status.js';
|
|
14
|
+
export * from './types.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal-tokens.d.ts","sourceRoot":"","sources":["../src/internal-tokens.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,gBAAgB,eAAmC,CAAC;AACjE,eAAO,MAAM,sBAAsB,eAAyC,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type MetadataPropertyKey } from '@fluojs/core';
|
|
2
|
+
import type { AuthRequirement } from './types.js';
|
|
3
|
+
export declare function defineAuthRequirement(target: Function | object, requirement: AuthRequirement, propertyKey?: MetadataPropertyKey): void;
|
|
4
|
+
export declare function getOwnAuthRequirement(target: Function | object, propertyKey?: MetadataPropertyKey): AuthRequirement | undefined;
|
|
5
|
+
export declare function getAuthRequirement(controllerType: Function, propertyKey?: MetadataPropertyKey): AuthRequirement | undefined;
|
|
6
|
+
//# sourceMappingURL=metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAIxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAwElD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAkCtI;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,EAAE,WAAW,CAAC,EAAE,mBAAmB,GAAG,eAAe,GAAG,SAAS,CAM/H;AAED,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,QAAQ,EAAE,WAAW,CAAC,EAAE,mBAAmB,GAAG,eAAe,GAAG,SAAS,CA6B3H"}
|
package/dist/metadata.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { metadataSymbol } from '@fluojs/core/internal';
|
|
2
|
+
import { mergeAuthRequirements, normalizeDeclaredScopes } from './scope.js';
|
|
3
|
+
const standardClassRequirementKey = Symbol.for('fluo.passport.standard.class-auth');
|
|
4
|
+
const standardMethodRequirementKey = Symbol.for('fluo.passport.standard.method-auth');
|
|
5
|
+
const classRequirementStore = new WeakMap();
|
|
6
|
+
const methodRequirementStore = new WeakMap();
|
|
7
|
+
const mergedClassRequirementCache = new WeakMap();
|
|
8
|
+
const mergedMethodRequirementCache = new WeakMap();
|
|
9
|
+
function normalizeRequirement(requirement) {
|
|
10
|
+
if (!requirement) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
const strategy = requirement.strategy;
|
|
14
|
+
const scopes = normalizeDeclaredScopes(requirement.scopes);
|
|
15
|
+
if (!strategy && !scopes) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
scopes,
|
|
20
|
+
strategy
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function toCacheValue(requirement) {
|
|
24
|
+
return requirement ?? null;
|
|
25
|
+
}
|
|
26
|
+
function invalidateRequirementCache(controllerType, propertyKey) {
|
|
27
|
+
mergedClassRequirementCache.delete(controllerType);
|
|
28
|
+
if (propertyKey === undefined) {
|
|
29
|
+
mergedMethodRequirementCache.delete(controllerType);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const methodCache = mergedMethodRequirementCache.get(controllerType);
|
|
33
|
+
if (!methodCache) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
methodCache.delete(propertyKey);
|
|
37
|
+
if (methodCache.size === 0) {
|
|
38
|
+
mergedMethodRequirementCache.delete(controllerType);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function getStandardMetadataBag(target) {
|
|
42
|
+
return target[metadataSymbol];
|
|
43
|
+
}
|
|
44
|
+
function getStandardClassRequirement(target) {
|
|
45
|
+
return normalizeRequirement(getStandardMetadataBag(target)?.[standardClassRequirementKey]);
|
|
46
|
+
}
|
|
47
|
+
function getStandardMethodRequirement(target, propertyKey) {
|
|
48
|
+
const constructor = target.constructor;
|
|
49
|
+
const map = constructor ? getStandardMetadataBag(constructor)?.[standardMethodRequirementKey] : undefined;
|
|
50
|
+
return normalizeRequirement(map?.get(propertyKey));
|
|
51
|
+
}
|
|
52
|
+
export function defineAuthRequirement(target, requirement, propertyKey) {
|
|
53
|
+
const normalizedRequirement = normalizeRequirement(requirement);
|
|
54
|
+
if (propertyKey === undefined) {
|
|
55
|
+
const controllerType = target;
|
|
56
|
+
if (normalizedRequirement) {
|
|
57
|
+
classRequirementStore.set(controllerType, normalizedRequirement);
|
|
58
|
+
} else {
|
|
59
|
+
classRequirementStore.delete(controllerType);
|
|
60
|
+
}
|
|
61
|
+
invalidateRequirementCache(controllerType);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
let map = methodRequirementStore.get(target);
|
|
65
|
+
if (!map) {
|
|
66
|
+
map = new Map();
|
|
67
|
+
methodRequirementStore.set(target, map);
|
|
68
|
+
}
|
|
69
|
+
if (normalizedRequirement) {
|
|
70
|
+
map.set(propertyKey, normalizedRequirement);
|
|
71
|
+
} else {
|
|
72
|
+
map.delete(propertyKey);
|
|
73
|
+
}
|
|
74
|
+
const controllerType = target.constructor;
|
|
75
|
+
if (controllerType) {
|
|
76
|
+
invalidateRequirementCache(controllerType, propertyKey);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export function getOwnAuthRequirement(target, propertyKey) {
|
|
80
|
+
if (propertyKey === undefined) {
|
|
81
|
+
return mergeAuthRequirements(classRequirementStore.get(target), getStandardClassRequirement(target));
|
|
82
|
+
}
|
|
83
|
+
return mergeAuthRequirements(methodRequirementStore.get(target)?.get(propertyKey), getStandardMethodRequirement(target, propertyKey));
|
|
84
|
+
}
|
|
85
|
+
export function getAuthRequirement(controllerType, propertyKey) {
|
|
86
|
+
if (propertyKey === undefined) {
|
|
87
|
+
if (mergedClassRequirementCache.has(controllerType)) {
|
|
88
|
+
return mergedClassRequirementCache.get(controllerType) ?? undefined;
|
|
89
|
+
}
|
|
90
|
+
const requirement = getOwnAuthRequirement(controllerType);
|
|
91
|
+
mergedClassRequirementCache.set(controllerType, toCacheValue(requirement));
|
|
92
|
+
return requirement;
|
|
93
|
+
}
|
|
94
|
+
const methodCache = mergedMethodRequirementCache.get(controllerType);
|
|
95
|
+
if (methodCache?.has(propertyKey)) {
|
|
96
|
+
return methodCache.get(propertyKey) ?? undefined;
|
|
97
|
+
}
|
|
98
|
+
const requirement = mergeAuthRequirements(getOwnAuthRequirement(controllerType), getOwnAuthRequirement(controllerType.prototype, propertyKey));
|
|
99
|
+
if (methodCache) {
|
|
100
|
+
methodCache.set(propertyKey, toCacheValue(requirement));
|
|
101
|
+
} else {
|
|
102
|
+
mergedMethodRequirementCache.set(controllerType, new Map([[propertyKey, toCacheValue(requirement)]]));
|
|
103
|
+
}
|
|
104
|
+
return requirement;
|
|
105
|
+
}
|