@fluojs/passport 1.0.4 → 2.0.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.ko.md +114 -45
- package/README.md +114 -45
- package/dist/adapters/passport-js.d.ts +20 -5
- package/dist/adapters/passport-js.d.ts.map +1 -1
- package/dist/adapters/passport-js.js +32 -9
- package/dist/bearer/bearer-jwt.d.ts +90 -0
- package/dist/bearer/bearer-jwt.d.ts.map +1 -0
- package/dist/bearer/bearer-jwt.js +160 -0
- package/dist/cookie/cookie-auth-module.d.ts +7 -1
- package/dist/cookie/cookie-auth-module.d.ts.map +1 -1
- package/dist/cookie/cookie-auth-module.js +6 -0
- package/dist/cookie/cookie-auth.d.ts +6 -0
- package/dist/cookie/cookie-auth.d.ts.map +1 -1
- package/dist/cookie/cookie-auth.js +21 -3
- package/dist/cookie/cookie-manager.d.ts +15 -4
- package/dist/cookie/cookie-manager.d.ts.map +1 -1
- package/dist/cookie/cookie-manager.js +43 -44
- package/dist/guard.d.ts.map +1 -1
- package/dist/guard.js +3 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/refresh/jwt-refresh-token-adapter.d.ts.map +1 -1
- package/dist/refresh/jwt-refresh-token-adapter.js +22 -1
- package/dist/refresh/refresh-token.d.ts +53 -5
- package/dist/refresh/refresh-token.d.ts.map +1 -1
- package/dist/refresh/refresh-token.js +33 -3
- package/package.json +8 -8
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { setCookie } from '@fluojs/http';
|
|
1
2
|
import { normalizeCookieAuthOptions } from './cookie-auth.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -5,7 +6,13 @@ import { normalizeCookieAuthOptions } from './cookie-auth.js';
|
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
|
-
* Describes the set cookie options contract.
|
|
9
|
+
* Describes the set cookie options contract accepted by {@link CookieManagerConfig}.
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* `accessTokenTtlSeconds` and `refreshTokenTtlSeconds` become the default `Max-Age`
|
|
13
|
+
* for the matching token cookie when the positional TTL argument of
|
|
14
|
+
* `CookieManager.setAccessTokenCookie(...)` / `setRefreshTokenCookie(...)` is omitted.
|
|
15
|
+
* An explicit positional TTL always wins over these defaults.
|
|
9
16
|
*/
|
|
10
17
|
|
|
11
18
|
/**
|
|
@@ -23,34 +30,6 @@ export const DEFAULT_COOKIE_OPTIONS = {
|
|
|
23
30
|
domain: undefined,
|
|
24
31
|
maxAge: undefined
|
|
25
32
|
};
|
|
26
|
-
function buildCookieHeader(name, value, options) {
|
|
27
|
-
const parts = [`${name}=${value}`];
|
|
28
|
-
if (options.maxAge !== undefined && options.maxAge >= 0) {
|
|
29
|
-
parts.push(`Max-Age=${options.maxAge}`);
|
|
30
|
-
}
|
|
31
|
-
if (options.path) {
|
|
32
|
-
parts.push(`Path=${options.path}`);
|
|
33
|
-
}
|
|
34
|
-
if (options.domain) {
|
|
35
|
-
parts.push(`Domain=${options.domain}`);
|
|
36
|
-
}
|
|
37
|
-
if (options.secure) {
|
|
38
|
-
parts.push('Secure');
|
|
39
|
-
}
|
|
40
|
-
if (options.httpOnly) {
|
|
41
|
-
parts.push('HttpOnly');
|
|
42
|
-
}
|
|
43
|
-
if (options.sameSite) {
|
|
44
|
-
parts.push(`SameSite=${options.sameSite.charAt(0).toUpperCase() + options.sameSite.slice(1)}`);
|
|
45
|
-
}
|
|
46
|
-
return parts.join('; ');
|
|
47
|
-
}
|
|
48
|
-
function buildClearCookieHeader(name, options) {
|
|
49
|
-
return buildCookieHeader(name, '', {
|
|
50
|
-
...options,
|
|
51
|
-
maxAge: 0
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
33
|
function getHeaderCaseInsensitive(headers, name) {
|
|
55
34
|
for (const [headerName, value] of Object.entries(headers)) {
|
|
56
35
|
if (headerName.toLowerCase() === name.toLowerCase() && (typeof value === 'string' || Array.isArray(value))) {
|
|
@@ -75,6 +54,8 @@ function toHeaderValues(value) {
|
|
|
75
54
|
export class CookieManager {
|
|
76
55
|
options;
|
|
77
56
|
cookieOptions;
|
|
57
|
+
accessTokenTtlSeconds;
|
|
58
|
+
refreshTokenTtlSeconds;
|
|
78
59
|
constructor(config) {
|
|
79
60
|
this.options = normalizeCookieAuthOptions(config);
|
|
80
61
|
this.cookieOptions = {
|
|
@@ -85,28 +66,36 @@ export class CookieManager {
|
|
|
85
66
|
domain: config?.cookieOptions?.domain ?? DEFAULT_COOKIE_OPTIONS.domain,
|
|
86
67
|
maxAge: config?.cookieOptions?.maxAge ?? DEFAULT_COOKIE_OPTIONS.maxAge
|
|
87
68
|
};
|
|
69
|
+
this.accessTokenTtlSeconds = config?.cookieOptions?.accessTokenTtlSeconds;
|
|
70
|
+
this.refreshTokenTtlSeconds = config?.cookieOptions?.refreshTokenTtlSeconds;
|
|
88
71
|
}
|
|
89
72
|
setAccessTokenCookie(response, token, ttlSeconds) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
73
|
+
this.writeCookie(response, {
|
|
74
|
+
maxAgeSeconds: ttlSeconds ?? this.accessTokenTtlSeconds ?? this.cookieOptions.maxAge,
|
|
75
|
+
name: this.options.accessTokenCookieName,
|
|
76
|
+
value: token
|
|
93
77
|
});
|
|
94
|
-
this.appendSetCookie(response, cookie);
|
|
95
78
|
}
|
|
96
79
|
setRefreshTokenCookie(response, token, ttlSeconds) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
80
|
+
this.writeCookie(response, {
|
|
81
|
+
maxAgeSeconds: ttlSeconds ?? this.refreshTokenTtlSeconds ?? this.cookieOptions.maxAge,
|
|
82
|
+
name: this.options.refreshTokenCookieName,
|
|
83
|
+
value: token
|
|
100
84
|
});
|
|
101
|
-
this.appendSetCookie(response, cookie);
|
|
102
85
|
}
|
|
103
86
|
clearAccessTokenCookie(response) {
|
|
104
|
-
|
|
105
|
-
|
|
87
|
+
this.writeCookie(response, {
|
|
88
|
+
maxAgeSeconds: 0,
|
|
89
|
+
name: this.options.accessTokenCookieName,
|
|
90
|
+
value: ''
|
|
91
|
+
});
|
|
106
92
|
}
|
|
107
93
|
clearRefreshTokenCookie(response) {
|
|
108
|
-
|
|
109
|
-
|
|
94
|
+
this.writeCookie(response, {
|
|
95
|
+
maxAgeSeconds: 0,
|
|
96
|
+
name: this.options.refreshTokenCookieName,
|
|
97
|
+
value: ''
|
|
98
|
+
});
|
|
110
99
|
}
|
|
111
100
|
clearAllCookies(response) {
|
|
112
101
|
this.clearAccessTokenCookie(response);
|
|
@@ -118,11 +107,21 @@ export class CookieManager {
|
|
|
118
107
|
this.setRefreshTokenCookie(response, refreshToken, refreshTokenTtlSeconds);
|
|
119
108
|
}
|
|
120
109
|
}
|
|
121
|
-
|
|
110
|
+
writeCookie(response, cookie) {
|
|
122
111
|
const existingHeader = getHeaderCaseInsensitive(response.headers, 'Set-Cookie');
|
|
123
|
-
const
|
|
124
|
-
response.
|
|
112
|
+
const existingValues = toHeaderValues(existingHeader?.value);
|
|
113
|
+
setCookie(response, cookie.name, cookie.value, {
|
|
114
|
+
domain: this.cookieOptions.domain,
|
|
115
|
+
httpOnly: this.cookieOptions.httpOnly,
|
|
116
|
+
maxAgeSeconds: cookie.maxAgeSeconds,
|
|
117
|
+
path: this.cookieOptions.path,
|
|
118
|
+
sameSite: this.cookieOptions.sameSite,
|
|
119
|
+
secure: this.cookieOptions.secure
|
|
120
|
+
});
|
|
125
121
|
const updatedHeader = getHeaderCaseInsensitive(response.headers, 'Set-Cookie');
|
|
122
|
+
const writtenValues = toHeaderValues(response.headers['Set-Cookie'] ?? updatedHeader?.value);
|
|
123
|
+
const appendedExistingValues = writtenValues.length > existingValues.length && existingValues.every((value, index) => writtenValues[index] === value);
|
|
124
|
+
const cookies = appendedExistingValues ? writtenValues : [...existingValues, ...writtenValues];
|
|
126
125
|
if (updatedHeader?.key && updatedHeader.key !== 'Set-Cookie') {
|
|
127
126
|
delete response.headers[updatedHeader.key];
|
|
128
127
|
}
|
package/dist/guard.d.ts.map
CHANGED
|
@@ -1 +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,EAKjB,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAsFpB;;;;;;;;;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;
|
|
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,EAKjB,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAsFpB;;;;;;;;;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;CAmExD"}
|
package/dist/guard.js
CHANGED
|
@@ -129,13 +129,13 @@ class AuthGuard {
|
|
|
129
129
|
});
|
|
130
130
|
try {
|
|
131
131
|
const result = await strategy.authenticate(context);
|
|
132
|
-
|
|
133
|
-
if (isAuthHandledResult(result) && !principal) {
|
|
132
|
+
if (isAuthHandledResult(result)) {
|
|
134
133
|
if (!context.requestContext.response.committed) {
|
|
135
|
-
throw new AuthenticationFailedError('Auth strategy returned handled:true
|
|
134
|
+
throw new AuthenticationFailedError('Auth strategy returned handled:true but did not commit a response.');
|
|
136
135
|
}
|
|
137
136
|
return true;
|
|
138
137
|
}
|
|
138
|
+
const principal = resolvePrincipal(result);
|
|
139
139
|
if (!principal) {
|
|
140
140
|
if (isAuthOptionalResult(result) && requirement?.optional && !requirement.scopes?.length) {
|
|
141
141
|
return true;
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,wBAAwB,CAAC;AACvC,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
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwt-refresh-token-adapter.d.ts","sourceRoot":"","sources":["../../src/refresh/jwt-refresh-token-adapter.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAChB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"jwt-refresh-token-adapter.d.ts","sourceRoot":"","sources":["../../src/refresh/jwt-refresh-token-adapter.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAIlB,KAAK,iBAAiB,EACvB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D;;GAEG;AACH,eAAO,MAAM,4BAA4B,eAA2D,CAAC;AAErG;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,iBAAiB,GAAG,QAAQ,CAAC;CACrC;AAoFD;;GAEG;AACH,qBACa,sBAAuB,YAAW,mBAAmB;IAChE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;gBAG/C,MAAM,EAAE,gBAAgB,EACxB,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE,yBAAyB;IAuB9B,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAInD,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAIhG,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG1D"}
|
|
@@ -23,25 +23,46 @@ function resolveSecret(options) {
|
|
|
23
23
|
}
|
|
24
24
|
function createInMemoryStore() {
|
|
25
25
|
const records = new Map();
|
|
26
|
+
const pruneExpired = now => {
|
|
27
|
+
for (const [id, record] of records.entries()) {
|
|
28
|
+
if (record.expiresAt.getTime() <= now) {
|
|
29
|
+
records.delete(id);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
26
33
|
return {
|
|
27
34
|
async save(token) {
|
|
28
35
|
records.set(token.id, token);
|
|
36
|
+
pruneExpired(Date.now());
|
|
29
37
|
},
|
|
30
38
|
async find(tokenId) {
|
|
31
|
-
|
|
39
|
+
const record = records.get(tokenId);
|
|
40
|
+
pruneExpired(Date.now());
|
|
41
|
+
return record;
|
|
32
42
|
},
|
|
33
43
|
async revoke(tokenId) {
|
|
44
|
+
pruneExpired(Date.now());
|
|
34
45
|
records.delete(tokenId);
|
|
35
46
|
},
|
|
36
47
|
async revokeBySubject(subject) {
|
|
48
|
+
pruneExpired(Date.now());
|
|
37
49
|
for (const [id, record] of records.entries()) {
|
|
38
50
|
if (record.subject === subject) {
|
|
39
51
|
records.delete(id);
|
|
40
52
|
}
|
|
41
53
|
}
|
|
42
54
|
},
|
|
55
|
+
async revokeByFamily(family) {
|
|
56
|
+
pruneExpired(Date.now());
|
|
57
|
+
for (const [id, record] of records.entries()) {
|
|
58
|
+
if (record.family === family) {
|
|
59
|
+
records.delete(id);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
43
63
|
async consume(input) {
|
|
44
64
|
const record = records.get(input.tokenId);
|
|
65
|
+
pruneExpired(input.now.getTime());
|
|
45
66
|
if (!record) {
|
|
46
67
|
return 'invalid';
|
|
47
68
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { GuardContext } from '@fluojs/http';
|
|
2
1
|
import { type Token } from '@fluojs/core';
|
|
2
|
+
import type { GuardContext } from '@fluojs/http';
|
|
3
3
|
import { DefaultJwtVerifier } from '@fluojs/jwt';
|
|
4
4
|
import { type ModuleType } from '@fluojs/runtime';
|
|
5
|
-
import type { AuthStrategy, AuthStrategyRegistration
|
|
5
|
+
import type { AuthStrategy, AuthStrategyRegistration } from '../types.js';
|
|
6
6
|
/**
|
|
7
7
|
* Defines the operations required to issue, rotate, and revoke refresh tokens.
|
|
8
8
|
*/
|
|
@@ -27,17 +27,53 @@ export interface RefreshTokenInput {
|
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
29
|
* Captures the token pair returned after a successful refresh-token exchange.
|
|
30
|
+
*
|
|
31
|
+
* @remarks
|
|
32
|
+
* This is the application-facing exchange payload shape refresh endpoints return to
|
|
33
|
+
* clients. It is not the shape `RefreshTokenStrategy` places on `ctx.principal`;
|
|
34
|
+
* that principal result is typed by {@link RefreshTokenPrincipal}.
|
|
30
35
|
*/
|
|
31
36
|
export interface RefreshTokenAuthResult {
|
|
32
37
|
accessToken: string;
|
|
33
38
|
refreshToken: string;
|
|
34
39
|
subject: string;
|
|
35
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* The principal `RefreshTokenStrategy` resolves onto `ctx.principal` after a
|
|
43
|
+
* successful exchange, with the rotated token pair nested under `claims`.
|
|
44
|
+
*/
|
|
45
|
+
export interface RefreshTokenPrincipal {
|
|
46
|
+
/**
|
|
47
|
+
* Principal claims carrying the rotated pair under `accessToken` and `refreshToken`.
|
|
48
|
+
*/
|
|
49
|
+
claims: Record<string, unknown> & {
|
|
50
|
+
accessToken: string;
|
|
51
|
+
refreshToken: string;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Verified subject the rotated access token was issued for.
|
|
55
|
+
*/
|
|
56
|
+
subject: string;
|
|
57
|
+
}
|
|
36
58
|
/**
|
|
37
59
|
* Identifies the built-in refresh-token authentication strategy.
|
|
38
60
|
*/
|
|
39
61
|
export declare const REFRESH_TOKEN_STRATEGY_NAME = "refresh-token";
|
|
40
62
|
type RefreshTokenModuleType = ModuleType;
|
|
63
|
+
/**
|
|
64
|
+
* Configures application modules that supply refresh-token service dependencies.
|
|
65
|
+
*/
|
|
66
|
+
export interface RefreshTokenModuleImportOptions {
|
|
67
|
+
/**
|
|
68
|
+
* Modules that export dependencies injected into the refresh-token service.
|
|
69
|
+
*
|
|
70
|
+
* @remarks
|
|
71
|
+
* `RefreshTokenModule` keeps ownership of a class service token. Import modules
|
|
72
|
+
* that own and export its constructor dependencies so they are visible without
|
|
73
|
+
* duplicating the service provider in the importing application module.
|
|
74
|
+
*/
|
|
75
|
+
imports?: ModuleType[];
|
|
76
|
+
}
|
|
41
77
|
/**
|
|
42
78
|
* Authenticates refresh-token requests and exchanges them for a fresh token pair.
|
|
43
79
|
*/
|
|
@@ -45,7 +81,7 @@ export declare class RefreshTokenStrategy implements AuthStrategy {
|
|
|
45
81
|
private readonly refreshTokenService;
|
|
46
82
|
private readonly verifier;
|
|
47
83
|
constructor(refreshTokenService: RefreshTokenService, verifier: DefaultJwtVerifier);
|
|
48
|
-
authenticate(context: GuardContext): Promise<
|
|
84
|
+
authenticate(context: GuardContext): Promise<RefreshTokenPrincipal>;
|
|
49
85
|
private rotateRefreshToken;
|
|
50
86
|
private extractRefreshToken;
|
|
51
87
|
private normalizeRefreshToken;
|
|
@@ -65,7 +101,20 @@ export declare class RefreshTokenModule {
|
|
|
65
101
|
* Registers the shared refresh-token service alias together with `RefreshTokenStrategy`.
|
|
66
102
|
*
|
|
67
103
|
* @param service DI token for the concrete refresh-token service implementation.
|
|
104
|
+
* Class tokens are registered inside this module.
|
|
105
|
+
* String and symbol tokens must be visible to this module through an imported
|
|
106
|
+
* module export, a global module export, or bootstrap runtime providers.
|
|
107
|
+
* @param options Optional module imports that export class-service constructor dependencies.
|
|
68
108
|
* @returns A module definition that exports `RefreshTokenStrategy` and `REFRESH_TOKEN_SERVICE`.
|
|
109
|
+
* @remarks
|
|
110
|
+
* To register a class service with constructor dependencies, place those
|
|
111
|
+
* dependencies in an application-owned module, export them, and pass that module
|
|
112
|
+
* through `options.imports`. This preserves strict
|
|
113
|
+
* `duplicateProviderPolicy: 'throw'` behavior because the service class remains
|
|
114
|
+
* registered exactly once by `RefreshTokenModule`. Do not re-register the service
|
|
115
|
+
* class in the importing application module. String and symbol service tokens
|
|
116
|
+
* can be exported by a module in `options.imports`; globally exported and
|
|
117
|
+
* bootstrap runtime provider tokens are also visible.
|
|
69
118
|
*
|
|
70
119
|
* @example
|
|
71
120
|
* ```ts
|
|
@@ -85,12 +134,11 @@ export declare class RefreshTokenModule {
|
|
|
85
134
|
* [{ name: REFRESH_TOKEN_STRATEGY_NAME, token: RefreshTokenStrategy }],
|
|
86
135
|
* ),
|
|
87
136
|
* ],
|
|
88
|
-
* providers: [MyRefreshTokenService],
|
|
89
137
|
* })
|
|
90
138
|
* export class AuthModule {}
|
|
91
139
|
* ```
|
|
92
140
|
*/
|
|
93
|
-
static forRoot(service: Token<RefreshTokenService
|
|
141
|
+
static forRoot(service: Token<RefreshTokenService>, options?: RefreshTokenModuleImportOptions): RefreshTokenModuleType;
|
|
94
142
|
}
|
|
95
143
|
export {};
|
|
96
144
|
//# sourceMappingURL=refresh-token.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refresh-token.d.ts","sourceRoot":"","sources":["../../src/refresh/refresh-token.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"refresh-token.d.ts","sourceRoot":"","sources":["../../src/refresh/refresh-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,KAAK,EAAE,MAAM,cAAc,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAA8C,MAAM,aAAa,CAAC;AAC7F,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAOhE,OAAO,KAAK,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAE1E;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjG,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB,eAAoD,CAAC;AAEvF;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAChF;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,eAAO,MAAM,2BAA2B,kBAAkB,CAAC;AAI3D,KAAK,sBAAsB,GAAG,UAAU,CAAC;AAEzC;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC9C;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;CACxB;AAED;;GAEG;AACH,qBACa,oBAAqB,YAAW,YAAY;IAErD,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBADR,mBAAmB,EAAE,mBAAmB,EACxC,QAAQ,EAAE,kBAAkB;IAGzC,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,qBAAqB,CAAC;YAwB3D,kBAAkB;IAmBhC,OAAO,CAAC,mBAAmB;IAe3B,OAAO,CAAC,qBAAqB;YAYf,sBAAsB;CAQrC;AAoBD;;;;GAIG;AACH,wBAAgB,sCAAsC,IAAI,wBAAwB,CAKjF;AAED;;GAEG;AACH,qBAAa,kBAAkB;IAC7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAwCG;IACH,MAAM,CAAC,OAAO,CACZ,OAAO,EAAE,KAAK,CAAC,mBAAmB,CAAC,EACnC,OAAO,GAAE,+BAAoC,GAC5C,sBAAsB;CAY1B"}
|
|
@@ -24,6 +24,16 @@ export const REFRESH_TOKEN_SERVICE = Symbol.for('fluo.passport.refresh-token-ser
|
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Captures the token pair returned after a successful refresh-token exchange.
|
|
27
|
+
*
|
|
28
|
+
* @remarks
|
|
29
|
+
* This is the application-facing exchange payload shape refresh endpoints return to
|
|
30
|
+
* clients. It is not the shape `RefreshTokenStrategy` places on `ctx.principal`;
|
|
31
|
+
* that principal result is typed by {@link RefreshTokenPrincipal}.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The principal `RefreshTokenStrategy` resolves onto `ctx.principal` after a
|
|
36
|
+
* successful exchange, with the rotated token pair nested under `claims`.
|
|
27
37
|
*/
|
|
28
38
|
|
|
29
39
|
/**
|
|
@@ -31,6 +41,10 @@ export const REFRESH_TOKEN_SERVICE = Symbol.for('fluo.passport.refresh-token-ser
|
|
|
31
41
|
*/
|
|
32
42
|
export const REFRESH_TOKEN_STRATEGY_NAME = 'refresh-token';
|
|
33
43
|
const MALFORMED_REFRESH_TOKEN = Symbol('MALFORMED_REFRESH_TOKEN');
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Configures application modules that supply refresh-token service dependencies.
|
|
47
|
+
*/
|
|
34
48
|
let _RefreshTokenStrategy;
|
|
35
49
|
/**
|
|
36
50
|
* Authenticates refresh-token requests and exchanges them for a fresh token pair.
|
|
@@ -115,8 +129,11 @@ class RefreshTokenStrategy {
|
|
|
115
129
|
}
|
|
116
130
|
}
|
|
117
131
|
export { _RefreshTokenStrategy as RefreshTokenStrategy };
|
|
132
|
+
function isClassToken(token) {
|
|
133
|
+
return typeof token === 'function';
|
|
134
|
+
}
|
|
118
135
|
function createRefreshTokenAliasProviders(service) {
|
|
119
|
-
return [{
|
|
136
|
+
return [...(isClassToken(service) ? [service] : []), {
|
|
120
137
|
provide: REFRESH_TOKEN_SERVICE,
|
|
121
138
|
useExisting: service
|
|
122
139
|
}];
|
|
@@ -142,7 +159,20 @@ export class RefreshTokenModule {
|
|
|
142
159
|
* Registers the shared refresh-token service alias together with `RefreshTokenStrategy`.
|
|
143
160
|
*
|
|
144
161
|
* @param service DI token for the concrete refresh-token service implementation.
|
|
162
|
+
* Class tokens are registered inside this module.
|
|
163
|
+
* String and symbol tokens must be visible to this module through an imported
|
|
164
|
+
* module export, a global module export, or bootstrap runtime providers.
|
|
165
|
+
* @param options Optional module imports that export class-service constructor dependencies.
|
|
145
166
|
* @returns A module definition that exports `RefreshTokenStrategy` and `REFRESH_TOKEN_SERVICE`.
|
|
167
|
+
* @remarks
|
|
168
|
+
* To register a class service with constructor dependencies, place those
|
|
169
|
+
* dependencies in an application-owned module, export them, and pass that module
|
|
170
|
+
* through `options.imports`. This preserves strict
|
|
171
|
+
* `duplicateProviderPolicy: 'throw'` behavior because the service class remains
|
|
172
|
+
* registered exactly once by `RefreshTokenModule`. Do not re-register the service
|
|
173
|
+
* class in the importing application module. String and symbol service tokens
|
|
174
|
+
* can be exported by a module in `options.imports`; globally exported and
|
|
175
|
+
* bootstrap runtime provider tokens are also visible.
|
|
146
176
|
*
|
|
147
177
|
* @example
|
|
148
178
|
* ```ts
|
|
@@ -162,15 +192,15 @@ export class RefreshTokenModule {
|
|
|
162
192
|
* [{ name: REFRESH_TOKEN_STRATEGY_NAME, token: RefreshTokenStrategy }],
|
|
163
193
|
* ),
|
|
164
194
|
* ],
|
|
165
|
-
* providers: [MyRefreshTokenService],
|
|
166
195
|
* })
|
|
167
196
|
* export class AuthModule {}
|
|
168
197
|
* ```
|
|
169
198
|
*/
|
|
170
|
-
static forRoot(service) {
|
|
199
|
+
static forRoot(service, options = {}) {
|
|
171
200
|
class RefreshTokenRuntimeModule extends RefreshTokenModule {}
|
|
172
201
|
return defineModule(RefreshTokenRuntimeModule, {
|
|
173
202
|
exports: [_RefreshTokenStrategy, REFRESH_TOKEN_SERVICE],
|
|
203
|
+
imports: options.imports ? [...options.imports] : undefined,
|
|
174
204
|
providers: [_RefreshTokenStrategy, ...createRefreshTokenAliasProviders(service)]
|
|
175
205
|
});
|
|
176
206
|
}
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"guard",
|
|
10
10
|
"strategy"
|
|
11
11
|
],
|
|
12
|
-
"version": "
|
|
12
|
+
"version": "2.0.0",
|
|
13
13
|
"private": false,
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"repository": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"directory": "packages/passport"
|
|
19
19
|
},
|
|
20
20
|
"engines": {
|
|
21
|
-
"node": ">=
|
|
21
|
+
"node": ">=24.0.0 <27"
|
|
22
22
|
},
|
|
23
23
|
"publishConfig": {
|
|
24
24
|
"access": "public"
|
|
@@ -36,14 +36,14 @@
|
|
|
36
36
|
"dist"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@fluojs/
|
|
40
|
-
"@fluojs/
|
|
41
|
-
"@fluojs/
|
|
42
|
-
"@fluojs/
|
|
43
|
-
"@fluojs/
|
|
39
|
+
"@fluojs/core": "^2.0.0",
|
|
40
|
+
"@fluojs/di": "^3.0.0",
|
|
41
|
+
"@fluojs/http": "^3.0.0",
|
|
42
|
+
"@fluojs/jwt": "^2.0.0",
|
|
43
|
+
"@fluojs/runtime": "^3.0.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"vitest": "^
|
|
46
|
+
"vitest": "^4.1.11"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"prebuild": "node ../../tooling/scripts/clean-dist.mjs",
|