@fluojs/passport 1.0.5 → 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 CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  <p><a href="./README.md"><kbd>English</kbd></a> <strong><kbd>한국어</kbd></strong></p>
4
4
 
5
+ Node.js 지원 범위는 `>=24.0.0 <27`입니다. 업그레이드 절차는 [Node.js 지원 및 마이그레이션](../../docs/reference/node-support.ko.md)을 참조하세요.
6
+
5
7
  fluo를 위한 인증 실행 계층으로, 어떤 `AuthStrategy`든 공통 `AuthGuard`를 통해 요청 컨텍스트(`requestContext.principal`)에 연결합니다.
6
8
 
7
9
  ## 목차
@@ -31,29 +33,17 @@ npm install @fluojs/passport
31
33
 
32
34
  ### 1. 모듈 등록
33
35
 
34
- 사용할 전략을 정의하고 `PassportModule.forRoot(...)`를 통해 등록합니다.
36
+ 내장 bearer JWT preset을 사용하고 `PassportModule.forRoot(...)`로 등록합니다.
35
37
 
36
38
  ```typescript
37
- import { Inject, Module } from '@fluojs/core';
38
- import type { GuardContext } from '@fluojs/http';
39
- import { DefaultJwtVerifier, JwtModule } from '@fluojs/jwt';
40
- import { AuthenticationRequiredError, PassportModule, type AuthStrategy } from '@fluojs/passport';
41
-
42
- @Inject(DefaultJwtVerifier)
43
- export class BearerJwtStrategy implements AuthStrategy {
44
- constructor(private readonly verifier: DefaultJwtVerifier) {}
45
-
46
- async authenticate(context: GuardContext) {
47
- const authorization = context.requestContext.request.headers.authorization;
48
- const [scheme, token] = typeof authorization === 'string' ? authorization.split(' ') : [];
49
-
50
- if (scheme !== 'Bearer' || !token) {
51
- throw new AuthenticationRequiredError('Bearer access token is required.');
52
- }
53
-
54
- return this.verifier.verifyAccessToken(token);
55
- }
56
- }
39
+ import { Module } from '@fluojs/core';
40
+ import { JwtModule } from '@fluojs/jwt';
41
+ import {
42
+ BEARER_JWT_STRATEGY_NAME,
43
+ BearerJwtStrategy,
44
+ createBearerJwtStrategyRegistration,
45
+ PassportModule,
46
+ } from '@fluojs/passport';
57
47
 
58
48
  @Module({
59
49
  imports: [
@@ -64,8 +54,8 @@ export class BearerJwtStrategy implements AuthStrategy {
64
54
  secret: 'your-secure-secret',
65
55
  }),
66
56
  PassportModule.forRoot(
67
- { defaultStrategy: 'jwt' },
68
- [{ name: 'jwt', token: BearerJwtStrategy }],
57
+ { defaultStrategy: BEARER_JWT_STRATEGY_NAME },
58
+ [createBearerJwtStrategyRegistration()],
69
59
  ),
70
60
  ],
71
61
  providers: [BearerJwtStrategy],
@@ -73,11 +63,11 @@ export class BearerJwtStrategy implements AuthStrategy {
73
63
  export class AuthModule {}
74
64
  ```
75
65
 
76
- JWT 기반 passport 전략에는 두 가지 module wiring이 모두 필요합니다. `JwtModule.forRoot(...)`는 `DefaultJwtVerifier`를 등록하고, `PassportModule.forRoot(...)`는 `@UseAuth('jwt')`가 resolve할 named strategy를 등록합니다. `DefaultJwtVerifier.verifyAccessToken(...)` 결과를 반환하면 `AuthGuard`가 `requestContext.principal`에 기록하는 정규화 principal 계약(`subject`, `claims`, `issuer`, `audience`, `roles`, `scopes`)이 보존됩니다.
66
+ `BearerJwtStrategy`는 `Authorization: Bearer <token>` credential을 위한 first-party strategy preset입니다. JWT 기반 passport 전략에는 두 가지 module wiring이 모두 필요합니다. `JwtModule.forRoot(...)`는 `DefaultJwtVerifier`를 등록하고, `providers` 항목은 `BearerJwtStrategy`를 DI를 통해 해당 verifier에 연결하며, `PassportModule.forRoot(...)`는 `@UseAuth('jwt')`가 resolve할 named strategy를 등록합니다. `DefaultJwtVerifier.verifyAccessToken(...)` 결과를 변경 없이 반환하면 `AuthGuard`가 `requestContext.principal`에 기록하는 정규화 principal 계약(`subject`, `claims`, `issuer`, `audience`, `roles`, `scopes`)이 보존됩니다.
77
67
 
78
68
  ### 2. 라우트 보호
79
69
 
80
- `@UseAuth()`와 `@RequireScopes()`를 사용하여 인증을 강제합니다.
70
+ `@UseAuth()`로 인증을, `@RequireScopes()`로 인가를 강제합니다.
81
71
 
