@fluojs/passport 1.0.0-beta.3 → 1.0.0-beta.5

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
@@ -114,9 +114,27 @@ export class AuthModule {}
114
114
 
115
115
  `CookieAuthStrategy`는 `@fluojs/jwt`가 정규화한 JWT principal 계약을 보존하며, `subject`, `claims`, `issuer`, `audience`, `roles`, `scopes`를 그대로 전달합니다.
116
116
 
117
+ 보호된 라우트는 계속 `@UseAuth(...)`를 사용해야 합니다. `requireAccessToken: false`를 설정해도 쿠키가 없을 때는 익명 principal이 아니라 명시적인 미인증 결과를 반환하므로, 보호된 라우트는 요청을 계속 거부합니다.
118
+
119
+ 로그인 사용자와 게스트 호출자를 모두 허용하려는 라우트에서만 `@UseOptionalAuth(...)`를 사용하세요.
120
+
121
+ ```typescript
122
+ import { Controller, Get, type RequestContext } from '@fluojs/http';
123
+ import { UseOptionalAuth } from '@fluojs/passport';
124
+
125
+ @Controller('/session')
126
+ export class SessionController {
127
+ @Get('/')
128
+ @UseOptionalAuth('cookie')
129
+ getSession(_input: never, ctx: RequestContext) {
130
+ return { subject: ctx.principal?.subject ?? null };
131
+ }
132
+ }
133
+ ```
134
+
117
135
  ### 리프레시 토큰 수명 주기
118
136
 
119
- 패키지에서 제공하는 `RefreshTokenStrategy`와 `RefreshTokenService`를 사용하여 안전한 토큰 로테이션 폐기 기능을 구현할 수 있습니다.
137
+ 패키지는 안전한 토큰 rotation과 revocation을 위해 built-in `RefreshTokenStrategy`, `RefreshTokenModule`, `RefreshTokenService` contract를 제공합니다.
120
138
 
121
139
  ```typescript
122
140
  import { Module } from '@fluojs/core';
@@ -153,10 +171,19 @@ export class AuthController {
153
171
 
154
172
  `RefreshTokenModule.forRoot(...)`를 `PassportModule.forRoot(...)`와 함께 import 하여 refresh-token 전략과 공유 `REFRESH_TOKEN_SERVICE` alias를 같은 모듈 wiring에서 사용하세요.
155
173
 
174
+ `RefreshTokenStrategy`는 `body.refreshToken`, `Authorization: Bearer ...`, `x-refresh-token`에서 token을 읽습니다. Malformed non-string token은 인증 실패로 처리됩니다. `JwtRefreshTokenAdapter`는 `secret`과 backing store가 필요하며, `store: 'memory'`는 development 및 single-instance deployment용입니다.
175
+
176
+ ### Account linking과 status
177
+
178
+ Identity-link 결정을 모델링하려면 `createConservativeAccountLinkPolicy(...)`와 `resolveAccountLinking(...)`을 사용합니다. 기본 conservative policy는 명시적인 existing link 또는 user-confirmed match만 연결하고, 그 외에는 create/skip/reject/conflict를 결정적으로 처리합니다.
179
+
180
+ `createPassportPlatformStatusSnapshot(...)`와 `createPassportPlatformDiagnosticIssues(...)`는 등록된 strategy, default strategy 설정, preset, refresh-token store readiness에 대한 readiness/health diagnostic을 노출합니다.
181
+
156
182
  ## 공개 API 개요
157
183
 
158
184
  ### 데코레이터
159
185
  - `@UseAuth(strategyName)`: `AuthGuard`를 부착하고 사용할 전략을 설정합니다.
186
+ - `@UseOptionalAuth(strategyName)`: `AuthGuard`를 부착하지만 전략이 자격 증명 누락을 보고하면 스코프가 없는 라우트는 계속 진행할 수 있게 합니다.
160
187
  - `@RequireScopes(...scopes)`: 특정 권한(스코프) 요구 사항을 강제합니다.
161
188
 
162
189
  ### 주요 클래스
@@ -166,11 +193,17 @@ export class AuthController {
166
193
  - `CookieManager`: HttpOnly 인증 쿠키 관리를 위한 유틸리티입니다.
167
194
  - `RefreshTokenModule`: 내장 refresh-token 프리셋의 모듈 진입점입니다.
168
195
  - `JwtRefreshTokenAdapter`: `@fluojs/jwt`의 리프레시 로직을 패스포트 인터페이스로 연결합니다.
196
+ - `createPassportJsStrategyBridge(...)`: Passport.js strategy를 fluo `AuthStrategy`로 변환합니다.
197
+ - Cookie helper: `createCookieAuthPreset`, `createCookieAuthStrategyRegistration`, `createCookieManager`, `normalizeCookieAuthOptions`.
198
+ - Refresh helper: `createRefreshTokenStrategyRegistration`.
199
+ - Status/diagnostics helper: `createPassportPlatformStatusSnapshot`, `createPassportPlatformDiagnosticIssues`.
169
200
 
170
201
  ### 인터페이스
171
202
  - `AuthStrategy`: 커스텀 인증 로직 구현을 위한 계약입니다.
172
203
  - `AccountLinkPolicy`: 계정 연결 결정 로직을 위한 확장 지점입니다.
173
204
 
205
+ `UseOptionalAuth`는 scope가 필요 없는 route에서만 credential 누락을 우회합니다. Scoped route에는 여전히 principal이 필요합니다. Passport.js bridge의 `redirect()`는 response를 commit하고 protected handler를 건너뛰며, `pass()`와 Passport action 없이 완료된 strategy는 인증 실패입니다.
206
+
174
207
  ## 관련 패키지
175
208
 
176
209
  - `@fluojs/jwt`: JWT 기반 전략을 위한 하위 토큰 코어 패키지입니다.
package/README.md CHANGED
@@ -114,9 +114,27 @@ Import `CookieAuthModule.forRoot(...)` alongside `PassportModule.forRoot(...)` w
114
114
 
115
115
  `CookieAuthStrategy` preserves the normalized JWT principal contract from `@fluojs/jwt`, including `subject`, `claims`, `issuer`, `audience`, `roles`, and `scopes`.
116
116
 
117
+ 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.
118
+
119
+ Use `@UseOptionalAuth(...)` only on routes that intentionally support both signed-in and guest callers:
120
+
121
+ ```typescript
122
+ import { Controller, Get, type RequestContext } from '@fluojs/http';
123
+ import { UseOptionalAuth } from '@fluojs/passport';
124
+
125
+ @Controller('/session')
126
+ export class SessionController {
127
+ @Get('/')
128
+ @UseOptionalAuth('cookie')
129
+ getSession(_input: never, ctx: RequestContext) {
130
+ return { subject: ctx.principal?.subject ?? null };
131
+ }
132
+ }
133
+ ```
134
+
117
135
  ### Refresh Token Lifecycle
118
136
 
119
- The package provides a built-in `RefreshTokenStrategy` and `RefreshTokenService` to handle secure token rotation and revocation.
137
+ The package provides a built-in `RefreshTokenStrategy` plus the `RefreshTokenModule` and `RefreshTokenService` contract for secure token rotation and revocation.
120
138
 
121
139
  ```typescript
