@fluojs/passport 1.0.0 → 1.0.2

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
@@ -106,7 +106,7 @@ const googleBridge = createPassportJsStrategyBridge('google', GoogleStrategy, {
106
106
  });
107
107
  ```
108
108
 
109
- 브릿지는 각 Passport.js 전략 실행을 정확히 한 번만 정착(settle)시킵니다. 전략은 바인딩된 Passport 액션(`success`, `fail`, `redirect`, `pass`, `error`) 중 하나를 호출해야 하며, promise rejection 또는 액션 없이 완료된 promise 요청을 미해결 상태로 두지 않고 인증 실패로 처리됩니다. 커스텀 `mapPrincipal` 함수는 비어 있지 않은 `subject`와 객체 형태의 `claims`를 포함한 유효한 fluo `Principal`을 반환해야 합니다.
109
+ 브릿지는 각 Passport.js 전략 실행을 정확히 한 번만 정착(settle)시킵니다. 전략은 바인딩된 Passport 액션(`success`, `fail`, `redirect`, `pass`, `error`) 중 하나를 호출해야 하며, promise rejection, 액션 없이 완료된 promise, 그리고 제한된 action timeout을 초과한 callback-style 실행은 요청을 미해결 상태로 두지 않고 인증 실패로 처리됩니다. 커스텀 `mapPrincipal` 함수는 비어 있지 않은 `subject`와 객체 형태의 `claims`를 포함한 유효한 fluo `Principal`을 반환해야 합니다.
110
110
 
111
111
  ### 쿠키 인증 프리셋
112
112
 
@@ -201,11 +201,11 @@ export class AuthController {
201
201
 
202
202
  `RefreshTokenModule.forRoot(...)`를 `PassportModule.forRoot(...)`와 함께 import 하여 refresh-token 전략과 공유 `REFRESH_TOKEN_SERVICE` alias를 같은 모듈 wiring에서 사용하세요.
203
203
 
204
- `RefreshTokenStrategy`는 `body.refreshToken`, `Authorization: Bearer ...`, `x-refresh-token`에서 token을 읽습니다. Malformed non-string token은 인증 실패로 처리됩니다. `JwtRefreshTokenAdapter`는 `secret`과 backing store가 필요하며, `store: 'memory'`는 development 및 single-instance deployment용입니다.
204
+ `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를 통해 재사용을 감지합니다.
205
205
 
206
206
  ### Account linking과 status
207
207
 
208
- Identity-link 결정을 모델링하려면 `createConservativeAccountLinkPolicy(...)`와 `resolveAccountLinking(...)`을 사용합니다. 기본 conservative policy는 명시적인 existing link 또는 user-confirmed match만 연결하고, 그 외에는 create/skip/reject/conflict를 결정적으로 처리합니다.
208
+ Identity-link 결정을 모델링하려면 `createConservativeAccountLinkPolicy(...)`와 `resolveAccountLinking(...)`을 사용합니다. 기본 conservative policy는 모호하지 않은 단일 existing account link 또는 user-confirmed match만 연결하고, 여러 existing link는 conflict로 보고하며, 그 외에는 create/skip/reject/conflict를 결정적으로 처리합니다.
209
209
 
210
210
  `createPassportPlatformStatusSnapshot(...)`와 `createPassportPlatformDiagnosticIssues(...)`는 등록된 strategy, default strategy 설정, preset, refresh-token store readiness에 대한 readiness/health diagnostic을 노출합니다.
211
211
 