82
72
  ```typescript
83
73
  import { Controller, Get, type RequestContext } from '@fluojs/http';
@@ -96,6 +86,46 @@ export class ProfileController {
96
86
 
97
87
  ## 일반적인 패턴
98
88
 
89
+ ### Bearer JWT 프리셋
90
+
91
+ 애플리케이션이 `Authorization: Bearer <token>` 요청을 인증한다면 내장 `BearerJwtStrategy`를 사용하세요. 애플리케이션이 직접 parsing 코드를 소유할 필요가 없습니다.
92
+
93
+ ```typescript
94
+ import { Module } from '@fluojs/core';
95
+ import { JwtModule } from '@fluojs/jwt';
96
+ import {
97
+ BEARER_JWT_STRATEGY_NAME,
98
+ BearerJwtStrategy,
99
+ createBearerJwtStrategyRegistration,
100
+ PassportModule,
101
+ } from '@fluojs/passport';
102
+
103
+ @Module({
104
+ imports: [
105
+ JwtModule.forRoot({
106
+ algorithms: ['HS256'],
107
+ audience: 'my-app',
108
+ issuer: 'my-api',
109
+ secret: 'your-secure-secret',
110
+ }),
111
+ PassportModule.forRoot(
112
+ { defaultStrategy: BEARER_JWT_STRATEGY_NAME },
113
+ [createBearerJwtStrategyRegistration()],
114
+ ),
115
+ ],
116
+ providers: [BearerJwtStrategy],
117
+ })
118
+ export class AuthModule {}
119
+ ```
120
+
121
+ `BearerJwtStrategy`는 `PassportModule`과 `JwtModule`을 import하는 같은 애플리케이션 모듈에 provider로 등록하세요. 검증 설정의 소유자는 계속 `JwtModule`이며, preset은 결합된 JWT/Passport module facade를 도입하지 않습니다. Preset은 안정적인 전략 이름 `BEARER_JWT_STRATEGY_NAME`(`'jwt'`)으로 등록되므로 보호된 라우트는 계속 `@UseAuth('jwt')`를 사용하고, `createBearerJwtStrategyRegistration()`이 대응하는 `AuthStrategyRegistration`을 반환합니다.
122
+
123
+ Credential extraction은 엄격합니다. Credential은 RFC 6750 `b64token`을 따라 ASCII 문자, 숫자, `-`, `.`, `_`, `~`, `+`, `/`가 하나 이상 있고 뒤에만 optional `=` padding이 올 수 있습니다. `?`, 내부 `=`, Unicode, whitespace, control character는 검증 전에 거부합니다. `Authorization` header가 없거나 비어 있으면 `AuthenticationRequiredError`가 발생하고, adapter가 array-valued `Authorization` header를 전달하면 첫 번째 entry만 읽습니다. 잘못된 scheme이거나 malformed인 header는 `AuthenticationFailedError`를 발생시키며, scheme 비교는 대소문자를 구분하지 않고 ASCII space를 하나 이상 사용합니다. 누락되거나 malformed인 credential은 bare `WWW-Authenticate: Bearer` challenge를 직렬화합니다. 검증 실패는 `DefaultJwtVerifier`의 error causality를 유지합니다. 만료된 credential은 `AuthenticationExpiredError`, `JwtInvalidTokenError` credential은 `AuthenticationFailedError`를 발생시키며, 둘 다 원본 JWT error를 `cause`로 보존하고 `WWW-Authenticate: Bearer error="invalid_token"`을 직렬화합니다. `JwtConfigurationError`와 verifier-provider infrastructure failure는 credential failure로 바꾸지 않고 변경 없이 전파합니다. `AuthGuard`는 credential failure만 canonical `401 Unauthorized` response로 변환하고, scope 불일치는 여전히 `403 Forbidden`을 만듭니다.
124
+
125
+ `BearerJwtStrategy`는 `DefaultJwtVerifier.verifyAccessToken(...)`이 반환한 정규화 `JwtPrincipal`을 변경 없이 반환하여 `subject`, `claims`, `issuer`, `audience`, `roles`, `scopes`를 보존합니다.
126
+
127
+ 커스텀 `AuthStrategy`는 token revocation, 계정 상태 확인, 대체 extraction, 애플리케이션 소유 정책을 위한 extension point로 남아 있습니다.
128
+
99
129
  ### Passport.js 브릿지 (Bridge)
100
130
 
101
131
  표준 Passport.js 전략(예: `passport-google-oauth20`)을 fluo의 DI와 비동기 수명 주기에 맞춰 쉽게 변환하여 사용할 수 있습니다.
@@ -120,7 +150,13 @@ export class AuthModule {}
120
150
 
121
151
  이 bridge helper는 third-party Passport.js strategy instance를 `AuthGuard`가 실행하기 전에 provider로 바인딩해야 하므로 Passport.js adapter에 대해 공식적으로 허용되는 module-facade 예외입니다. 애플리케이션-facing 인증 표면은 계속 `PassportModule`, `@UseAuth(...)`, `AuthGuard`이며, 이 helper가 이를 대체하지 않습니다.
122
152
 
123
- 브릿지는 각 Passport.js 전략 실행을 정확히 한 번만 정착(settle)시킵니다. 전략은 바인딩된 Passport 액션(`success`, `fail`, `redirect`, `pass`, `error`) 중 하나를 호출해야 하며, promise rejection, 액션 없이 완료된 promise, 그리고 제한된 action timeout을 초과한 callback-style 실행은 요청을 미해결 상태로 두지 않고 인증 실패로 처리됩니다. `handled: true`를 포함한 모든 `AuthStrategyResult`는 전략이 response를 commit한 뒤에는 `principal`을 함께 포함하더라도 완전히 terminal입니다. 이때 `AuthGuard`는 principal validation, scope check, `requestContext.principal` 할당, protected handler 실행을 모두 건너뜁니다. 커스텀 `mapPrincipal` 함수는 비어 있지 않은 `subject`와 객체 형태의 `claims`를 포함한 유효한 fluo `Principal`을 반환해야 합니다.
153
+ 브릿지는 각 Passport.js 전략 실행을 정확히 한 번만 정착(settle)시킵니다. 전략은 바인딩된 Passport 액션(`success`, `fail`, `redirect`, `pass`, `error`) 중 하나를 호출해야 하며, promise rejection, 액션 없이 완료된 promise, 그리고 제한된 action timeout을 초과한 callback-style 실행은 요청을 미해결 상태로 두지 않고 인증 실패로 처리됩니다. 브릿지는 `authenticate()`의 반환 값을 소비하지 않으며, 바인딩된 액션만이 요청을 정착시킵니다. `handled: true`를 포함한 모든 `AuthStrategyResult`는 전략이 response를 commit한 뒤에는 `principal`을 함께 포함하더라도 완전히 terminal입니다. 이때 `AuthGuard`는 principal validation, scope check, `requestContext.principal` 할당, protected handler 실행을 모두 건너뜁니다. 커스텀 `mapPrincipal` 함수는 비어 있지 않은 `subject`와 객체 형태의 `claims`를 포함한 유효한 fluo `Principal`을 반환해야 합니다.
154
+
155
+ 브릿지는 활성 platform adapter가 raw host request를 제공하면 그 값을, 그렇지 않으면 정규화된 fluo request를 `authenticate(request, options)`에 전달합니다. Passport-initialized host request를 생성하지는 않으므로, Passport middleware augmentation(`request.logIn`, `request.user`, session state)이나 adapter별 request field에 의존하는 strategy는 cutover 전에 [NestJS → fluo Migration Map](../../docs/getting-started/migrate-from-nestjs.ko.md#passportjs-bridge-migration)의 compatibility checklist를 확인해야 합니다.
156
+
157
+ `actionTimeoutMs` 기본값은 `30_000`밀리초이며 0 이상인 유한한 숫자여야 합니다. `0`으로 설정하면 다음 timer turn에 timeout settlement를 예약합니다. 음수, `NaN`, 무한대 값은 settlement 제한을 비활성화하지 않고 bridge strategy 생성 시 `RangeError`를 발생시킵니다.
158
+
159
+ 애플리케이션 종료가 시작되면 진행 중인 모든 bridge 실행을 취소하고 action timeout을 정리한 뒤, 설정된 timeout까지 request state를 유지하지 않고 pending authentication을 reject합니다. Bridge는 일반 application lifecycle에 참여하므로, application 또는 application context가 close될 때 이 취소가 실행됩니다.
124
160
 
125
161
  ### 쿠키 인증 프리셋
126
162
 
@@ -141,6 +177,7 @@ import {
141
177
  CookieAuthModule.forRoot(),
142
178
  JwtModule.forRoot({
143
179
  algorithms: ['HS256'],
180
+ global: true,
144
181
  secret: 'your-secure-secret',
145
182
  }),
146
183
  PassportModule.forRoot(
@@ -152,7 +189,7 @@ import {
152
189
  export class AuthModule {}
153
190
  ```
154
191
 
