@fluojs/passport 1.0.2 → 1.0.4

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
@@ -144,6 +144,8 @@ export class AuthModule {}
144
144
 
145
145
  Cookie access token은 비어 있지 않은 문자열이어야 합니다. `requireAccessToken: false`일 때만 누락된 cookie가 `{ authenticated: false }`로 resolve될 수 있으며, 존재하지만 malformed인 cookie 값은 JWT 검증 전에 항상 인증 실패로 처리됩니다.
146
146
 
147
+ `CookieManager`는 underlying adapter가 기존 header를 `set-cookie`처럼 다른 casing으로 저장했더라도, response에 이미 설정된 cookie를 덮어쓰지 않고 access-token 및 refresh-token `Set-Cookie` 값을 append합니다.
148
+
147
149
  보호된 라우트는 계속 `@UseAuth(...)`를 사용해야 합니다. `requireAccessToken: false`를 설정해도 쿠키가 없을 때는 익명 principal이 아니라 명시적인 미인증 결과를 반환하므로, 보호된 라우트는 요청을 계속 거부합니다.
148
150
 
149
151
  로그인 사용자와 게스트 호출자를 모두 허용하려는 라우트에서만 `@UseOptionalAuth(...)`를 사용하세요.
@@ -233,7 +235,7 @@ Identity-link 결정을 모델링하려면 `createConservativeAccountLinkPolicy(
233
235
 
234
236
  ### Cookie auth preset
235
237
  - `CookieAuthModule`: 내장 cookie-auth preset의 모듈 진입점입니다.
236
- - `CookieAuthStrategy`, `COOKIE_AUTH_STRATEGY_NAME`, `COOKIE_AUTH_OPTIONS`, `DEFAULT_COOKIE_AUTH_OPTIONS`: Cookie strategy wiring token 기본값입니다.
238
+ - `CookieAuthStrategy`, `COOKIE_AUTH_STRATEGY_NAME`, `COOKIE_AUTH_OPTIONS`, `DEFAULT_COOKIE_AUTH_OPTIONS`, `DEFAULT_COOKIE_OPTIONS`: Cookie strategy wiring token, preset 기본값, response-cookie 기본값입니다.
237
239
  - `CookieAuthOptions`, `CookieAuthPresetConfig`, `CookieManagerConfig`, `CookieOptions`, `SetCookieOptions`: Cookie strategy 및 response cookie 설정 타입입니다.
238
240
  - `CookieManager`: HttpOnly access/refresh token cookie를 설정하고 제거하는 유틸리티입니다.
239
241
  - Cookie helper: `createCookieAuthPreset`, `createCookieAuthStrategyRegistration`, `createCookieManager`, `normalizeCookieAuthOptions`.
@@ -261,7 +263,7 @@ Identity-link 결정을 모델링하려면 `createConservativeAccountLinkPolicy(
261
263
  - `createPassportPlatformDiagnosticIssues(...)`: Empty registry, 누락된 default strategy, cookie preset readiness, refresh-token backing store readiness 문제에 대한 diagnostic issue를 생성합니다.
262
264
  - `PassportPlatformStatusSnapshot`, `PassportStatusAdapterInput`: Status helper input/output 계약입니다.
263
265
 
264
- `UseOptionalAuth`는 scope가 필요 없는 route에서만 credential 누락을 우회합니다. Scoped route에는 여전히 principal이 필요합니다. Passport.js bridge의 `redirect()`는 response를 commit하고 protected handler를 건너뛰며, `pass()`와 Passport action 없이 완료된 strategy는 인증 실패입니다.
266
+ `UseOptionalAuth`는 scope가 필요 없는 route에서만 credential 누락을 우회합니다. Scoped route에는 여전히 principal이 필요합니다. Passport.js bridge의 `redirect()`는 response를 commit하고 protected handler를 건너뛰며, `pass()`와 Passport action 없이 완료된 strategy는 인증 실패입니다. Refresh-token backing store status 및 diagnostic surface는 readiness, health, details, diagnostic cause를 노출하기 전에 secret처럼 보이는 reason 문자열을 redact합니다.
265
267
 
266
268
  ## 관련 패키지
267
269
 
package/README.md CHANGED
@@ -144,6 +144,8 @@ Import `CookieAuthModule.forRoot(...)`, `JwtModule.forRoot(...)`, and `PassportM
144
144
 
145
145
  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.
146
146
 
147
+ `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`.
148
+
147
149
  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.
148
150
 
149
151
  Use `@UseOptionalAuth(...)` only on routes that intentionally support both signed-in and guest callers:
@@ -233,7 +235,7 @@ Use `createConservativeAccountLinkPolicy(...)` and `resolveAccountLinking(...)`
233
235
 
234
236
  ### Cookie Auth Preset
235
237
  - `CookieAuthModule`: Module entry point for the built-in cookie-auth preset.
236
- - `CookieAuthStrategy`, `COOKIE_AUTH_STRATEGY_NAME`, `COOKIE_AUTH_OPTIONS`, `DEFAULT_COOKIE_AUTH_OPTIONS`: Cookie strategy wiring tokens and defaults.
238
+ - `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.
237
239
  - `CookieAuthOptions`, `CookieAuthPresetConfig`, `CookieManagerConfig`, `CookieOptions`, `SetCookieOptions`: Cookie strategy and response cookie configuration types.
238
240
  - `CookieManager`: Utility for setting and clearing HttpOnly access/refresh token cookies.
239
241
  - Cookie helpers: `createCookieAuthPreset`, `createCookieAuthStrategyRegistration`, `createCookieManager`, `normalizeCookieAuthOptions`.
@@ -261,7 +263,7 @@ Use `createConservativeAccountLinkPolicy(...)` and `resolveAccountLinking(...)`
261
263
  - `createPassportPlatformDiagnosticIssues(...)`: Emits diagnostic issues for empty registries, missing default strategies, cookie preset readiness, and refresh-token backing store readiness.
262
264
  - `PassportPlatformStatusSnapshot`, `PassportStatusAdapterInput`: Status helper input/output contracts.
263
265
 
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.
266
+ `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. Refresh-token backing store status and diagnostic surfaces redact secret-like reason strings before exposing readiness, health, details, or diagnostic causes.
265
267
 
266
268
  ## Related Packages
267
269
 
@@ -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;;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
+ {"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,EAAE,KAAK,iBAAiB,EAA8B,MAAM,kBAAkB,CAAC;AAEtF;;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;AA4DF;;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;CAcxB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,CAAC,EAAE,mBAAmB,GAAG,aAAa,CAE/E"}
@@ -51,6 +51,23 @@ function buildClearCookieHeader(name, options) {
51
51
  maxAge: 0
52
52
  });
53
53
  }
54
+ function getHeaderCaseInsensitive(headers, name) {
55
+ for (const [headerName, value] of Object.entries(headers)) {
56
+ if (headerName.toLowerCase() === name.toLowerCase() && (typeof value === 'string' || Array.isArray(value))) {
57
+ return {
58
+ key: headerName,
59
+ value
60
+ };
61
+ }
62
+ }
63
+ return undefined;
64
+ }
65
+ function toHeaderValues(value) {
66
+ if (Array.isArray(value)) {
67
+ return [...value];
68
+ }
69
+ return value ? [value] : [];
70
+ }
54
71
 
55
72
  /**
56
73
  * Represents the cookie manager.
@@ -102,10 +119,14 @@ export class CookieManager {
102
119
  }
103
120
  }
104
121
  appendSetCookie(response, cookie) {
105
- const existingCookies = response.headers['Set-Cookie'];
106
- const cookies = Array.isArray(existingCookies) ? existingCookies : existingCookies ? [existingCookies] : [];
107
- cookies.push(cookie);
108
- response.setHeader('Set-Cookie', cookies.length === 1 ? cookies[0] : cookies);
122
+ const existingHeader = getHeaderCaseInsensitive(response.headers, 'Set-Cookie');
123
+ const cookies = [...toHeaderValues(existingHeader?.value), cookie];
124
+ response.setHeader('Set-Cookie', cookie);
125
+ const updatedHeader = getHeaderCaseInsensitive(response.headers, 'Set-Cookie');
126
+ if (updatedHeader?.key && updatedHeader.key !== 'Set-Cookie') {
127
+ delete response.headers[updatedHeader.key];
128
+ }
129
+ response.headers['Set-Cookie'] = cookies.length === 1 ? cookies[0] : cookies;
109
130
  }
110
131
  }
111
132
 
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EACvB,oBAAoB,EACpB,uBAAuB,EACvB,gBAAgB,EACjB,MAAM,iBAAiB,CAAC;AAEzB,iGAAiG;AACjG,MAAM,WAAW,8BAA8B;IAC7C,SAAS,EAAE,uBAAuB,CAAC;IACnC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,SAAS,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,sFAAsF;AACtF,MAAM,WAAW,0BAA0B;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAqFD;;;;;GAKG;AACH,wBAAgB,oCAAoC,CAAC,KAAK,EAAE,0BAA0B,GAAG,8BAA8B,CAsDtH;AAED;;;;;GAKG;AACH,wBAAgB,sCAAsC,CAAC,KAAK,EAAE,0BAA0B,GAAG,uBAAuB,EAAE,CAiDnH"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EACvB,oBAAoB,EACpB,uBAAuB,EACvB,gBAAgB,EACjB,MAAM,iBAAiB,CAAC;AAEzB,iGAAiG;AACjG,MAAM,WAAW,8BAA8B;IAC7C,SAAS,EAAE,uBAAuB,CAAC;IACnC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,SAAS,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,sFAAsF;AACtF,MAAM,WAAW,0BAA0B;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAiGD;;;;;GAKG;AACH,wBAAgB,oCAAoC,CAAC,KAAK,EAAE,0BAA0B,GAAG,8BAA8B,CAsDtH;AAED;;;;;GAKG;AACH,wBAAgB,sCAAsC,CAAC,KAAK,EAAE,0BAA0B,GAAG,uBAAuB,EAAE,CAiDnH"}
package/dist/status.js CHANGED
@@ -23,6 +23,15 @@ function isRefreshTokenStoreReady(input) {
23
23
  }
24
24
  return input.refreshTokenStoreReady ?? true;
25
25
  }
26
+ function redactSensitiveDiagnosticReason(reason) {
27
+ if (reason === undefined) {
28
+ return undefined;
29
+ }
30
+ if (/(accessToken|api[-_ ]?key|authorization|bearer|cookie|credential|password|passwd|refreshToken|secret|token\s*[:=])/i.test(reason)) {
31
+ return 'Refresh token backing store is unavailable; sensitive diagnostic detail was redacted.';
32
+ }
33
+ return reason;
34
+ }
26
35
  function collectReadinessReasons(input) {
27
36
  const reasons = [];
28
37
  if (!hasRegisteredStrategies(input)) {
@@ -35,7 +44,7 @@ function collectReadinessReasons(input) {
35
44
  reasons.push('Cookie auth preset is enabled but not ready.');
36
45
  }
37
46
  if (!isRefreshTokenStoreReady(input)) {
38
- reasons.push(input.refreshTokenStoreReason ?? 'Refresh token backing store is unavailable.');
47
+ reasons.push(redactSensitiveDiagnosticReason(input.refreshTokenStoreReason) ?? 'Refresh token backing store is unavailable.');
39
48
  }
40
49
  return reasons;
41
50
  }
@@ -90,7 +99,7 @@ export function createPassportPlatformStatusSnapshot(input) {
90
99
  refreshToken: {
91
100
  backingStore: {
92
101
  dependencyId: input.refreshTokenDependencyId,
93
- reason: input.refreshTokenStoreReason,
102
+ reason: redactSensitiveDiagnosticReason(input.refreshTokenStoreReason),
94
103
  ready: isRefreshTokenStoreReady(input)
95
104
  },
96
105
  enabled: input.refreshTokenEnabled ?? false
@@ -162,7 +171,7 @@ export function createPassportPlatformDiagnosticIssues(input) {
162
171
  issues.push({
163
172
  code: 'AUTH_PASSPORT_REFRESH_TOKEN_BACKING_STORE_NOT_READY',
164
173
  componentId,
165
- cause: input.refreshTokenStoreReason,
174
+ cause: redactSensitiveDiagnosticReason(input.refreshTokenStoreReason),
166
175
  dependsOn: input.refreshTokenDependencyId ? [input.refreshTokenDependencyId] : undefined,
167
176
  fixHint: 'Restore refresh token backing store readiness, or disable refresh-token strategy for this environment.',
168
177
  message: 'Refresh token strategy is enabled, but its backing dependency is not ready.',
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "guard",
10
10
  "strategy"
11
11
  ],
12
- "version": "1.0.2",
12
+ "version": "1.0.4",
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.2",
40
- "@fluojs/di": "^1.0.2",
41
- "@fluojs/http": "^1.0.0",
42
- "@fluojs/jwt": "^1.0.0",
43
- "@fluojs/runtime": "^1.1.0"
39
+ "@fluojs/di": "^1.0.3",
40
+ "@fluojs/http": "^1.1.0",
41
+ "@fluojs/jwt": "^1.0.1",
42
+ "@fluojs/runtime": "^1.1.2",
43
+ "@fluojs/core": "^1.0.3"
44
44
  },
45
45
  "devDependencies": {
46
46
  "vitest": "^3.2.4"