@@ -216,21 +216,50 @@ Identity-link 결정을 모델링하려면 `createConservativeAccountLinkPolicy(
216
216
  - `@UseOptionalAuth(strategyName)`: `AuthGuard`를 부착하지만 전략이 자격 증명 누락을 보고하면 스코프가 없는 라우트는 계속 진행할 수 있게 합니다.
217
217
  - `@RequireScopes(...scopes)`: 특정 권한(스코프) 요구 사항을 강제합니다.
218
218
 
219
- ### 주요 클래스
219
+ ### 모듈 및 가드 진입점
220
220
  - `PassportModule`: passport 전략 wiring을 위한 모듈 진입점입니다.
221
- - `AuthGuard`: 전략 체인을 실행하는 HTTP 가드입니다.
222
- - `CookieAuthModule`: 내장 cookie-auth 프리셋의 모듈 진입점입니다.
223
- - `CookieManager`: HttpOnly 인증 쿠키 관리를 위한 유틸리티입니다.
224
- - `RefreshTokenModule`: 내장 refresh-token 프리셋의 모듈 진입점입니다.
225
- - `JwtRefreshTokenAdapter`: `@fluojs/jwt`의 리프레시 로직을 패스포트 인터페이스로 연결합니다.
226
- - `createPassportJsStrategyBridge(...)`: Passport.js strategy를 fluo `AuthStrategy`로 변환합니다.
221
+ - `AuthGuard`: 전략 체인을 실행하고 required scope를 강제하는 HTTP 가드입니다.
222
+ - `PassportModuleOptions`, `AuthStrategyRegistration`, `AuthStrategyRegistry`, `AuthGuardContract`: Strategy registry guard wiring 계약입니다.
223
+
224
+ ### 전략 계약 에러
225
+ - `AuthStrategy`: 커스텀 인증 로직 구현을 위한 계약입니다.
226
+ - `AuthRequirement`: 선택된 strategy, optional-auth mode, required scope담는 route-level metadata입니다.
227
+ - `AuthStrategyResult`, `AuthOptionalResult`, `AuthHandledResult`: Principal, 의도적으로 누락된 credential, 이미 처리된 response를 표현하는 strategy return variant입니다.
228
+ - `AuthStrategyResolutionError`, `AuthenticationRequiredError`, `AuthenticationFailedError`, `AuthenticationExpiredError`: Registry miss, 누락된 credential, 잘못된 credential, 만료된 credential에 대해 guard와 strategy adapter가 사용하는 공개 error입니다.
229
+
230
+ ### Metadata 및 scope helper
231
+ - `defineAuthRequirement(...)`, `getOwnAuthRequirement(...)`, `getAuthRequirement(...)`: Custom decorator나 tooling을 `AuthGuard`와 통합할 때 auth requirement metadata를 읽고 쓰는 공개 helper입니다.
232
+ - Scope requirement는 일반적으로 `@RequireScopes(...)`로 작성합니다. 더 낮은 수준의 scope normalization helper는 internal로 남아 있으며 package root export의 일부가 아닙니다.
233
+
234
+ ### Cookie auth preset
235
+ - `CookieAuthModule`: 내장 cookie-auth preset의 모듈 진입점입니다.
236
+ - `CookieAuthStrategy`, `COOKIE_AUTH_STRATEGY_NAME`, `COOKIE_AUTH_OPTIONS`, `DEFAULT_COOKIE_AUTH_OPTIONS`: Cookie strategy wiring token과 기본값입니다.
237
+ - `CookieAuthOptions`, `CookieAuthPresetConfig`, `CookieManagerConfig`, `CookieOptions`, `SetCookieOptions`: Cookie strategy 및 response cookie 설정 타입입니다.
238
+ - `CookieManager`: HttpOnly access/refresh token cookie를 설정하고 제거하는 유틸리티입니다.
227
239
  - Cookie helper: `createCookieAuthPreset`, `createCookieAuthStrategyRegistration`, `createCookieManager`, `normalizeCookieAuthOptions`.
240
+
241
+ ### Refresh token preset
242
+ - `RefreshTokenModule`: 내장 refresh-token preset의 모듈 진입점입니다.
243
+ - `RefreshTokenStrategy`, `REFRESH_TOKEN_STRATEGY_NAME`, `REFRESH_TOKEN_SERVICE`: Refresh-token strategy 및 service alias wiring입니다.
244
+ - `RefreshTokenService`, `RefreshTokenInput`, `RefreshTokenAuthResult`: Application service contract와 exchange payload shape입니다.
245
+ - `JwtRefreshTokenAdapter`: `@fluojs/jwt`의 refresh logic을 passport interface로 연결합니다.
246
+ - `REFRESH_TOKEN_MODULE_OPTIONS`, `RefreshTokenModuleOptions`: 필수 `secret`과 `store` 계약을 포함하는 JWT 기반 refresh-token adapter 설정 token 및 option입니다.
228
247
  - Refresh helper: `createRefreshTokenStrategyRegistration`.
229
- - Status/diagnostics helper: `createPassportPlatformStatusSnapshot`, `createPassportPlatformDiagnosticIssues`.
230
248
 
231
- ### 인터페이스
232
- - `AuthStrategy`: 커스텀 인증 로직 구현을 위한 계약입니다.
233
- - `AccountLinkPolicy`: 계정 연결 결정 로직을 위한 확장 지점입니다.
249
+ ### Passport.js bridge
250
+ - `createPassportJsStrategyBridge(...)`: Passport.js strategy를 fluo `AuthStrategy`로 변환합니다.
251
+ - `PassportJsAuthStrategy`, `PassportJsStrategyLike`, `PassportJsPrincipalMapperInput`, `PassportJsPrincipalMapper`, `PassportJsAuthStrategyOptions`, `PassportJsStrategyBridge`: Bridge strategy, mapper, 설정, provider bundle 계약입니다.
252
+
253
+ ### Account linking
254
+ - `ACCOUNT_LINKING_POLICY`: Account-linking policy implementation을 등록하기 위한 DI token입니다.
255
+ - `createConservativeAccountLinkPolicy(...)`, `resolveAccountLinking(...)`: Identity-link 결정을 위한 conservative default policy와 resolver입니다.
256
+ - `AccountIdentity`, `AccountLinkCandidate`, `AccountLinkAttempt`, `AccountLinkContext`, `AccountLinkPolicy`, `AccountLinkPolicyDecision`, `AccountLinkingOptions`, `AccountLinkingResolution`: Account-linking input, policy, result 계약입니다.
257
+ - `AccountLinkConflictError`, `AccountLinkRejectedError`: 모호하거나 거부된 account-linking attempt에서 발생하는 error입니다.
258
+
259
+ ### Status 및 diagnostics
260
+ - `createPassportPlatformStatusSnapshot(...)`: Strategy registry, preset readiness, ownership, telemetry label을 위한 runtime platform snapshot을 생성합니다.
261
+ - `createPassportPlatformDiagnosticIssues(...)`: Empty registry, 누락된 default strategy, cookie preset readiness, refresh-token backing store readiness 문제에 대한 diagnostic issue를 생성합니다.
262
+ - `PassportPlatformStatusSnapshot`, `PassportStatusAdapterInput`: Status helper input/output 계약입니다.
234
263
 
235
264
  `UseOptionalAuth`는 scope가 필요 없는 route에서만 credential 누락을 우회합니다. Scoped route에는 여전히 principal이 필요합니다. Passport.js bridge의 `redirect()`는 response를 commit하고 protected handler를 건너뛰며, `pass()`와 Passport action 없이 완료된 strategy는 인증 실패입니다.
236
265
 
package/README.md CHANGED
@@ -106,7 +106,7 @@ const googleBridge = createPassportJsStrategyBridge('google', GoogleStrategy, {
106
106
  });
107
107
  ```
