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

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
@@ -83,6 +83,8 @@ const googleBridge = createPassportJsStrategyBridge('google', GoogleStrategy, {
83
83
  });
84
84
  ```
85
85
 
86
+ 브릿지는 각 Passport.js 전략 실행을 정확히 한 번만 정착(settle)시킵니다. 전략은 바인딩된 Passport 액션(`success`, `fail`, `redirect`, `pass`, `error`) 중 하나를 호출해야 하며, promise rejection 또는 액션 없이 완료된 promise는 요청을 미해결 상태로 두지 않고 인증 실패로 처리됩니다. 커스텀 `mapPrincipal` 함수는 비어 있지 않은 `subject`와 객체 형태의 `claims`를 포함한 유효한 fluo `Principal`을 반환해야 합니다.
87
+
86
88
  ### 쿠키 인증 프리셋
87
89
 
88
90
  HTTP 쿠키에서 인증 정보를 읽는 애플리케이션이라면 `CookieAuthModule.forRoot(...)`를 사용합니다.
@@ -110,6 +112,8 @@ export class AuthModule {}
110
112
 
111
113
  애플리케이션 모듈에서 cookie-auth 지원이 필요하면 `CookieAuthModule.forRoot(...)`를 `PassportModule.forRoot(...)`와 함께 import 하세요.
112
114
 
115
+ `CookieAuthStrategy`는 `@fluojs/jwt`가 정규화한 JWT principal 계약을 보존하며, `subject`, `claims`, `issuer`, `audience`, `roles`, `scopes`를 그대로 전달합니다.
116
+
113
117
  ### 리프레시 토큰 수명 주기
114
118
 
115
119
  패키지에서 제공하는 `RefreshTokenStrategy`와 `RefreshTokenService`를 사용하여 안전한 토큰 로테이션 및 폐기 기능을 구현할 수 있습니다.
package/README.md CHANGED
@@ -83,6 +83,8 @@ const googleBridge = createPassportJsStrategyBridge('google', GoogleStrategy, {
83
83
  });
84
84
  ```
85
85
 