122
140
  import { Module } from '@fluojs/core';
@@ -153,10 +171,19 @@ export class AuthController {
153
171
 
154
172
  Import `RefreshTokenModule.forRoot(...)` alongside `PassportModule.forRoot(...)` so the refresh-token strategy and shared `REFRESH_TOKEN_SERVICE` alias are available in the same module wiring.
155
173
 
174
+ `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.
175
+
176
+ ### Account Linking and Status
177
+
178
+ 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.
179
+
180
+ `createPassportPlatformStatusSnapshot(...)` and `createPassportPlatformDiagnosticIssues(...)` expose readiness/health diagnostics for registered strategies, default strategy configuration, presets, and refresh-token store readiness.
181
+
156
182
  ## Public API Overview
157
183
 
158
184
  ### Decorators
159
185
  - `@UseAuth(strategyName)`: Attaches `AuthGuard` and sets the active strategy.
186
+ - `@UseOptionalAuth(strategyName)`: Attaches `AuthGuard` but allows routes without scopes to continue when the strategy reports missing credentials.
160
187
  - `@RequireScopes(...scopes)`: Enforces specific scope requirements.
161
188
 
162
189
  ### Core Classes
@@ -166,11 +193,17 @@ Import `RefreshTokenModule.forRoot(...)` alongside `PassportModule.forRoot(...)`
166
193
  - `CookieManager`: Utility for managing HttpOnly auth cookies.
167
194
  - `RefreshTokenModule`: Module entry point for the built-in refresh-token preset.
168
195
  - `JwtRefreshTokenAdapter`: Bridges `@fluojs/jwt` refresh logic to the passport interface.
196
+ - `createPassportJsStrategyBridge(...)`: Adapts Passport.js strategies to fluo `AuthStrategy`.
197
+ - Cookie helpers: `createCookieAuthPreset`, `createCookieAuthStrategyRegistration`, `createCookieManager`, `normalizeCookieAuthOptions`.
198
+ - Refresh helpers: `createRefreshTokenStrategyRegistration`.
199
+ - Status/diagnostics helpers: `createPassportPlatformStatusSnapshot`, `createPassportPlatformDiagnosticIssues`.
169
200
 
170
201
  ### Interfaces
171
202
  - `AuthStrategy`: The contract for implementing custom authentication logic.
172
203
  - `AccountLinkPolicy`: Extension point for identity-linking decisions.
173
204
 
205
+ `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.
206
+
174
207
  ## Related Packages
175
208
 
176
209
  - `@fluojs/jwt`: The underlying token core for JWT-based strategies.
@@ -6,7 +6,7 @@ import type { AuthStrategy, AuthStrategyResult } from '../types.js';
6
6
  */
7
7
  export declare const COOKIE_AUTH_OPTIONS: unique symbol;
8
8
  /**
9
- * Configures cookie names and fallback behavior for cookie-based authentication.
9
+ * Configures cookie names and missing-cookie behavior for cookie-based authentication.
10
10
  */
11
11
  export interface CookieAuthOptions {
12
12
  accessTokenCookieName?: string;
@@ -26,6 +26,11 @@ export declare const DEFAULT_COOKIE_AUTH_OPTIONS: Required<CookieAuthOptions>;
26
26
  export declare function normalizeCookieAuthOptions(options?: CookieAuthOptions): Required<CookieAuthOptions>;
27
27
  /**
28
28
  * Authenticates requests by reading and verifying JWTs from HTTP cookies.
29
+ *
30
+ * @remarks
31
+ * When `requireAccessToken` is `false`, missing cookies now resolve to an explicit
32
+ * unauthenticated result. Protected routes still reject that result unless they opt in
33
+ * with `@UseOptionalAuth(...)`.
29
34
  */
30
35
  export declare class CookieAuthStrategy implements AuthStrategy {
31
36
  private readonly verifier;
@@ -1 +1 @@
1
- {"version":3,"file":"cookie-auth.d.ts","sourceRoot":"","sources":["../../src/cookie/cookie-auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjD,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEpE;;GAEG;AACH,eAAO,MAAM,mBAAmB,eAAkD,CAAC;AAEnF;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;GAEG;AACH,eAAO,MAAM,2BAA2B,EAAE,QAAQ,CAAC,iBAAiB,CAInE,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,QAAQ,CAAC,iBAAiB,CAAC,CAMnG;AAED;;GAEG;AACH,qBACa,kBAAmB,YAAW,YAAY;IAInD,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAH3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;gBAGnC,QAAQ,EAAE,kBAAkB,EAC7C,OAAO,CAAC,EAAE,iBAAiB;IAKvB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,kBAAkB,CAAC;CA+CvE;AAED;;GAEG;AACH,eAAO,MAAM,yBAAyB,WAAW,CAAC"}
1
+ {"version":3,"file":"cookie-auth.d.ts","sourceRoot":"","sources":["../../src/cookie/cookie-auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjD,OAAO,KAAK,EAAsB,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAMxF;;GAEG;AACH,eAAO,MAAM,mBAAmB,eAAkD,CAAC;AAEnF;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;GAEG;AACH,eAAO,MAAM,2BAA2B,EAAE,QAAQ,CAAC,iBAAiB,CAInE,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,QAAQ,CAAC,iBAAiB,CAAC,CAMnG;AAED;;;;;;;GAOG;AACH,qBACa,kBAAmB,YAAW,YAAY;IAInD,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAH3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;gBAGnC,QAAQ,EAAE,kBAAkB,EAC7C,OAAO,CAAC,EAAE,iBAAiB;IAKvB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAyCvE;AAED;;GAEG;AACH,eAAO,MAAM,yBAAyB,WAAW,CAAC"}
@@ -7,13 +7,17 @@ function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side
7
7
  import { Inject } from '@fluojs/core';
8
8
  import { DefaultJwtVerifier } from '@fluojs/jwt';
9
9
  import { AuthenticationRequiredError } from '../errors.js';
10
+ const unauthenticatedCookieAuthResult = {
11
+ authenticated: false
12
+ };
13
+
10
14
  /**
11
15
  * Provides cookie-auth strategy options through dependency injection.
12
16
  */
13
17
  export const COOKIE_AUTH_OPTIONS = Symbol.for('fluo.passport.cookie-auth-options');
14
18
 
15
19
  /**
16
- * Configures cookie names and fallback behavior for cookie-based authentication.
20
+ * Configures cookie names and missing-cookie behavior for cookie-based authentication.
17
21
  */
18
22
 
19
23
  /**
@@ -41,6 +45,11 @@ export function normalizeCookieAuthOptions(options) {
41
45
 
42
46
  /**
43
47
  * Authenticates requests by reading and verifying JWTs from HTTP cookies.
48
+ *
49
+ * @remarks
50
+ * When `requireAccessToken` is `false`, missing cookies now resolve to an explicit
51
+ * unauthenticated result. Protected routes still reject that result unless they opt in
52
+ * with `@UseOptionalAuth(...)`.
44
53
  */
45
54
  let _CookieAuthStrategy;
46
55
  class CookieAuthStrategy {
@@ -59,20 +68,14 @@ class CookieAuthStrategy {
59
68
  if (this.options.requireAccessToken) {
60
69
  throw new AuthenticationRequiredError('Access token cookie is required.');
61
70
  }
62
- return {
63
- claims: {},
64
- subject: 'anonymous'
65
- };
71
+ return unauthenticatedCookieAuthResult;
66
72
  }
67
73
  const accessToken = cookies[this.options.accessTokenCookieName];
68
74
  if (!accessToken) {
69
75
  if (this.options.requireAccessToken) {
70
76
  throw new AuthenticationRequiredError('Access token cookie is required.');
71
77
  }
72
- return {
73
- claims: {},
74
- subject: 'anonymous'
75
- };
78
+ return unauthenticatedCookieAuthResult;
76
79
  }
77
80
  try {
78
81
  const principal = await this.verifier.verifyAccessToken(accessToken);
@@ -1,5 +1,8 @@
1
1
  import type { FrameworkResponse } from '@fluojs/http';
2
2
  import { type CookieAuthOptions } from './cookie-auth.js';
3
+ /**
4
+ * Describes the cookie options contract.
5
+ */
3
6
  export interface CookieOptions {
4
7
  httpOnly?: boolean;
5
8
  secure?: boolean;
@@ -8,15 +11,27 @@ export interface CookieOptions {
8
11
  domain?: string;
9
12
  maxAge?: number;
10
13
  }
14
+ /**
15
+ * Describes the set cookie options contract.
16
+ */
11
17
  export interface SetCookieOptions extends CookieOptions {
12
18
  accessTokenTtlSeconds?: number;
13
19
  refreshTokenTtlSeconds?: number;
14
20
  }
21
+ /**
22
+ * Describes the cookie manager config contract.
23
+ */
15
24
  export interface CookieManagerConfig extends CookieAuthOptions {
16
25
  cookieOptions?: CookieOptions;
17
26
  }
18
27
  type NormalizedCookieOptions = Omit<Required<CookieOptions>, 'domain' | 'maxAge'> & Pick<CookieOptions, 'domain' | 'maxAge'>;
28
+ /**
29
+ * Provides the default cookie options value.
30
+ */
19
31
  export declare const DEFAULT_COOKIE_OPTIONS: NormalizedCookieOptions;
32
+ /**
33
+ * Represents the cookie manager.
34
+ */
20
35
  export declare class CookieManager {
21
36
  private readonly options;
22
37
  private readonly cookieOptions;
@@ -29,6 +44,12 @@ export declare class CookieManager {
29
44
  setAuthCookies(response: FrameworkResponse, accessToken: string, accessTokenTtlSeconds?: number, refreshToken?: string, refreshTokenTtlSeconds?: number): void;
30
45
  private appendSetCookie;
31
46
  }
47
+ /**
48
+ * Create cookie manager.
49
+ *
50
+ * @param config The config.
51
+ * @returns The create cookie manager result.
52
+ */
32
53
  export declare function createCookieManager(config?: CookieManagerConfig): CookieManager;
33
54
  export {};
34
55
  //# sourceMappingURL=cookie-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cookie-manager.d.ts","sourceRoot":"","sources":["../../src/cookie/cookie-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,EAA+B,KAAK,iBAAiB,EAA8B,MAAM,kBAAkB,CAAC;AAEnH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACrD,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC5D,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,KAAK,uBAAuB,GAAG,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC,GAC/E,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,QAAQ,CAAC,CAAC;AAE3C,eAAO,MAAM,sBAAsB,EAAE,uBAOpC,CAAC;AAuCF,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IACtD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA0B;gBAE5C,MAAM,CAAC,EAAE,mBAAmB;IAYxC,oBAAoB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI;IAa3F,qBAAqB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI;IAa5F,sBAAsB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;IASzD,uBAAuB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;IAS1D,eAAe,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;IAKlD,cAAc,CACZ,QAAQ,EAAE,iBAAiB,EAC3B,WAAW,EAAE,MAAM,EACnB,qBAAqB,CAAC,EAAE,MAAM,EAC9B,YAAY,CAAC,EAAE,MAAM,EACrB,sBAAsB,CAAC,EAAE,MAAM,GAC9B,IAAI;IAQP,OAAO,CAAC,eAAe;CAWxB;AAED,wBAAgB,mBAAmB,CAAC,MAAM,CAAC,EAAE,mBAAmB,GAAG,aAAa,CAE/E"}
1
+ {"version":3,"file":"cookie-manager.d.ts","sourceRoot":"","sources":["../../src/cookie/cookie-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,EAA+B,KAAK,iBAAiB,EAA8B,MAAM,kBAAkB,CAAC;AAEnH;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACrD,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC5D,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,KAAK,uBAAuB,GAAG,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC,GAC/E,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,QAAQ,CAAC,CAAC;AAE3C;;GAEG;AACH,eAAO,MAAM,sBAAsB,EAAE,uBAOpC,CAAC;AAuCF;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IACtD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA0B;gBAE5C,MAAM,CAAC,EAAE,mBAAmB;IAYxC,oBAAoB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI;IAa3F,qBAAqB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI;IAa5F,sBAAsB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;IASzD,uBAAuB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;IAS1D,eAAe,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;IAKlD,cAAc,CACZ,QAAQ,EAAE,iBAAiB,EAC3B,WAAW,EAAE,MAAM,EACnB,qBAAqB,CAAC,EAAE,MAAM,EAC9B,YAAY,CAAC,EAAE,MAAM,EACrB,sBAAsB,CAAC,EAAE,MAAM,GAC9B,IAAI;IAQP,OAAO,CAAC,eAAe;CAWxB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,CAAC,EAAE,mBAAmB,GAAG,aAAa,CAE/E"}
@@ -1,4 +1,20 @@
1
1
  import { normalizeCookieAuthOptions } from './cookie-auth.js';
2
+
3
+ /**
4
+ * Describes the cookie options contract.
5
+ */
6
+
7
+ /**
8
+ * Describes the set cookie options contract.
9
+ */
10
+
11
+ /**
12
+ * Describes the cookie manager config contract.
13
+ */
14
+
15
+ /**
16
+ * Provides the default cookie options value.
17
+ */
2
18
  export const DEFAULT_COOKIE_OPTIONS = {
3
19
  httpOnly: true,
4
20
  secure: true,
@@ -35,6 +51,10 @@ function buildClearCookieHeader(name, options) {
35
51
  maxAge: 0
36
52
  });
37
53
  }
54
+
55
+ /**
56
+ * Represents the cookie manager.
57
+ */
38
58
  export class CookieManager {
39
59
  options;
40
60
  cookieOptions;
@@ -88,6 +108,13 @@ export class CookieManager {
88
108
  response.setHeader('Set-Cookie', cookies.length === 1 ? cookies[0] : cookies);
89
109
  }
90
110
  }
111
+
112
+ /**
113
+ * Create cookie manager.
114
+ *
115
+ * @param config The config.
116
+ * @returns The create cookie manager result.
117
+ */
91
118
  export function createCookieManager(config) {
92
119
  return new CookieManager(config);
93
120
  }
@@ -19,6 +19,26 @@ type ClassOrMethodDecoratorLike = StandardClassDecoratorFn & StandardMethodDecor
19
19
  * @returns A class-or-method decorator that stores the auth requirement metadata.
20
20
  */
21
21
  export declare function UseAuth(strategy: string): ClassOrMethodDecoratorLike;
22
+ /**
23
+ * Declares a strategy that may leave `requestContext.principal` unset when credentials are absent.
24
+ *
25
+ * @remarks
26
+ * Use this decorator only for routes that intentionally accept both authenticated and
27
+ * unauthenticated callers. Scope requirements still require a resolved principal.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * @UseOptionalAuth('cookie')
32
+ * @Get('/session')
33
+ * getSession(_input: unknown, ctx: RequestContext) {
34
+ * return { subject: ctx.principal?.subject ?? null };
35
+ * }
36
+ * ```
37
+ *
38
+ * @param strategy Strategy name previously registered through `PassportModule.forRoot(...)`.
39
+ * @returns A class-or-method decorator that stores the optional auth requirement metadata.
40
+ */
41
+ export declare function UseOptionalAuth(strategy: string): ClassOrMethodDecoratorLike;
22
42
  /**
23
43
  * Declares scope requirements that `AuthGuard` must enforce after authentication succeeds.
24
44
  *
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAQA,KAAK,wBAAwB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;AAC1F,KAAK,yBAAyB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,2BAA2B,KAAK,IAAI,CAAC;AACjG,KAAK,0BAA0B,GAAG,wBAAwB,GAAG,yBAAyB,CAAC;AAuEvF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,0BAA0B,CAEpE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,0BAA0B,CAE7E"}
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AASA,KAAK,wBAAwB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;AAC1F,KAAK,yBAAyB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,2BAA2B,KAAK,IAAI,CAAC;AACjG,KAAK,0BAA0B,GAAG,wBAAwB,GAAG,yBAAyB,CAAC;AAyEvF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,0BAA0B,CAEpE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,0BAA0B,CAE5E;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,0BAA0B,CAE7E"}
@@ -1,9 +1,11 @@
1
+ import { ensureMetadataSymbol } from '@fluojs/core/internal';
1
2
  import { UseGuards } from '@fluojs/http';
2
3
  import { AuthGuard } from './guard.js';
3
4
  import { getOwnAuthRequirement } from './metadata.js';
4
5
  import { mergeAuthRequirements } from './scope.js';
5
6
  const standardClassRequirementKey = Symbol.for('fluo.passport.standard.class-auth');
6
7
  const standardMethodRequirementKey = Symbol.for('fluo.passport.standard.method-auth');
8
+ ensureMetadataSymbol();
7
9
  function isStandardClassContext(context) {
8
10
  return typeof context === 'object' && context !== null && 'kind' in context && context.kind === 'class';
9
11
  }
@@ -70,6 +72,32 @@ export function UseAuth(strategy) {
70
72
  });
71
73
  }
72
74
 
75
+ /**
76
+ * Declares a strategy that may leave `requestContext.principal` unset when credentials are absent.
77
+ *
78
+ * @remarks
79
+ * Use this decorator only for routes that intentionally accept both authenticated and
80
+ * unauthenticated callers. Scope requirements still require a resolved principal.
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * @UseOptionalAuth('cookie')
85
+ * @Get('/session')
86
+ * getSession(_input: unknown, ctx: RequestContext) {
87
+ * return { subject: ctx.principal?.subject ?? null };
88
+ * }
89
+ * ```
90
+ *
91
+ * @param strategy Strategy name previously registered through `PassportModule.forRoot(...)`.
92
+ * @returns A class-or-method decorator that stores the optional auth requirement metadata.
93
+ */
94
+ export function UseOptionalAuth(strategy) {
95
+ return createAuthRequirementDecorator({
96
+ optional: true,
97
+ strategy
98
+ });
99
+ }
100
+
73
101
  /**
74
102
  * Declares scope requirements that `AuthGuard` must enforce after authentication succeeds.
75
103
  *
@@ -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,EAIjB,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AA8EpB;;;;;;;;;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;CA8DxD"}
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;CAkExD"}
package/dist/guard.js CHANGED
@@ -13,10 +13,16 @@ import { getAuthRequirement } from './metadata.js';
13
13
  function isAuthHandledResult(result) {
14
14
  return typeof result === 'object' && result !== null && 'handled' in result && result.handled === true;
15
15
  }
16
+ function isAuthOptionalResult(result) {
17
+ return typeof result === 'object' && result !== null && 'authenticated' in result && result.authenticated === false;
18
+ }
16
19
  function resolvePrincipal(result) {
17
20
  if (isAuthHandledResult(result)) {
18
21
  return result.principal;
19
22
  }
23
+ if (isAuthOptionalResult(result)) {
24
+ return undefined;
25
+ }
20
26
  return result;
21
27
  }
22
28
  function isRecord(value) {
@@ -131,6 +137,9 @@ class AuthGuard {
131
137
  return true;
132
138
  }
133
139
  if (!principal) {
140
+ if (isAuthOptionalResult(result) && requirement?.optional && !requirement.scopes?.length) {
141
+ return true;
142
+ }
134
143
  throw new AuthenticationFailedError('Authentication strategy did not return a principal.');
135
144
  }
136
145
  if (!isPrincipal(principal)) {
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Provides the passport options value.
3
+ */
1
4
  export declare const PASSPORT_OPTIONS: unique symbol;
5
+ /**
6
+ * Provides the auth strategy registry value.
7
+ */
2
8
  export declare const AUTH_STRATEGY_REGISTRY: unique symbol;
3
9
  //# sourceMappingURL=internal-tokens.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal-tokens.d.ts","sourceRoot":"","sources":["../src/internal-tokens.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,gBAAgB,eAAmC,CAAC;AACjE,eAAO,MAAM,sBAAsB,eAAyC,CAAC"}
1
+ {"version":3,"file":"internal-tokens.d.ts","sourceRoot":"","sources":["../src/internal-tokens.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,eAAO,MAAM,gBAAgB,eAAmC,CAAC;AACjE;;GAEG;AACH,eAAO,MAAM,sBAAsB,eAAyC,CAAC"}
@@ -1,4 +1,11 @@
1
1
  const PASSPORT_OPTIONS_KEY = 'fluo.passport.options';
2
2
  const AUTH_STRATEGY_REGISTRY_KEY = 'fluo.passport.strategy-registry';
3
+
4
+ /**
5
+ * Provides the passport options value.
6
+ */
3
7
  export const PASSPORT_OPTIONS = Symbol.for(PASSPORT_OPTIONS_KEY);
8
+ /**
9
+ * Provides the auth strategy registry value.
10
+ */
4
11
  export const AUTH_STRATEGY_REGISTRY = Symbol.for(AUTH_STRATEGY_REGISTRY_KEY);
@@ -1,6 +1,27 @@
1
1
  import { type MetadataPropertyKey } from '@fluojs/core';
2
2
  import type { AuthRequirement } from './types.js';
3
+ /**
4
+ * Define auth requirement.
5
+ *
6
+ * @param target The target.
7
+ * @param requirement The requirement.
8
+ * @param propertyKey The property key.
9
+ */
3
10
  export declare function defineAuthRequirement(target: Function | object, requirement: AuthRequirement, propertyKey?: MetadataPropertyKey): void;
11
+ /**
12
+ * Get own auth requirement.
13
+ *
14
+ * @param target The target.
15
+ * @param propertyKey The property key.
16
+ * @returns The get own auth requirement result.
17
+ */
4
18
  export declare function getOwnAuthRequirement(target: Function | object, propertyKey?: MetadataPropertyKey): AuthRequirement | undefined;
19
+ /**
20
+ * Get auth requirement.
21
+ *
22
+ * @param controllerType The controller type.
23
+ * @param propertyKey The property key.
24
+ * @returns The get auth requirement result.
25
+ */
5
26
  export declare function getAuthRequirement(controllerType: Function, propertyKey?: MetadataPropertyKey): AuthRequirement | undefined;
6
27
  //# sourceMappingURL=metadata.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAIxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAiElD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAkCtI;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,EAAE,WAAW,CAAC,EAAE,mBAAmB,GAAG,eAAe,GAAG,SAAS,CAM/H;AAED,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,QAAQ,EAAE,WAAW,CAAC,EAAE,mBAAmB,GAAG,eAAe,GAAG,SAAS,CA6B3H"}
1
+ {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAIxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAmElD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAkCtI;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,EAAE,WAAW,CAAC,EAAE,mBAAmB,GAAG,eAAe,GAAG,SAAS,CAM/H;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,QAAQ,EAAE,WAAW,CAAC,EAAE,mBAAmB,GAAG,eAAe,GAAG,SAAS,CA6B3H"}
package/dist/metadata.js CHANGED
@@ -11,11 +11,13 @@ function normalizeRequirement(requirement) {
11
11
  return undefined;
12
12
  }
13
13
  const strategy = requirement.strategy;
14
+ const optional = requirement.optional;
14
15
  const scopes = normalizeDeclaredScopes(requirement.scopes);
15
- if (!strategy && !scopes) {
16
+ if (!strategy && !scopes && optional !== true) {
16
17
  return undefined;
17
18
  }
18
19
  return {
20
+ optional,
19
21
  scopes,
20
22
  strategy
21
23
  };
@@ -45,6 +47,14 @@ function getStandardMethodRequirement(target, propertyKey) {
45
47
  const map = getStandardConstructorMetadataBag(target)?.[standardMethodRequirementKey];
46
48
  return normalizeRequirement(map?.get(propertyKey));
47
49
  }
50
+
51
+ /**
52
+ * Define auth requirement.
53
+ *
54
+ * @param target The target.
55
+ * @param requirement The requirement.
56
+ * @param propertyKey The property key.
57
+ */
48
58
  export function defineAuthRequirement(target, requirement, propertyKey) {
49
59
  const normalizedRequirement = normalizeRequirement(requirement);
50
60
  if (propertyKey === undefined) {
@@ -72,12 +82,28 @@ export function defineAuthRequirement(target, requirement, propertyKey) {
72
82
  invalidateRequirementCache(controllerType, propertyKey);
73
83
  }
74
84
  }
85
+
86
+ /**
87
+ * Get own auth requirement.
88
+ *
89
+ * @param target The target.
90
+ * @param propertyKey The property key.
91
+ * @returns The get own auth requirement result.
92
+ */
75
93
  export function getOwnAuthRequirement(target, propertyKey) {
76
94
  if (propertyKey === undefined) {
77
95
  return mergeAuthRequirements(classRequirementStore.get(target), getStandardClassRequirement(target));
78
96
  }
79
97
  return mergeAuthRequirements(methodRequirementStore.get(target)?.get(propertyKey), getStandardMethodRequirement(target, propertyKey));
80
98
  }
99
+
100
+ /**
101
+ * Get auth requirement.
102
+ *
103
+ * @param controllerType The controller type.
104
+ * @param propertyKey The property key.
105
+ * @returns The get auth requirement result.
106
+ */
81
107
  export function getAuthRequirement(controllerType, propertyKey) {
82
108
  if (propertyKey === undefined) {
83
109
  if (mergedClassRequirementCache.has(controllerType)) {
package/dist/scope.d.ts CHANGED
@@ -1,5 +1,24 @@
1
1
  import type { AuthRequirement } from './types.js';
2
+ /**
3
+ * Normalize declared scopes.
4
+ *
5
+ * @param scopes The scopes.
6
+ * @returns The normalize declared scopes result.
7
+ */
2
8
  export declare function normalizeDeclaredScopes(scopes: unknown): string[] | undefined;
9
+ /**
10
+ * Normalize principal scopes.
11
+ *
12
+ * @param claims The claims.
13
+ * @returns The normalize principal scopes result.
14
+ */
3
15
  export declare function normalizePrincipalScopes(claims: Record<string, unknown>): string[] | undefined;
16
+ /**
17
+ * Merge auth requirements.
18
+ *
19
+ * @param base The base.
20
+ * @param extra The extra.
21
+ * @returns The merge auth requirements result.
22
+ */
4
23
  export declare function mergeAuthRequirements(base: AuthRequirement | undefined, extra: AuthRequirement | undefined): AuthRequirement | undefined;
5
24
  //# sourceMappingURL=scope.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAoBlD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,SAAS,CAO7E;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,GAAG,SAAS,CAW9F;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,eAAe,GAAG,SAAS,EACjC,KAAK,EAAE,eAAe,GAAG,SAAS,GACjC,eAAe,GAAG,SAAS,CAgB7B"}
1
+ {"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAoBlD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,SAAS,CAO7E;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,GAAG,SAAS,CAW9F;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,eAAe,GAAG,SAAS,EACjC,KAAK,EAAE,eAAe,GAAG,SAAS,GACjC,eAAe,GAAG,SAAS,CAuB7B"}
package/dist/scope.js CHANGED
@@ -11,6 +11,13 @@ function normalizeScopeItems(items) {
11
11
  }
12
12
  return normalized.length > 0 ? normalized : undefined;
13
13
  }
14
+
15
+ /**
16
+ * Normalize declared scopes.
17
+ *
18
+ * @param scopes The scopes.
19
+ * @returns The normalize declared scopes result.
20
+ */
14
21
  export function normalizeDeclaredScopes(scopes) {
15
22
  if (!Array.isArray(scopes)) {
16
23
  return undefined;
@@ -18,6 +25,13 @@ export function normalizeDeclaredScopes(scopes) {
18
25
  const scopeItems = scopes.filter(scope => typeof scope === 'string');
19
26
  return normalizeScopeItems(scopeItems);
20
27
  }
28
+
29
+ /**
30
+ * Normalize principal scopes.
31
+ *
32
+ * @param claims The claims.
33
+ * @returns The normalize principal scopes result.
34
+ */
21
35
  export function normalizePrincipalScopes(claims) {
22
36
  if (Array.isArray(claims.scopes)) {
23
37
  const scopes = claims.scopes.filter(scope => typeof scope === 'string');
@@ -28,16 +42,26 @@ export function normalizePrincipalScopes(claims) {
28
42
  }
29
43
  return undefined;
30
44
  }
45
+
46
+ /**
47
+ * Merge auth requirements.
48
+ *
49
+ * @param base The base.
50
+ * @param extra The extra.
51
+ * @returns The merge auth requirements result.
52
+ */
31
53
  export function mergeAuthRequirements(base, extra) {
32
54
  if (!base && !extra) {
33
55
  return undefined;
34
56
  }
35
57
  const scopes = normalizeDeclaredScopes([...(base?.scopes ?? []), ...(extra?.scopes ?? [])]);
58
+ const optional = extra && Object.hasOwn(extra, 'optional') ? extra.optional : base?.optional === true && extra?.strategy ? false : base?.optional;
36
59
  const strategy = extra?.strategy ?? base?.strategy;
37
- if (!strategy && !scopes) {
60
+ if (!strategy && !scopes && optional !== true) {
38
61
  return undefined;
39
62
  }
40
63
  return {
64
+ optional,
41
65
  scopes,
42
66
  strategy
43
67
  };
package/dist/types.d.ts CHANGED
@@ -4,16 +4,22 @@ import type { Guard, GuardContext, Principal } from '@fluojs/http';
4
4
  export interface AuthRequirement {
5
5
  /** Named strategy to resolve from the strategy registry. */
6
6
  strategy?: string;
7
+ /** Allows the request to continue without a resolved principal. */
8
+ optional?: boolean;
7
9
  /** Required scopes that must be present on the resolved principal. */
8
10
  scopes?: string[];
9
11
  }
12
+ /** Authentication result variant used when a route explicitly allows missing credentials. */
13
+ export interface AuthOptionalResult {
14
+ authenticated: false;
15
+ }
10
16
  /** Authentication result variant used when a strategy fully handled the response. */
11
17
  export interface AuthHandledResult {
12
18
  handled: true;
13
19
  principal?: Principal;
14
20
  }
15
21
  /** Return type of an `AuthStrategy.authenticate(...)` call. */
16
- export type AuthStrategyResult = Principal | AuthHandledResult;
22
+ export type AuthStrategyResult = Principal | AuthHandledResult | AuthOptionalResult;
17
23
  /** Strategy contract implemented by authentication adapters. */
18
24
  export interface AuthStrategy {
19
25
  authenticate(context: GuardContext): MaybePromise<AuthStrategyResult>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEnE,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC9B,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,qFAAqF;AACrF,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,IAAI,CAAC;IACd,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,+DAA+D;AAC/D,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,iBAAiB,CAAC;AAE/D,gEAAgE;AAChE,MAAM,WAAW,YAAY;IAC3B,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,YAAY,CAAC,kBAAkB,CAAC,CAAC;CACvE;AAED,gEAAgE;AAChE,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;CAC5B;AAED,4DAA4D;AAC5D,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AAEjF,yDAAyD;AACzD,MAAM,WAAW,qBAAqB;IACpC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,oDAAoD;AACpD,MAAM,WAAW,iBAAkB,SAAQ,KAAK;IAC9C,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEnE,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC9B,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,6FAA6F;AAC7F,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,KAAK,CAAC;CACtB;AAED,qFAAqF;AACrF,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,IAAI,CAAC;IACd,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,+DAA+D;AAC/D,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,iBAAiB,GAAG,kBAAkB,CAAC;AAEpF,gEAAgE;AAChE,MAAM,WAAW,YAAY;IAC3B,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,YAAY,CAAC,kBAAkB,CAAC,CAAC;CACvE;AAED,gEAAgE;AAChE,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;CAC5B;AAED,4DAA4D;AAC5D,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AAEjF,yDAAyD;AACzD,MAAM,WAAW,qBAAqB;IACpC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,oDAAoD;AACpD,MAAM,WAAW,iBAAkB,SAAQ,KAAK;IAC9C,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD"}
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "guard",
10
10
  "strategy"
11
11
  ],
12
- "version": "1.0.0-beta.3",
12
+ "version": "1.0.0-beta.5",
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-beta.2",
40
- "@fluojs/di": "^1.0.0-beta.3",
41
- "@fluojs/http": "^1.0.0-beta.2",
42
- "@fluojs/jwt": "^1.0.0-beta.1",
43
- "@fluojs/runtime": "^1.0.0-beta.3"
39
+ "@fluojs/di": "^1.0.0-beta.6",
40
+ "@fluojs/http": "^1.0.0-beta.10",
41
+ "@fluojs/jwt": "^1.0.0-beta.2",
42
+ "@fluojs/core": "^1.0.0-beta.4",
43
+ "@fluojs/runtime": "^1.0.0-beta.11"
44
44
  },
45
45
  "devDependencies": {
46
46
  "vitest": "^3.2.4"