108
108
 
109
- 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 and promise completion without an action become authentication failures instead of leaving the request unresolved. Custom `mapPrincipal` functions must return a valid fluo `Principal` with a non-empty `subject` and object `claims`.
109
+ 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. Custom `mapPrincipal` functions must return a valid fluo `Principal` with a non-empty `subject` and object `claims`.
110
110
 
111
111
  ### Cookie Auth Preset
112
112
 
@@ -201,11 +201,11 @@ export class AuthController {
201
201
 
202
202
  Import `RefreshTokenModule.forRoot(...)` alongside `PassportModule.forRoot(...)` so the refresh-token strategy and shared `REFRESH_TOKEN_SERVICE` alias are available in the same module wiring.
203
203
 
204
- `RefreshTokenStrategy` reads tokens from `body.refreshToken`, `Authorization: Bearer ...`, or `x-refresh-token`; malformed non-string tokens fail authentication. `JwtRefreshTokenAdapter` requires a `secret` and a backing store; `store: 'memory'` is for development and single-instance deployments only.
204
+ `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.
205
205
 
206
206
  ### Account Linking and Status
207
207
 
208
- Use `createConservativeAccountLinkPolicy(...)` and `resolveAccountLinking(...)` to model identity-link decisions. The default conservative policy links explicit existing links or user-confirmed matches, and otherwise creates, skips, rejects, or reports conflicts deterministically.
208
+ Use `createConservativeAccountLinkPolicy(...)` and `resolveAccountLinking(...)` to model identity-link decisions. The default conservative policy links one unambiguous existing account link or user-confirmed matches, reports multiple existing links as conflicts, and otherwise creates, skips, rejects, or reports conflicts deterministically.
209
209
 
210
210
  `createPassportPlatformStatusSnapshot(...)` and `createPassportPlatformDiagnosticIssues(...)` expose readiness/health diagnostics for registered strategies, default strategy configuration, presets, and refresh-token store readiness.
211
211
 
@@ -216,21 +216,50 @@ Use `createConservativeAccountLinkPolicy(...)` and `resolveAccountLinking(...)`
216
216
  - `@UseOptionalAuth(strategyName)`: Attaches `AuthGuard` but allows routes without scopes to continue when the strategy reports missing credentials.
217
217
  - `@RequireScopes(...scopes)`: Enforces specific scope requirements.
218
218
 
219
- ### Core Classes
219
+ ### Module and Guard Entry Points
220
220
  - `PassportModule`: Module entry point for passport strategy wiring.
221
- - `AuthGuard`: The HTTP guard that executes the strategy chain.
221
+ - `AuthGuard`: The HTTP guard that executes the strategy chain and enforces required scopes.
222
+ - `PassportModuleOptions`, `AuthStrategyRegistration`, `AuthStrategyRegistry`, `AuthGuardContract`: Strategy registry and guard wiring contracts.
223
+
224
+ ### Strategy Contracts and Errors
225
+ - `AuthStrategy`: The contract for implementing custom authentication logic.
226
+ - `AuthRequirement`: Route-level metadata for the selected strategy, optional-auth mode, and required scopes.
227
+ - `AuthStrategyResult`, `AuthOptionalResult`, `AuthHandledResult`: Strategy return variants for principals, intentionally missing credentials, and fully handled responses.
228
+ - `AuthStrategyResolutionError`, `AuthenticationRequiredError`, `AuthenticationFailedError`, `AuthenticationExpiredError`: Public errors used by guards and strategy adapters for registry misses, missing credentials, invalid credentials, and expired credentials.
229
+
230
+ ### Metadata and Scope Helpers
231
+ - `defineAuthRequirement(...)`, `getOwnAuthRequirement(...)`, `getAuthRequirement(...)`: Public helpers for reading and writing auth requirement metadata when integrating custom decorators or tooling with `AuthGuard`.
232
+ - Scope requirements are normally authored with `@RequireScopes(...)`; lower-level scope normalization helpers remain internal and are not part of the package root export.
233
+
234
+ ### Cookie Auth Preset
222
235
  - `CookieAuthModule`: Module entry point for the built-in cookie-auth preset.
223
- - `CookieManager`: Utility for managing HttpOnly auth cookies.
236
+ - `CookieAuthStrategy`, `COOKIE_AUTH_STRATEGY_NAME`, `COOKIE_AUTH_OPTIONS`, `DEFAULT_COOKIE_AUTH_OPTIONS`: Cookie strategy wiring tokens and defaults.
237
+ - `CookieAuthOptions`, `CookieAuthPresetConfig`, `CookieManagerConfig`, `CookieOptions`, `SetCookieOptions`: Cookie strategy and response cookie configuration types.
238
+ - `CookieManager`: Utility for setting and clearing HttpOnly access/refresh token cookies.
239
+ - Cookie helpers: `createCookieAuthPreset`, `createCookieAuthStrategyRegistration`, `createCookieManager`, `normalizeCookieAuthOptions`.
240
+
241
+ ### Refresh Token Preset
224
242
  - `RefreshTokenModule`: Module entry point for the built-in refresh-token preset.
243
+ - `RefreshTokenStrategy`, `REFRESH_TOKEN_STRATEGY_NAME`, `REFRESH_TOKEN_SERVICE`: Refresh-token strategy and service alias wiring.
244
+ - `RefreshTokenService`, `RefreshTokenInput`, `RefreshTokenAuthResult`: Application service contract and exchange payload shapes.
225
245
  - `JwtRefreshTokenAdapter`: Bridges `@fluojs/jwt` refresh logic to the passport interface.
226
- - `createPassportJsStrategyBridge(...)`: Adapts Passport.js strategies to fluo `AuthStrategy`.
227
- - Cookie helpers: `createCookieAuthPreset`, `createCookieAuthStrategyRegistration`, `createCookieManager`, `normalizeCookieAuthOptions`.
246
+ - `REFRESH_TOKEN_MODULE_OPTIONS`, `RefreshTokenModuleOptions`: JWT-backed refresh-token adapter configuration token and options, including the required `secret` and `store` contract.
228
247
  - Refresh helpers: `createRefreshTokenStrategyRegistration`.
229
- - Status/diagnostics helpers: `createPassportPlatformStatusSnapshot`, `createPassportPlatformDiagnosticIssues`.
230
248
 
231
- ### Interfaces
232
- - `AuthStrategy`: The contract for implementing custom authentication logic.
233
- - `AccountLinkPolicy`: Extension point for identity-linking decisions.
249
+ ### Passport.js Bridge
250
+ - `createPassportJsStrategyBridge(...)`: Adapts Passport.js strategies to fluo `AuthStrategy`.
251
+ - `PassportJsAuthStrategy`, `PassportJsStrategyLike`, `PassportJsPrincipalMapperInput`, `PassportJsPrincipalMapper`, `PassportJsAuthStrategyOptions`, `PassportJsStrategyBridge`: Bridge strategy, mapper, configuration, and provider bundle contracts.
252
+
253
+ ### Account Linking
254
+ - `ACCOUNT_LINKING_POLICY`: DI token for registering an account-linking policy implementation.
255
+ - `createConservativeAccountLinkPolicy(...)`, `resolveAccountLinking(...)`: Conservative default policy and resolver for identity-link decisions.
256
+ - `AccountIdentity`, `AccountLinkCandidate`, `AccountLinkAttempt`, `AccountLinkContext`, `AccountLinkPolicy`, `AccountLinkPolicyDecision`, `AccountLinkingOptions`, `AccountLinkingResolution`: Account-linking input, policy, and result contracts.
257
+ - `AccountLinkConflictError`, `AccountLinkRejectedError`: Errors raised for ambiguous or rejected account-linking attempts.
258
+
259
+ ### Status and Diagnostics
260
+ - `createPassportPlatformStatusSnapshot(...)`: Creates a runtime platform snapshot for strategy registry, preset readiness, ownership, and telemetry labels.
261
+ - `createPassportPlatformDiagnosticIssues(...)`: Emits diagnostic issues for empty registries, missing default strategies, cookie preset readiness, and refresh-token backing store readiness.
262
+ - `PassportPlatformStatusSnapshot`, `PassportStatusAdapterInput`: Status helper input/output contracts.
234
263
 
235
264
  `UseOptionalAuth` only bypasses missing credentials when no scopes are required; scoped routes still need a principal. Passport.js bridge `redirect()` commits the response and skips the protected handler, while `pass()` and strategy completion without a Passport action are authentication failures.
236
265
 
@@ -1 +1 @@
1
- {"version":3,"file":"account-linking.d.ts","sourceRoot":"","sources":["../../src/account/account-linking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5D,yEAAyE;AACzE,eAAO,MAAM,sBAAsB,eAAqD,CAAC;AAEzF,iEAAiE;AACjE,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,oEAAoE;AACpE,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,eAAe,GAAG,aAAa,GAAG,gBAAgB,GAAG,MAAM,CAAC;CACrE;AAED,yEAAyE;AACzE,MAAM,WAAW,kBAAkB;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,sDAAsD;AACtD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,eAAe,CAAC;IAC1B,UAAU,EAAE,oBAAoB,EAAE,CAAC;IACnC,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAED,uEAAuE;AACvE,MAAM,MAAM,yBAAyB,GACjC;IACE,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,MAAM,EAAE,QAAQ,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GACD;IACE,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B,CAAC;AAEN,qDAAqD;AACrD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,GAAG,YAAY,CAAC,yBAAyB,CAAC,CAAC;CAChF;AAED,8DAA8D;AAC9D,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAAC;CACtC;AAED,iEAAiE;AACjE,MAAM,MAAM,wBAAwB,GAChC;IACE,MAAM,EAAE,QAAQ,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEN,6EAA6E;AAC7E,qBAAa,wBAAyB,SAAQ,SAAS;IACrD,QAAQ,CAAC,mBAAmB,EAAE,MAAM,EAAE,CAAC;gBAGrC,mBAAmB,EAAE,MAAM,EAAE,EAC7B,OAAO,SAAoE;CAW9E;AAED,0EAA0E;AAC1E,qBAAa,wBAAyB,SAAQ,SAAS;gBACzC,OAAO,SAA0C,EAAE,IAAI,SAA0B;CAG9F;AAED;;;;GAIG;AACH,wBAAgB,mCAAmC,IAAI,iBAAiB,CAsDvE;AAKD;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,kBAAkB,EAC3B,MAAM,CAAC,EAAE,iBAAiB,EAC1B,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,wBAAwB,CAAC,CA0CnC"}
1
+ {"version":3,"file":"account-linking.d.ts","sourceRoot":"","sources":["../../src/account/account-linking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5D,yEAAyE;AACzE,eAAO,MAAM,sBAAsB,eAAqD,CAAC;AAEzF,iEAAiE;AACjE,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,oEAAoE;AACpE,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,eAAe,GAAG,aAAa,GAAG,gBAAgB,GAAG,MAAM,CAAC;CACrE;AAED,yEAAyE;AACzE,MAAM,WAAW,kBAAkB;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,sDAAsD;AACtD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,eAAe,CAAC;IAC1B,UAAU,EAAE,oBAAoB,EAAE,CAAC;IACnC,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAED,uEAAuE;AACvE,MAAM,MAAM,yBAAyB,GACjC;IACE,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,MAAM,EAAE,QAAQ,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GACD;IACE,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B,CAAC;AAEN,qDAAqD;AACrD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,GAAG,YAAY,CAAC,yBAAyB,CAAC,CAAC;CAChF;AAED,8DAA8D;AAC9D,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAAC;CACtC;AAED,iEAAiE;AACjE,MAAM,MAAM,wBAAwB,GAChC;IACE,MAAM,EAAE,QAAQ,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEN,6EAA6E;AAC7E,qBAAa,wBAAyB,SAAQ,SAAS;IACrD,QAAQ,CAAC,mBAAmB,EAAE,MAAM,EAAE,CAAC;gBAGrC,mBAAmB,EAAE,MAAM,EAAE,EAC7B,OAAO,SAAoE;CAW9E;AAED,0EAA0E;AAC1E,qBAAa,wBAAyB,SAAQ,SAAS;gBACzC,OAAO,SAA0C,EAAE,IAAI,SAA0B;CAG9F;AAED;;;;GAIG;AACH,wBAAgB,mCAAmC,IAAI,iBAAiB,CAqEvE;AAKD;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,kBAAkB,EAC3B,MAAM,CAAC,EAAE,iBAAiB,EAC1B,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,wBAAwB,CAAC,CA0CnC"}
@@ -50,14 +50,21 @@ export class AccountLinkRejectedError extends FluoError {
50
50
  export function createConservativeAccountLinkPolicy() {
51
51
  return {
52
52
  evaluate(context) {
53
- const existingLink = context.candidates.find(candidate => candidate.reason === 'existing-link');
54
- if (existingLink) {
53
+ const existingLinkAccountIds = Array.from(new Set(context.candidates.filter(candidate => candidate.reason === 'existing-link').map(candidate => candidate.accountId)));
54
+ if (existingLinkAccountIds.length === 1) {
55
55
  return {
56
56
  action: 'link',
57
- accountId: existingLink.accountId,
57
+ accountId: existingLinkAccountIds[0],
58
58
  reason: 'Existing identity link found.'
59
59
  };
60
60
  }
61
+ if (existingLinkAccountIds.length > 1) {
62
+ return {
63
+ action: 'conflict',
64
+ candidateAccountIds: existingLinkAccountIds,
65
+ reason: 'Multiple existing identity links matched. Explicit review is required before linking.'
66
+ };
67
+ }
61
68
  const linkAttempt = context.linkAttempt;
62
69
  if (linkAttempt) {
63
70
  if (!linkAttempt.confirmedByUser) {
@@ -38,6 +38,8 @@ export type PassportJsPrincipalMapper = (input: PassportJsPrincipalMapperInput)
38
38
  export interface PassportJsAuthStrategyOptions {
39
39
  /** Optional options to pass to the Passport strategy's `authenticate` method. */
40
40
  authenticateOptions?: Readonly<Record<string, unknown>>;
41
+ /** Maximum time to wait for callback-style strategies to call a bound Passport action. */
42
+ actionTimeoutMs?: number;
41
43
  /** Optional custom mapper for converting user data to a principal. */
42
44
  mapPrincipal?: PassportJsPrincipalMapper;
43
45
  }
@@ -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;AAaD;;;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,sEAAsE;IACtE,YAAY,CAAC,EAAE,yBAAyB,CAAC;CAC1C;AAED;;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;IAyC3E,OAAO,CAAC,wBAAwB;IAmBhC,OAAO,CAAC,MAAM;IAgBd,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":"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"}
@@ -19,6 +19,8 @@ import { normalizePrincipalScopes } from '../scope.js';
19
19
  * Configuration options for the Passport.js authentication strategy.
20
20
  */
21
21
 
22
+ const DEFAULT_ACTION_TIMEOUT_MS = 30_000;
23
+
22
24
  /**
23
25
  * Bridge interface containing DI providers and strategy registration for Passport.js.
24
26
  */
@@ -112,7 +114,15 @@ export class PassportJsAuthStrategy {
112
114
  const strategy = this.createExecutableStrategy();
113
115
  const mapPrincipal = this.options.mapPrincipal ?? defaultPrincipalMapper;
114
116
  return new Promise((resolve, reject) => {
117
+ let actionTimeout;
118
+ const clearActionTimeout = () => {
119
+ if (actionTimeout) {
120
+ clearTimeout(actionTimeout);
121
+ actionTimeout = undefined;
122
+ }
123
+ };
115
124
  this.requestState.set(strategy, {
125
+ clearActionTimeout,
116
126
  context,
117
127
  mapPrincipal,
118
128
  reject,
@@ -121,20 +131,31 @@ export class PassportJsAuthStrategy {
121
131
  settled: false
122
132
  });
123
133
  this.bindStrategyActions(strategy);
134
+ const actionTimeoutMs = this.options.actionTimeoutMs ?? DEFAULT_ACTION_TIMEOUT_MS;
135
+ if (Number.isFinite(actionTimeoutMs) && actionTimeoutMs >= 0) {
136
+ actionTimeout = setTimeout(() => {
137
+ this.settle(strategy, state => {
138
+ state.reject(new AuthenticationRequiredError('Passport strategy did not settle authentication.'));
139
+ });
140
+ }, actionTimeoutMs);
141
+ }
124
142
  try {
125
143
  const result = strategy.authenticate(request, this.options.authenticateOptions);
126
144
  if (isPromiseLike(result)) {
127
145
  result.then(() => {
146
+ clearActionTimeout();
128
147
  this.settle(strategy, state => {
129
148
  state.reject(new AuthenticationRequiredError('Passport strategy did not settle authentication.'));
130
149
  });
131
150
  }, error => {
151
+ clearActionTimeout();
132
152
  this.settle(strategy, state => {
133
153
  state.reject(error);
134
154
  });
135
155
  });
136
156
  }
137
157
  } catch (error) {
158
+ clearActionTimeout();
138
159
  this.settle(strategy, () => reject(error));
139
160
  }
140
161
  });
@@ -161,6 +182,7 @@ export class PassportJsAuthStrategy {
161
182
  try {
162
183
  handler(state);
163
184
  } finally {
185
+ state.clearActionTimeout?.();
164
186
  this.requestState.delete(strategy);
165
187
  }
166
188
  }
@@ -74,7 +74,7 @@ class CookieAuthStrategy {
74
74
  return unauthenticatedCookieAuthResult;
75
75
  }
76
76
  const accessToken = cookies[this.options.accessTokenCookieName];
77
- if (accessToken === undefined || accessToken === '') {
77
+ if (accessToken === undefined) {
78
78
  if (this.options.requireAccessToken) {
79
79
  throw new AuthenticationRequiredError('Access token cookie is required.');
80
80
  }
@@ -105,11 +105,10 @@ class RefreshTokenStrategy {
105
105
  }
106
106
  async extractVerifiedSubject(accessToken) {
107
107
  const principal = await this.verifier.verifyAccessToken(accessToken);
108
- const sub = principal.claims.sub;
109
- if (typeof sub !== 'string' || sub.length === 0) {
108
+ if (typeof principal.subject !== 'string' || principal.subject.length === 0) {
110
109
  throw new InvariantError('Refresh token service returned an access token without a valid subject claim.');
111
110
  }
112
- return sub;
111
+ return principal.subject;
113
112
  }
114
113
  static {
115
114
  _initClass();
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "guard",
10
10
  "strategy"
11
11
  ],
12
- "version": "1.0.0",
12
+ "version": "1.0.2",
13
13
  "private": false,
14
14
  "license": "MIT",
15
15
  "repository": {
@@ -36,11 +36,11 @@
36
36
  "dist"
37
37
  ],
38
38
  "dependencies": {
39
- "@fluojs/core": "^1.0.0",
40
- "@fluojs/di": "^1.0.0",
39
+ "@fluojs/core": "^1.0.2",
40
+ "@fluojs/di": "^1.0.2",
41
41
  "@fluojs/http": "^1.0.0",
42
42
  "@fluojs/jwt": "^1.0.0",
43
- "@fluojs/runtime": "^1.0.0"
43
+ "@fluojs/runtime": "^1.1.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "vitest": "^3.2.4"