155
- 애플리케이션 모듈에서 cookie-auth 지원이 필요하면 `CookieAuthModule.forRoot(...)`, `JwtModule.forRoot(...)`, `PassportModule.forRoot(...)`를 함께 import 하세요. Cookie preset은 `CookieAuthStrategy`와 cookie option을 제공하고, JWT 검증은 여전히 `@fluojs/jwt`에서 오며, passport registry는 여전히 `PassportModule.forRoot(...)`에서 옵니다.
192
+ 애플리케이션 모듈에서 cookie-auth 지원이 필요하면 `CookieAuthModule.forRoot(...)`, `JwtModule.forRoot(...)`, `PassportModule.forRoot(...)`를 함께 import 하세요. 이 graph에서 `CookieAuthModule`과 `JwtModule`은 sibling import이므로, cookie module이 `CookieAuthStrategy`를 resolve할 때 `DefaultJwtVerifier`를 볼 수 있도록 문서화된 JWT option `global: true`를 설정해야 합니다. Cookie preset은 `CookieAuthStrategy`와 cookie option을 제공하고, JWT 검증은 여전히 `@fluojs/jwt`에서 오며, passport registry는 여전히 `PassportModule.forRoot(...)`에서 옵니다.
156
193
 
157
194
  `CookieAuthModule.forRoot(...)`는 애플리케이션 등록을 위한 canonical module-first entrypoint입니다. `createCookieAuthPreset(...)`은 provider graph를 직접 조립하는 host를 위한 compatibility bundle로 공개되어 있으며, 동일한 cookie-auth provider와 대응하는 strategy registration을 반환합니다. 애플리케이션 문서, generated code, 일반 app module에서는 module facade를 우선 사용하세요.
158
195
 
@@ -160,7 +197,11 @@ export class AuthModule {}
160
197
 
161
198
  Cookie access token은 비어 있지 않은 문자열이어야 합니다. `requireAccessToken: false`일 때만 누락된 cookie가 `{ authenticated: false }`로 resolve될 수 있으며, 존재하지만 malformed인 cookie 값은 JWT 검증 전에 항상 인증 실패로 처리됩니다.
162
199
 
163
- `CookieManager`는 underlying adapter가 기존 header를 `set-cookie`처럼 다른 casing으로 저장했더라도, response에 이미 설정된 cookie를 덮어쓰지 않고 access-token refresh-token `Set-Cookie` 값을 append합니다.
200
+ Cookie 검증 실패는 문서화된 분류를 유지합니다. 만료된 access token은 `AuthenticationExpiredError`, 유효하지 않은 access token은 `AuthenticationFailedError`, 누락되거나 malformed인 access-token cookie는 `AuthenticationRequiredError`를 발생시킵니다. 원본 `@fluojs/jwt` error는 `cause`로 보존되며, `AuthGuard`는 세 경우 모두 HTTP `401`로 응답합니다.
201
+
202
+ `CookieManagerConfig.cookieOptions`는 `SetCookieOptions`를 받습니다. `accessTokenTtlSeconds`와 `refreshTokenTtlSeconds` field는 positional TTL 인자가 생략되었을 때 해당 token cookie의 기본 `Max-Age`가 되며, 명시적인 positional TTL이 항상 우선합니다.
203
+
204
+ `CookieManager`는 underlying adapter가 기존 header를 `set-cookie`처럼 다른 casing으로 저장했더라도, response에 이미 설정된 cookie를 덮어쓰지 않고 access-token 및 refresh-token `Set-Cookie` 값을 append합니다. portable HTTP serializer를 사용하므로 cookie 값은 emit 전에 percent-encoding되며, malformed cookie name 또는 attribute는 invalid header를 emit하는 대신 validation에서 실패합니다.
164
205
 
165
206
  보호된 라우트는 계속 `@UseAuth(...)`를 사용해야 합니다. `requireAccessToken: false`를 설정해도 쿠키가 없을 때는 익명 principal이 아니라 명시적인 미인증 결과를 반환하므로, 보호된 라우트는 요청을 계속 거부합니다.
166
207
 