86
+ 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`.
87
+
86
88
  ### Cookie Auth Preset
87
89
 
88
90
  Use `CookieAuthModule.forRoot(...)` when your app authenticates requests from HTTP cookies.
@@ -110,6 +112,8 @@ export class AuthModule {}
110
112
 
111
113
  Import `CookieAuthModule.forRoot(...)` alongside `PassportModule.forRoot(...)` when you want cookie-auth support in an application module.
112
114
 
115
+ `CookieAuthStrategy` preserves the normalized JWT principal contract from `@fluojs/jwt`, including `subject`, `claims`, `issuer`, `audience`, `roles`, and `scopes`.
116
+
113
117
  ### Refresh Token Lifecycle
114
118
 
115
119
  The package provides a built-in `RefreshTokenStrategy` and `RefreshTokenService` to handle secure token rotation and revocation.
@@ -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;AA2FD;;;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;IA0B3E,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;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"}
@@ -92,6 +92,9 @@ function cloneStrategyStateValue(value) {
92
92
  }
93
93
  return value;
94
94
  }
95
+ function isPromiseLike(value) {
96
+ return typeof value === 'object' && value !== null && 'then' in value && typeof value.then === 'function';
97
+ }
95
98
 
96
99
  /**
97
100
  * A bridge strategy that allows using Passport.js strategies within the fluo
@@ -119,7 +122,18 @@ export class PassportJsAuthStrategy {
119
122
  });
120
123
  this.bindStrategyActions(strategy);
121
124
  try {
122
- strategy.authenticate(request, this.options.authenticateOptions);
125
+ const result = strategy.authenticate(request, this.options.authenticateOptions);
126
+ if (isPromiseLike(result)) {
127
+ result.then(() => {
128
+ this.settle(strategy, state => {
129
+ state.reject(new AuthenticationRequiredError('Passport strategy did not settle authentication.'));
130
+ });
131
+ }, error => {
132
+ this.settle(strategy, state => {
133
+ state.reject(error);
134
+ });
135
+ });
136
+ }
123
137
  } catch (error) {
124
138
  this.settle(strategy, () => reject(error));
125
139
  }
@@ -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;CA6CvE;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,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"}
@@ -77,7 +77,9 @@ class CookieAuthStrategy {
77
77
  try {
78
78
  const principal = await this.verifier.verifyAccessToken(accessToken);
79
79
  return {
80
+ audience: principal.audience,
80
81
  claims: principal.claims,
82
+ issuer: principal.issuer,
81
83
  roles: principal.roles,
82
84
  scopes: principal.scopes,
83
85
  subject: principal.subject
@@ -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;AAkCpB;;;;;;;;;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;CA0DxD"}
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"}
package/dist/guard.js CHANGED
@@ -19,6 +19,36 @@ function resolvePrincipal(result) {
19
19
  }
20
20
  return result;
21
21
  }
22
+ function isRecord(value) {
23
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
24
+ }
25
+ function isStringArray(value) {
26
+ return Array.isArray(value) && value.every(item => typeof item === 'string');
27
+ }
28
+ function isPrincipal(value) {
29
+ if (!isRecord(value)) {
30
+ return false;
31
+ }
32
+ if (typeof value.subject !== 'string' || value.subject.length === 0) {
33
+ return false;
34
+ }
35
+ if (!isRecord(value.claims)) {
36
+ return false;
37
+ }
38
+ if (value.issuer !== undefined && typeof value.issuer !== 'string') {
39
+ return false;
40
+ }
41
+ if (value.audience !== undefined && typeof value.audience !== 'string' && !isStringArray(value.audience)) {
42
+ return false;
43
+ }
44
+ if (value.roles !== undefined && !isStringArray(value.roles)) {
45
+ return false;
46
+ }
47
+ if (value.scopes !== undefined && !isStringArray(value.scopes)) {
48
+ return false;
49
+ }
50
+ return true;
51
+ }
22
52
  function hasRequiredScopes(principal, scopes) {
23
53
  return scopes.every(scope => principal.scopes?.includes(scope));
24
54
  }
@@ -103,6 +133,9 @@ class AuthGuard {
103
133
  if (!principal) {
104
134
  throw new AuthenticationFailedError('Authentication strategy did not return a principal.');
105
135
  }
136
+ if (!isPrincipal(principal)) {
137
+ throw new AuthenticationFailedError('Authentication strategy returned an invalid principal.');
138
+ }
106
139
  if (requirement?.scopes?.length && !hasRequiredScopes(principal, requirement.scopes)) {
107
140
  throw new ForbiddenException('Access denied.');
108
141
  }
@@ -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;AAwElD,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;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"}
package/dist/metadata.js CHANGED
@@ -1,4 +1,4 @@
1
- import { metadataSymbol } from '@fluojs/core/internal';
1
+ import { getStandardConstructorMetadataBag, getStandardMetadataBag } from '@fluojs/core/internal';
2
2
  import { mergeAuthRequirements, normalizeDeclaredScopes } from './scope.js';
3
3
  const standardClassRequirementKey = Symbol.for('fluo.passport.standard.class-auth');
4
4
  const standardMethodRequirementKey = Symbol.for('fluo.passport.standard.method-auth');
@@ -38,15 +38,11 @@ function invalidateRequirementCache(controllerType, propertyKey) {
38
38
  mergedMethodRequirementCache.delete(controllerType);
39
39
  }
40
40
  }
41
- function getStandardMetadataBag(target) {
42
- return target[metadataSymbol];
43
- }
44
41
  function getStandardClassRequirement(target) {
45
42
  return normalizeRequirement(getStandardMetadataBag(target)?.[standardClassRequirementKey]);
46
43
  }
47
44
  function getStandardMethodRequirement(target, propertyKey) {
48
- const constructor = target.constructor;
49
- const map = constructor ? getStandardMetadataBag(constructor)?.[standardMethodRequirementKey] : undefined;
45
+ const map = getStandardConstructorMetadataBag(target)?.[standardMethodRequirementKey];
50
46
  return normalizeRequirement(map?.get(propertyKey));
51
47
  }
52
48
  export function defineAuthRequirement(target, requirement, propertyKey) {
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "guard",
10
10
  "strategy"
11
11
  ],
12
- "version": "1.0.0-beta.1",
12
+ "version": "1.0.0-beta.3",
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.1",
40
- "@fluojs/di": "^1.0.0-beta.1",
41
- "@fluojs/http": "^1.0.0-beta.1",
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
42
  "@fluojs/jwt": "^1.0.0-beta.1",
43
- "@fluojs/runtime": "^1.0.0-beta.1"
43
+ "@fluojs/runtime": "^1.0.0-beta.3"
44
44
  },
45
45
  "devDependencies": {
46
46
  "vitest": "^3.2.4"