@@ -187,6 +228,7 @@ export class SessionController {
187
228
  ```typescript
188
229
  import { Module } from '@fluojs/core';
189
230
  import { Controller, Post, type RequestContext } from '@fluojs/http';
231
+ import { JwtModule } from '@fluojs/jwt';
190
232
  import {
191
233
  PassportModule,
192
234
  REFRESH_TOKEN_STRATEGY_NAME,
@@ -195,29 +237,36 @@ import {
195
237
  UseAuth,
196
238
  } from '@fluojs/passport';
197
239
 
240
+ @Controller('/auth')
241
+ export class AuthController {
242
+ @Post('/refresh')
243
+ @UseAuth('refresh-token')
244
+ async refresh(input: never, ctx: RequestContext) {
245
+ return ctx.principal; // 새 토큰 쌍이 포함된 principal 반환
246
+ }
247
+ }
248
+
198
249
  @Module({
250
+ controllers: [AuthController],
199
251
  imports: [
252
+ JwtModule.forRoot({
253
+ algorithms: ['HS256'],
254
+ global: true,
255
+ secret: 'your-access-token-secret',
256
+ }),
200
257
  RefreshTokenModule.forRoot(MyRefreshTokenService),
201
258
  PassportModule.forRoot(
202
259
  { defaultStrategy: REFRESH_TOKEN_STRATEGY_NAME },
203
260
  [{ name: REFRESH_TOKEN_STRATEGY_NAME, token: RefreshTokenStrategy }],
204
261
  ),
205
262
  ],
206
- providers: [MyRefreshTokenService],
207
263
  })
208
264
  export class AuthModule {}
209
-
210
- @Controller('/auth')
211
- export class AuthController {
212
- @Post('/refresh')
213
- @UseAuth('refresh-token')
214
- async refresh(input: never, ctx: RequestContext) {
215
- return ctx.principal; // 새 토큰 쌍이 포함된 principal 반환
216
- }
217
- }
218
265
  ```
219
266
 
220
- `RefreshTokenModule.forRoot(...)`를 `PassportModule.forRoot(...)`와 함께 import 하여 refresh-token 전략과 공유 `REFRESH_TOKEN_SERVICE` alias를 같은 모듈 wiring에서 사용하세요.
267
+ `JwtModule.forRoot(...)`, `RefreshTokenModule.forRoot(...)`, `PassportModule.forRoot(...)`를 함께 import 하세요. 이 graph에서 `RefreshTokenStrategy`는 `JwtModule`의 sibling인 `RefreshTokenModule`에 속하므로, 이 예제는 문서화된 `global: true` option을 설정해 refresh module이 strategy를 resolve할 때 `DefaultJwtVerifier`를 볼 수 있게 합니다. `RefreshTokenModule.forRoot(MyRefreshTokenService)`는 service class를 refresh module 내부에 등록하고 공유 `REFRESH_TOKEN_SERVICE` alias로 export합니다. 이 class에 constructor dependency가 있다면 application-owned module에서 dependency제공하고 export한 뒤 `imports`로 전달하세요. String 및 symbol service token은 `RefreshTokenModule`에서 보여야 합니다. Imported module 또는 global module에서 export하거나 bootstrap runtime provider로 등록하세요. `MyRefreshTokenService`를 application module의 `providers`에 다시 등록하지 마세요. 중복 provider registration이 되며 bootstrap은 기본적으로 warning을 남기고 `duplicateProviderPolicy: 'throw'`에서는 거부할 수 있습니다. Application code가 service를 사용할 때는 export된 `REFRESH_TOKEN_SERVICE` alias를 inject하고, refresh route가 실제로 존재하려면 application module이 `AuthController`를 등록해야 합니다. `PassportModule`은 `@UseAuth('refresh-token')`가 resolve하는 named strategy를 등록합니다.
268
+
269
+ 교환에 성공하면 `ctx.principal`은 `RefreshTokenPrincipal` shape으로 resolve됩니다. Rotation된 token 쌍은 `claims.accessToken`과 `claims.refreshToken`에 중첩되고, 검증된 `subject`는 최상위에 위치합니다. 별도로 export되는 `RefreshTokenAuthResult` 타입은 refresh endpoint가 client에 반환하는 application-facing 교환 payload를 설명합니다.
221
270
 
222
271
  `RefreshTokenStrategy`는 `body.refreshToken`, `Authorization: Bearer ...`, `x-refresh-token`에서 token을 읽습니다. Malformed non-string token은 인증 실패로 처리됩니다. Rotation 후에는 `@fluojs/jwt`가 반환한 정규화 access-token principal subject를 신뢰합니다. `JwtRefreshTokenAdapter`는 `secret`과 backing store가 필요하며, `store: 'memory'`는 development 및 single-instance deployment용이고 rotation은 store consume contract를 통해 재사용을 감지합니다.
223
272
 
@@ -249,17 +298,21 @@ Identity-link 결정을 모델링하려면 `createConservativeAccountLinkPolicy(
249
298
  - `defineAuthRequirement(...)`, `getOwnAuthRequirement(...)`, `getAuthRequirement(...)`: Custom decorator나 tooling을 `AuthGuard`와 통합할 때 auth requirement metadata를 읽고 쓰는 공개 helper입니다.
250
299
  - Scope requirement는 일반적으로 `@RequireScopes(...)`로 작성합니다. 더 낮은 수준의 scope normalization helper는 internal로 남아 있으며 package root export의 일부가 아닙니다.
251
300
 
301
+ ### Bearer JWT preset
302
+ - `BearerJwtStrategy`, `BEARER_JWT_STRATEGY_NAME`: 내장 bearer credential strategy와 안정적인 등록 이름(`'jwt'`)입니다.
303
+ - Bearer helper: `createBearerJwtStrategyRegistration`.
304
+
252
305
  ### Cookie auth preset
253
306
  - `CookieAuthModule`: 내장 cookie-auth preset의 모듈 진입점입니다.
254
307
  - `CookieAuthStrategy`, `COOKIE_AUTH_STRATEGY_NAME`, `COOKIE_AUTH_OPTIONS`, `DEFAULT_COOKIE_AUTH_OPTIONS`, `DEFAULT_COOKIE_OPTIONS`: Cookie strategy wiring token, preset 기본값, response-cookie 기본값입니다.
255
- - `CookieAuthOptions`, `CookieAuthPresetConfig`, `CookieManagerConfig`, `CookieOptions`, `SetCookieOptions`: Cookie strategy 및 response cookie 설정 타입입니다.
308
+ - `CookieAuthOptions`, `CookieAuthPresetConfig`, `CookieManagerConfig`, `CookieOptions`, `SetCookieOptions`: Cookie strategy 및 response cookie 설정 타입입니다. `CookieManagerConfig.cookieOptions`는 `SetCookieOptions`를 받으며, token별 TTL field는 기본 cookie `Max-Age`가 됩니다.
256
309
  - `CookieManager`: HttpOnly access/refresh token cookie를 설정하고 제거하는 유틸리티입니다.
257
310
  - Cookie helper: `createCookieAuthPreset`(compatibility-only manual provider bundle), `createCookieAuthStrategyRegistration`(low-level registration helper), `createCookieManager`, `normalizeCookieAuthOptions`.
258
311
 
259
312
  ### Refresh token preset
260
313
  - `RefreshTokenModule`: 내장 refresh-token preset의 모듈 진입점입니다.
261
314
  - `RefreshTokenStrategy`, `REFRESH_TOKEN_STRATEGY_NAME`, `REFRESH_TOKEN_SERVICE`: Refresh-token strategy 및 service alias wiring입니다.
262
- - `RefreshTokenService`, `RefreshTokenInput`, `RefreshTokenAuthResult`: Application service contract exchange payload shape입니다.
315
+ - `RefreshTokenService`, `RefreshTokenInput`, `RefreshTokenAuthResult`, `RefreshTokenPrincipal`: Application service contract, exchange payload shape, 그리고 교환 성공 후 `ctx.principal`에 resolve되는 principal shape입니다.
263
316
  - `JwtRefreshTokenAdapter`: `@fluojs/jwt`의 refresh logic을 passport interface로 연결합니다.
264
317
  - `REFRESH_TOKEN_MODULE_OPTIONS`, `RefreshTokenModuleOptions`: 필수 `secret`과 `store` 계약을 포함하는 JWT 기반 refresh-token adapter 설정 token 및 option입니다.
265
318
  - Refresh helper: `createRefreshTokenStrategyRegistration`.
@@ -290,4 +343,4 @@ Identity-link 결정을 모델링하려면 `createConservativeAccountLinkPolicy(
290
343
 
291
344
  - `packages/passport/src/guard.test.ts`: 가드 실행 및 권한 강제 패턴 예제.
292
345
  - `packages/passport/src/adapters/passport-js.ts`: Passport.js 브릿지 구현체.
293
- - `examples/auth-jwt-passport/src/auth/bearer.strategy.ts`: 표준 JWT 전략 구현 예제.
346
+ - `packages/passport/src/bearer/bearer-jwt.ts`: 내장 bearer JWT strategy preset.
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  <p><strong><kbd>English</kbd></strong> <a href="./README.ko.md"><kbd>한국어</kbd></a></p>
4
4
 
5
+ Node.js support is `>=24.0.0 <27`. See [Node.js support and migration](../../docs/reference/node-support.md) before upgrading.
6
+
5
7
  Strategy-agnostic auth execution layer for fluo. It routes any `AuthStrategy` through a generic `AuthGuard` into the request context, populating `requestContext.principal`.
6
8
 
7
9
  ## Table of Contents
@@ -31,29 +33,17 @@ npm install @fluojs/passport
31
33
 
32
34
  ### 1. Register Modules
33
35
 
34
- Define your strategies and register them using `PassportModule.forRoot(...)`.
36
+ Use the built-in bearer JWT preset and register it with `PassportModule.forRoot(...)`.
35
37
 
36
38
  ```typescript
37
- import { Inject, Module } from '@fluojs/core';
38
- import type { GuardContext } from '@fluojs/http';
39
- import { DefaultJwtVerifier, JwtModule } from '@fluojs/jwt';
40
- import { AuthenticationRequiredError, PassportModule, type AuthStrategy } from '@fluojs/passport';
41
-
42
- @Inject(DefaultJwtVerifier)
43
- export class BearerJwtStrategy implements AuthStrategy {
44
- constructor(private readonly verifier: DefaultJwtVerifier) {}
45
-
46
- async authenticate(context: GuardContext) {
47
- const authorization = context.requestContext.request.headers.authorization;
48
- const [scheme, token] = typeof authorization === 'string' ? authorization.split(' ') : [];
49
-
50
- if (scheme !== 'Bearer' || !token) {
51
- throw new AuthenticationRequiredError('Bearer access token is required.');
52
- }
53
-
54
- return this.verifier.verifyAccessToken(token);
55
- }
56
- }
39
+ import { Module } from '@fluojs/core';
40
+ import { JwtModule } from '@fluojs/jwt';
41
+ import {
42
+ BEARER_JWT_STRATEGY_NAME,
43
+ BearerJwtStrategy,
44
+ createBearerJwtStrategyRegistration,
45
+ PassportModule,
46
+ } from '@fluojs/passport';
57
47
 
58
48
  @Module({
59
49
  imports: [
@@ -64,8 +54,8 @@ export class BearerJwtStrategy implements AuthStrategy {
64
54
  secret: 'your-secure-secret',
65
55
  }),
66
56
  PassportModule.forRoot(
67
- { defaultStrategy: 'jwt' },
68
- [{ name: 'jwt', token: BearerJwtStrategy }],
57
+ { defaultStrategy: BEARER_JWT_STRATEGY_NAME },
58
+ [createBearerJwtStrategyRegistration()],
69
59
  ),
70
60
  ],
71
61
  providers: [BearerJwtStrategy],
@@ -73,11 +63,11 @@ export class BearerJwtStrategy implements AuthStrategy {
73
63
  export class AuthModule {}
74
64
  ```
75
65
 
76
- JWT-based passport strategies require both pieces of module wiring: `JwtModule.forRoot(...)` registers `DefaultJwtVerifier`, and `PassportModule.forRoot(...)` registers the named strategy that `@UseAuth('jwt')` resolves. Returning the `DefaultJwtVerifier.verifyAccessToken(...)` result preserves the normalized principal contract (`subject`, `claims`, `issuer`, `audience`, `roles`, and `scopes`) that `AuthGuard` writes to `requestContext.principal`.
66
+ `BearerJwtStrategy` is the first-party strategy preset for `Authorization: Bearer <token>` credentials. JWT-based passport strategies require both pieces of module wiring: `JwtModule.forRoot(...)` registers `DefaultJwtVerifier`, the `providers` entry exposes `BearerJwtStrategy` to that verifier through DI, and `PassportModule.forRoot(...)` registers the named strategy that `@UseAuth('jwt')` resolves. Returning the `DefaultJwtVerifier.verifyAccessToken(...)` result unchanged preserves the normalized principal contract (`subject`, `claims`, `issuer`, `audience`, `roles`, and `scopes`) that `AuthGuard` writes to `requestContext.principal`.
77
67
 
78
68
  ### 2. Protect Routes
79
69
 
80
- Use `@UseAuth()` and `@RequireScopes()` to enforce authentication.
70
+ Use `@UseAuth()` to enforce authentication and `@RequireScopes()` to enforce authorization.
81
71
 
82
72
  ```typescript
83
73
  import { Controller, Get, type RequestContext } from '@fluojs/http';
@@ -96,6 +86,46 @@ export class ProfileController {
96
86
 
97
87
  ## Common Patterns
98
88
 
89
+ ### Bearer JWT Preset
90
+
91
+ Use the built-in `BearerJwtStrategy` when your app authenticates `Authorization: Bearer <token>` requests; no application-owned parsing code is required.
92
+
93
+ ```typescript
94
+ import { Module } from '@fluojs/core';
95
+ import { JwtModule } from '@fluojs/jwt';
96
+ import {
97
+ BEARER_JWT_STRATEGY_NAME,
98
+ BearerJwtStrategy,
99
+ createBearerJwtStrategyRegistration,
100
+ PassportModule,
101
+ } from '@fluojs/passport';
102
+
103
+ @Module({
104
+ imports: [
105
+ JwtModule.forRoot({
106
+ algorithms: ['HS256'],
107
+ audience: 'my-app',
108
+ issuer: 'my-api',
109
+ secret: 'your-secure-secret',
110
+ }),
111
+ PassportModule.forRoot(
112
+ { defaultStrategy: BEARER_JWT_STRATEGY_NAME },
113
+ [createBearerJwtStrategyRegistration()],
114
+ ),
115
+ ],
116
+ providers: [BearerJwtStrategy],
117
+ })
118
+ export class AuthModule {}
119
+ ```
120
+
121
+ Register `BearerJwtStrategy` as a provider in the same application module that imports `PassportModule` and `JwtModule`; `JwtModule` remains the owner of verification configuration, and the preset does not introduce a combined JWT/Passport module facade. The preset registers under the stable strategy name `BEARER_JWT_STRATEGY_NAME` (`'jwt'`), so protected routes keep using `@UseAuth('jwt')`, and `createBearerJwtStrategyRegistration()` returns the matching `AuthStrategyRegistration`.
122
+
123
+ Credential extraction is strict. The credential must match RFC 6750 `b64token`: one or more ASCII letters, digits, `-`, `.`, `_`, `~`, `+`, or `/`, followed only by optional trailing `=` padding. It rejects `?`, internal `=`, Unicode, whitespace, and control characters before verification. A missing or empty `Authorization` header throws `AuthenticationRequiredError`, and when the adapter surfaces an array-valued `Authorization` header only the first entry is read. A wrong-scheme or malformed header throws `AuthenticationFailedError`; the scheme match is case-insensitive and uses one or more ASCII spaces. Missing and malformed credentials serialize the bare `WWW-Authenticate: Bearer` challenge. Verification failures keep `DefaultJwtVerifier` error causality: expired credentials throw `AuthenticationExpiredError`, and `JwtInvalidTokenError` credentials throw `AuthenticationFailedError`, each preserving the original JWT error as `cause` and serializing `WWW-Authenticate: Bearer error="invalid_token"`. `JwtConfigurationError` and verifier-provider infrastructure failures propagate unchanged instead of becoming credential failures. `AuthGuard` maps only credential failures to the canonical `401 Unauthorized` response, and scope mismatches still produce `403 Forbidden`.
124
+
125
+ `BearerJwtStrategy` returns the normalized `JwtPrincipal` from `DefaultJwtVerifier.verifyAccessToken(...)` unchanged, preserving `subject`, `claims`, `issuer`, `audience`, `roles`, and `scopes`.
126
+
127
+ A custom `AuthStrategy` remains the extension point for token revocation, account-state checks, alternate extraction, and application-owned policy.
128
+
99
129
  ### Passport.js Bridge
100
130
 
101
131
  Easily adapt any standard Passport.js strategy (like `passport-google-oauth20`) to work with fluo's DI and async lifecycle.
@@ -120,7 +150,13 @@ export class AuthModule {}
120
150
 
121
151
  This bridge helper is the official exception to the module-facade rule for Passport.js adapters because third-party strategy instances must be bound as providers before `AuthGuard` can execute them. It does not replace `PassportModule`, `@UseAuth(...)`, or `AuthGuard` as the application-facing authentication surface.
122
152
 
123
- The bridge settles each Passport.js strategy execution exactly once. A strategy must call one of the bound Passport actions (`success`, `fail`, `redirect`, `pass`, or `error`); promise rejections, promise completion without an action, and callback-style executions that exceed the bounded action timeout become authentication failures instead of leaving the request unresolved. Any `AuthStrategyResult` with `handled: true` is fully terminal after the strategy commits a response, even if it also includes a `principal`; `AuthGuard` skips principal validation, scope checks, `requestContext.principal` assignment, and the protected handler. Custom `mapPrincipal` functions must return a valid fluo `Principal` with a non-empty `subject` and object `claims`.
153
+ The bridge settles each Passport.js strategy execution exactly once. A strategy must call one of the bound Passport actions (`success`, `fail`, `redirect`, `pass`, or `error`); promise rejections, promise completion without an action, and callback-style executions that exceed the bounded action timeout become authentication failures instead of leaving the request unresolved. The bridge never consumes an `authenticate()` return value: only a bound action settles the request. Any `AuthStrategyResult` with `handled: true` is fully terminal after the strategy commits a response, even if it also includes a `principal`; `AuthGuard` skips principal validation, scope checks, `requestContext.principal` assignment, and the protected handler. Custom `mapPrincipal` functions must return a valid fluo `Principal` with a non-empty `subject` and object `claims`.
154
+
155
+ The bridge calls `authenticate(request, options)` with the active platform adapter's raw host request when one exists, and with the normalized fluo request otherwise. It never creates a Passport-initialized host request, so strategies that depend on Passport middleware augmentation (`request.logIn`, `request.user`, session state) or on adapter-specific request fields need the compatibility checklist in [NestJS → fluo Migration Map](../../docs/getting-started/migrate-from-nestjs.md#passportjs-bridge-migration) before cutover.
156
+
157
+ `actionTimeoutMs` defaults to `30_000` milliseconds and must be a non-negative finite number. Set it to `0` to schedule timeout settlement on the next timer turn. Negative, `NaN`, and infinite values throw `RangeError` when the bridge strategy is constructed instead of disabling the settlement bound.
158
+
159
+ Application shutdown cancels every in-flight bridge execution, clears its action timeout, and rejects the pending authentication instead of retaining request state through the configured timeout. The bridge participates in the ordinary application lifecycle, so this cancellation runs when the application or application context is closed.
124
160
 
125
161
  ### Cookie Auth Preset
126
162
 
@@ -141,6 +177,7 @@ import {
141
177
  CookieAuthModule.forRoot(),
142
178
  JwtModule.forRoot({
143
179
  algorithms: ['HS256'],
180
+ global: true,
144
181
  secret: 'your-secure-secret',
145
182
  }),
146
183
  PassportModule.forRoot(
@@ -152,7 +189,7 @@ import {
152
189
  export class AuthModule {}
153
190
  ```
154
191
 
155
- Import `CookieAuthModule.forRoot(...)`, `JwtModule.forRoot(...)`, and `PassportModule.forRoot(...)` together when you want cookie-auth support in an application module. The cookie preset provides `CookieAuthStrategy` and cookie options; JWT verification still comes from `@fluojs/jwt`, and the passport registry still comes from `PassportModule.forRoot(...)`.
192
+ Import `CookieAuthModule.forRoot(...)`, `JwtModule.forRoot(...)`, and `PassportModule.forRoot(...)` together when you want cookie-auth support in an application module. `CookieAuthModule` and `JwtModule` are sibling imports in this graph, so set the documented `global: true` JWT option to make `DefaultJwtVerifier` visible when the cookie module resolves `CookieAuthStrategy`. The cookie preset provides `CookieAuthStrategy` and cookie options; JWT verification still comes from `@fluojs/jwt`, and the passport registry still comes from `PassportModule.forRoot(...)`.
156
193
 
157
194
  `CookieAuthModule.forRoot(...)` is the canonical module-first entrypoint for application registration. `createCookieAuthPreset(...)` remains public as a compatibility bundle for manual provider composition; it returns the same cookie-auth providers plus the matching strategy registration for hosts that assemble provider graphs themselves. Prefer the module facade in application docs, generated code, and ordinary app modules.
158
195
 
@@ -160,7 +197,11 @@ Import `CookieAuthModule.forRoot(...)`, `JwtModule.forRoot(...)`, and `PassportM
160
197
 
161
198
  Cookie access tokens must be non-empty strings. Missing cookies can resolve to `{ authenticated: false }` only when `requireAccessToken: false`; malformed present cookie values always fail authentication before JWT verification.
162
199
 
163
- `CookieManager` appends access-token and refresh-token `Set-Cookie` values without overwriting cookies that were already placed on the response, even when the underlying adapter stores the existing header with different casing such as `set-cookie`.
200
+ Cookie verification failures keep their documented classification: expired access tokens raise `AuthenticationExpiredError`, invalid access tokens raise `AuthenticationFailedError`, and missing or malformed access-token cookies raise `AuthenticationRequiredError`. The originating `@fluojs/jwt` error is preserved as the `cause`, while `AuthGuard` still answers HTTP `401` for every variant.
201
+
202
+ `CookieManagerConfig.cookieOptions` accepts `SetCookieOptions`. Its `accessTokenTtlSeconds` and `refreshTokenTtlSeconds` fields supply the default `Max-Age` for the matching token cookie when the positional TTL argument is omitted; an explicit positional TTL always wins.
203
+
204
+ `CookieManager` appends access-token and refresh-token `Set-Cookie` values without overwriting cookies that were already placed on the response, even when the underlying adapter stores the existing header with different casing such as `set-cookie`. It uses the portable HTTP serializer, so cookie values are percent-encoded before they are emitted and malformed cookie names or attributes fail validation instead of emitting invalid headers.
164
205
 
165
206
  Protected routes must keep using `@UseAuth(...)`. If you configure `requireAccessToken: false`, a missing cookie resolves to an explicit unauthenticated result instead of an anonymous principal, so protected routes still reject the request.
166
207
 
@@ -187,6 +228,7 @@ The package provides a built-in `RefreshTokenStrategy` plus the `RefreshTokenMod
187
228
  ```typescript
188
229
  import { Module } from '@fluojs/core';
189
230
  import { Controller, Post, type RequestContext } from '@fluojs/http';
231
+ import { JwtModule } from '@fluojs/jwt';
190
232
  import {
191
233
  PassportModule,
192
234
  REFRESH_TOKEN_STRATEGY_NAME,
@@ -195,29 +237,36 @@ import {
195
237
  UseAuth,
196
238
  } from '@fluojs/passport';
197
239
 
240
+ @Controller('/auth')
241
+ export class AuthController {
242
+ @Post('/refresh')
243
+ @UseAuth('refresh-token')
244
+ async refresh(input: never, ctx: RequestContext) {
245
+ return ctx.principal; // Contains new token pair
246
+ }
247
+ }
248
+
198
249
  @Module({
250
+ controllers: [AuthController],
199
251
  imports: [
252
+ JwtModule.forRoot({
253
+ algorithms: ['HS256'],
254
+ global: true,
255
+ secret: 'your-access-token-secret',
256
+ }),
200
257
  RefreshTokenModule.forRoot(MyRefreshTokenService),
201
258
  PassportModule.forRoot(
202
259
  { defaultStrategy: REFRESH_TOKEN_STRATEGY_NAME },
203
260
  [{ name: REFRESH_TOKEN_STRATEGY_NAME, token: RefreshTokenStrategy }],
204
261
  ),
205
262
  ],
206
- providers: [MyRefreshTokenService],
207
263
  })
208
264
  export class AuthModule {}
209
-
210
- @Controller('/auth')
211
- export class AuthController {
212
- @Post('/refresh')
213
- @UseAuth('refresh-token')
214
- async refresh(input: never, ctx: RequestContext) {
215
- return ctx.principal; // Contains new token pair
216
- }
217
- }
218
265
  ```
219
266
 
220
- Import `RefreshTokenModule.forRoot(...)` alongside `PassportModule.forRoot(...)` so the refresh-token strategy and shared `REFRESH_TOKEN_SERVICE` alias are available in the same module wiring.
267
+ Import `JwtModule.forRoot(...)`, `RefreshTokenModule.forRoot(...)`, and `PassportModule.forRoot(...)` together. `RefreshTokenStrategy` belongs to `RefreshTokenModule`, which is a sibling of `JwtModule` in this graph, so this example sets the documented `global: true` option to make `DefaultJwtVerifier` visible when the refresh module resolves the strategy. `RefreshTokenModule.forRoot(MyRefreshTokenService)` registers the service class inside the refresh module and exports it through the shared `REFRESH_TOKEN_SERVICE` alias. When that class has constructor dependencies, place those dependencies in an application-owned module that exports them, then pass that module through `imports`. String and symbol service tokens must be visible to `RefreshTokenModule`: export them through an imported module, a global module, or bootstrap runtime providers. Do not also list `MyRefreshTokenService` in the application module's `providers`; that duplicates a provider registration, which bootstrap warns about by default and can reject under `duplicateProviderPolicy: 'throw'`. Inject the exported `REFRESH_TOKEN_SERVICE` alias where application code needs the service, and register `AuthController` in the application module so the refresh route exists. `PassportModule` registers the named strategy resolved by `@UseAuth('refresh-token')`.
268
+
269
+ A successful exchange resolves `ctx.principal` to the `RefreshTokenPrincipal` shape: the rotated pair is nested under `claims.accessToken` and `claims.refreshToken`, with the verified `subject` at the top level. The separate exported `RefreshTokenAuthResult` type describes the application-facing exchange payload a refresh endpoint returns to clients.
221
270
 
222
271
  `RefreshTokenStrategy` reads tokens from `body.refreshToken`, `Authorization: Bearer ...`, or `x-refresh-token`; malformed non-string tokens fail authentication. After rotation, it trusts the normalized access-token principal subject returned by `@fluojs/jwt`. `JwtRefreshTokenAdapter` requires a `secret` and a backing store; `store: 'memory'` is for development and single-instance deployments only, and rotation detects reuse through the store consume contract.
223
272
 
@@ -249,17 +298,21 @@ Use `createConservativeAccountLinkPolicy(...)` and `resolveAccountLinking(...)`
249
298
  - `defineAuthRequirement(...)`, `getOwnAuthRequirement(...)`, `getAuthRequirement(...)`: Public helpers for reading and writing auth requirement metadata when integrating custom decorators or tooling with `AuthGuard`.
250
299
  - Scope requirements are normally authored with `@RequireScopes(...)`; lower-level scope normalization helpers remain internal and are not part of the package root export.
251
300
 
301
+ ### Bearer JWT Preset
302
+ - `BearerJwtStrategy`, `BEARER_JWT_STRATEGY_NAME`: Built-in bearer credential strategy and its stable registration name (`'jwt'`).
303
+ - Bearer helper: `createBearerJwtStrategyRegistration`.
304
+
252
305
  ### Cookie Auth Preset
253
306
  - `CookieAuthModule`: Module entry point for the built-in cookie-auth preset.
254
307
  - `CookieAuthStrategy`, `COOKIE_AUTH_STRATEGY_NAME`, `COOKIE_AUTH_OPTIONS`, `DEFAULT_COOKIE_AUTH_OPTIONS`, `DEFAULT_COOKIE_OPTIONS`: Cookie strategy wiring tokens, preset defaults, and response-cookie defaults.
255
- - `CookieAuthOptions`, `CookieAuthPresetConfig`, `CookieManagerConfig`, `CookieOptions`, `SetCookieOptions`: Cookie strategy and response cookie configuration types.
308
+ - `CookieAuthOptions`, `CookieAuthPresetConfig`, `CookieManagerConfig`, `CookieOptions`, `SetCookieOptions`: Cookie strategy and response cookie configuration types. `CookieManagerConfig.cookieOptions` accepts `SetCookieOptions`, whose per-token TTL fields become default cookie `Max-Age` values.
256
309
  - `CookieManager`: Utility for setting and clearing HttpOnly access/refresh token cookies.
257
310
  - Cookie helpers: `createCookieAuthPreset` (compatibility-only manual provider bundle), `createCookieAuthStrategyRegistration` (low-level registration helper), `createCookieManager`, `normalizeCookieAuthOptions`.
258
311
 
259
312
  ### Refresh Token Preset
260
313
  - `RefreshTokenModule`: Module entry point for the built-in refresh-token preset.
261
314
  - `RefreshTokenStrategy`, `REFRESH_TOKEN_STRATEGY_NAME`, `REFRESH_TOKEN_SERVICE`: Refresh-token strategy and service alias wiring.
262
- - `RefreshTokenService`, `RefreshTokenInput`, `RefreshTokenAuthResult`: Application service contract and exchange payload shapes.
315
+ - `RefreshTokenService`, `RefreshTokenInput`, `RefreshTokenAuthResult`, `RefreshTokenPrincipal`: Application service contract, exchange payload shapes, and the principal shape resolved onto `ctx.principal` after a successful exchange.
263
316
  - `JwtRefreshTokenAdapter`: Bridges `@fluojs/jwt` refresh logic to the passport interface.
264
317
  - `REFRESH_TOKEN_MODULE_OPTIONS`, `RefreshTokenModuleOptions`: JWT-backed refresh-token adapter configuration token and options, including the required `secret` and `store` contract.
265
318
  - Refresh helpers: `createRefreshTokenStrategyRegistration`.
@@ -290,4 +343,4 @@ Use `createConservativeAccountLinkPolicy(...)` and `resolveAccountLinking(...)`
290
343
 
291
344
  - `packages/passport/src/guard.test.ts`: Guard execution and scope enforcement patterns.
292
345
  - `packages/passport/src/adapters/passport-js.ts`: Implementation of the Passport.js bridge.
293
- - `examples/auth-jwt-passport/src/auth/bearer.strategy.ts`: JWT strategy implementation.
346
+ - `packages/passport/src/bearer/bearer-jwt.ts`: Built-in bearer JWT strategy preset.
@@ -1,6 +1,7 @@
1
1
  import type { Token } from '@fluojs/core';
2
- import type { GuardContext, Principal } from '@fluojs/http';
3
2
  import type { Provider } from '@fluojs/di';
3
+ import type { GuardContext, Principal } from '@fluojs/http';
4
+ import type { OnApplicationShutdown } from '@fluojs/runtime';
4
5
  import type { AuthHandledResult, AuthStrategy, AuthStrategyRegistration } from '../types.js';
5
6
  /**
6
7
  * Represents a Passport.js strategy-like object that implements
@@ -10,9 +11,14 @@ export interface PassportJsStrategyLike {
10
11
  /**
11
12
  * Performs authentication for the given request and options.
12
13
  *
13
- * @param request - The raw request object from the underlying framework.
14
+ * @param request - The active platform adapter's raw host request when one exists,
15
+ * otherwise the normalized fluo request. The bridge never constructs a
16
+ * Passport-initialized host request.
14
17
  * @param options - Strategy-specific authentication options.
15
- * @returns An execution result or a promise resolving to one.
18
+ * @returns The bridge ignores the returned value. Strategy implementations must
19
+ * settle the request by calling one of the bound Passport actions (`success`,
20
+ * `fail`, `redirect`, `pass`, or `error`). A returned promise only propagates
21
+ * rejections; resolving it without calling an action fails authentication.
16
22
  */
17
23
  authenticate(request: unknown, options?: unknown): unknown;
18
24
  }
@@ -38,7 +44,10 @@ export type PassportJsPrincipalMapper = (input: PassportJsPrincipalMapperInput)
38
44
  export interface PassportJsAuthStrategyOptions {
39
45
  /** Optional options to pass to the Passport strategy's `authenticate` method. */
40
46
  authenticateOptions?: Readonly<Record<string, unknown>>;
41
- /** Maximum time to wait for callback-style strategies to call a bound Passport action. */
47
+ /**
48
+ * Maximum time to wait for callback-style strategies to call a bound Passport action.
49
+ * Must be a non-negative finite number. A value of `0` schedules settlement on the next timer turn.
50
+ */
42
51
  actionTimeoutMs?: number;
43
52
  /** Optional custom mapper for converting user data to a principal. */
44
53
  mapPrincipal?: PassportJsPrincipalMapper;
@@ -55,13 +64,19 @@ export interface PassportJsStrategyBridge {
55
64
  /**
56
65
  * A bridge strategy that allows using Passport.js strategies within the fluo
57
66
  * authentication framework.
67
+ *
68
+ * @throws {RangeError} When `actionTimeoutMs` is negative or non-finite.
58
69
  */
59
- export declare class PassportJsAuthStrategy implements AuthStrategy {
70
+ export declare class PassportJsAuthStrategy implements AuthStrategy, OnApplicationShutdown {
60
71
  private readonly strategyTemplate;
61
72
  private readonly options;
73
+ private readonly actionTimeoutMs;
62
74
  private readonly requestState;
75
+ private acceptingAuthentications;
63
76
  constructor(strategyTemplate: PassportJsStrategyLike, options?: PassportJsAuthStrategyOptions);
64
77
  authenticate(context: GuardContext): Promise<Principal | AuthHandledResult>;
78
+ /** Cancels every unsettled Passport.js execution when application shutdown starts. */
79
+ onApplicationShutdown(): void;
65
80
  private createExecutableStrategy;
66
81
  private settle;
67
82
  private bindStrategyActions;
@@ -1 +1 @@
1
- {"version":3,"file":"passport-js.d.ts","sourceRoot":"","sources":["../../src/adapters/passport-js.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAU7F;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;;;OAMG;IACH,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;CAC5D;AAcD;;;GAGG;AACH,MAAM,WAAW,8BAA8B;IAC7C,iDAAiD;IACjD,OAAO,EAAE,YAAY,CAAC;IACtB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,yDAAyD;IACzD,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,KAAK,EAAE,8BAA8B,KAAK,SAAS,CAAC;AAE7F;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC5C,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,0FAA0F;IAC1F,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,YAAY,CAAC,EAAE,yBAAyB,CAAC;CAC1C;AAID;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,mEAAmE;IACnE,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,EAAE,wBAAwB,CAAC;CACpC;AAoGD;;;GAGG;AACH,qBAAa,sBAAuB,YAAW,YAAY;IAIvD,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJ1B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAuE;gBAGjF,gBAAgB,EAAE,sBAAsB,EACxC,OAAO,GAAE,6BAAkC;IAG9D,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,GAAG,iBAAiB,CAAC;IAgE3E,OAAO,CAAC,wBAAwB;IAmBhC,OAAO,CAAC,MAAM;IAiBd,OAAO,CAAC,mBAAmB;IAqC3B,OAAO,CAAC,kBAAkB;CAS3B;AAED;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,KAAK,CAAC,sBAAsB,CAAC,EAC5C,OAAO,GAAE,6BAAkC,GAC1C,wBAAwB,CAwB1B"}
1
+ {"version":3,"file":"passport-js.d.ts","sourceRoot":"","sources":["../../src/adapters/passport-js.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAI7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAU7F;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;;;;;;;;OAWG;IACH,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;CAC5D;AAcD;;;GAGG;AACH,MAAM,WAAW,8BAA8B;IAC7C,iDAAiD;IACjD,OAAO,EAAE,YAAY,CAAC;IACtB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,yDAAyD;IACzD,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,KAAK,EAAE,8BAA8B,KAAK,SAAS,CAAC;AAE7F;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC5C,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,YAAY,CAAC,EAAE,yBAAyB,CAAC;CAC1C;AAID;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,mEAAmE;IACnE,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,EAAE,wBAAwB,CAAC;CACpC;AAoGD;;;;;GAKG;AACH,qBAAa,sBAAuB,YAAW,YAAY,EAAE,qBAAqB;IAM9E,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAN1B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmE;IAChG,OAAO,CAAC,wBAAwB,CAAQ;gBAGrB,gBAAgB,EAAE,sBAAsB,EACxC,OAAO,GAAE,6BAAkC;IAW9D,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,GAAG,iBAAiB,CAAC;IAkE3E,sFAAsF;IACtF,qBAAqB,IAAI,IAAI;IAY7B,OAAO,CAAC,wBAAwB;IAmBhC,OAAO,CAAC,MAAM;IAiBd,OAAO,CAAC,mBAAmB;IAqC3B,OAAO,CAAC,kBAAkB;CAS3B;AAED;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,KAAK,CAAC,sBAAsB,CAAC,EAC5C,OAAO,GAAE,6BAAkC,GAC1C,wBAAwB,CAwB1B"}