@fluojs/passport 1.0.0-beta.6 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ko.md CHANGED
@@ -34,23 +34,46 @@ npm install @fluojs/passport
34
34
  사용할 전략을 정의하고 `PassportModule.forRoot(...)`를 통해 등록합니다.
35
35
 
36
36
  ```typescript
37
- import { Module } from '@fluojs/core';
38
- import { PassportModule } from '@fluojs/passport';
39
- import { MyJwtStrategy } from './jwt.strategy';
37
+ import { Inject, Module } from '@fluojs/core';
38
+ import type { GuardContext } from '@fluojs/http';
39
+ import { DefaultJwtVerifier, JwtModule } from '@fluojs/jwt';
40
+ import { AuthenticationRequiredError, PassportModule, type AuthStrategy } from '@fluojs/passport';
41
+
42
+ @Inject(DefaultJwtVerifier)
43
+ export class BearerJwtStrategy implements AuthStrategy {
44
+ constructor(private readonly verifier: DefaultJwtVerifier) {}
45
+
46
+ async authenticate(context: GuardContext) {
47
+ const authorization = context.requestContext.request.headers.authorization;
48
+ const [scheme, token] = typeof authorization === 'string' ? authorization.split(' ') : [];
49
+
50
+ if (scheme !== 'Bearer' || !token) {
51
+ throw new AuthenticationRequiredError('Bearer access token is required.');
52
+ }
53
+
54
+ return this.verifier.verifyAccessToken(token);
55
+ }
56
+ }
40
57
 
41
58
  @Module({
42
59
  imports: [
60
+ JwtModule.forRoot({
61
+ algorithms: ['HS256'],
62
+ audience: 'my-app',
63
+ issuer: 'my-api',
64
+ secret: 'your-secure-secret',
65
+ }),
43
66
  PassportModule.forRoot(
44
67
  { defaultStrategy: 'jwt' },
45
- [{ name: 'jwt', token: MyJwtStrategy }]
68
+ [{ name: 'jwt', token: BearerJwtStrategy }],
46
69
  ),
47
70
  ],
48
- providers: [MyJwtStrategy],
71
+ providers: [BearerJwtStrategy],
49
72
  })
50
73
  export class AuthModule {}
51
74
  ```
52
75
 
53
- 전략 등록은 `PassportModule.forRoot(...)`로 구성합니다.
76
+ JWT 기반 passport 전략에는 두 가지 module wiring이 모두 필요합니다. `JwtModule.forRoot(...)`는 `DefaultJwtVerifier`를 등록하고, `PassportModule.forRoot(...)`는 `@UseAuth('jwt')`가 resolve할 named strategy를 등록합니다. `DefaultJwtVerifier.verifyAccessToken(...)` 결과를 반환하면 `AuthGuard`가 `requestContext.principal`에 기록하는 정규화 principal 계약(`subject`, `claims`, `issuer`, `audience`, `roles`, `scopes`)이 보존됩니다.
54
77
 
55
78
  ### 2. 라우트 보호
56
79
 
@@ -91,6 +114,7 @@ HTTP 쿠키에서 인증 정보를 읽는 애플리케이션이라면 `CookieAut
91
114
 
92
115
  ```typescript
93
116
  import { Module } from '@fluojs/core';
117
+ import { JwtModule } from '@fluojs/jwt';
94
118
  import {
95
119
  CookieAuthModule,
96
120
  CookieAuthStrategy,
@@ -101,6 +125,10 @@ import {
101
125
  @Module({
102
126
  imports: [
103
127
  CookieAuthModule.forRoot(),
128
+ JwtModule.forRoot({
129
+ algorithms: ['HS256'],
130
+ secret: 'your-secure-secret',
131
+ }),
104
132
  PassportModule.forRoot(
105
133
  { defaultStrategy: COOKIE_AUTH_STRATEGY_NAME },
106
134
  [{ name: COOKIE_AUTH_STRATEGY_NAME, token: CookieAuthStrategy }],
@@ -110,10 +138,12 @@ import {
110
138
  export class AuthModule {}
111
139
  ```
112
140
 
113
- 애플리케이션 모듈에서 cookie-auth 지원이 필요하면 `CookieAuthModule.forRoot(...)`를 `PassportModule.forRoot(...)`와 함께 import 하세요.
141
+ 애플리케이션 모듈에서 cookie-auth 지원이 필요하면 `CookieAuthModule.forRoot(...)`, `JwtModule.forRoot(...)`, `PassportModule.forRoot(...)`를 함께 import 하세요. Cookie preset은 `CookieAuthStrategy`와 cookie option을 제공하고, JWT 검증은 여전히 `@fluojs/jwt`에서 오며, passport registry는 여전히 `PassportModule.forRoot(...)`에서 옵니다.
114
142
 
115
143
  `CookieAuthStrategy`는 `@fluojs/jwt`가 정규화한 JWT principal 계약을 보존하며, `subject`, `claims`, `issuer`, `audience`, `roles`, `scopes`를 그대로 전달합니다.
116
144
 
145
+ Cookie access token은 비어 있지 않은 문자열이어야 합니다. `requireAccessToken: false`일 때만 누락된 cookie가 `{ authenticated: false }`로 resolve될 수 있으며, 존재하지만 malformed인 cookie 값은 JWT 검증 전에 항상 인증 실패로 처리됩니다.
146
+
117
147
  보호된 라우트는 계속 `@UseAuth(...)`를 사용해야 합니다. `requireAccessToken: false`를 설정해도 쿠키가 없을 때는 익명 principal이 아니라 명시적인 미인증 결과를 반환하므로, 보호된 라우트는 요청을 계속 거부합니다.
118
148
 
119
149
  로그인 사용자와 게스트 호출자를 모두 허용하려는 라우트에서만 `@UseOptionalAuth(...)`를 사용하세요.
package/README.md CHANGED
@@ -34,23 +34,46 @@ npm install @fluojs/passport
34
34
  Define your strategies and register them using `PassportModule.forRoot(...)`.
35
35
 
36
36
  ```typescript
37
- import { Module } from '@fluojs/core';
38
- import { PassportModule } from '@fluojs/passport';
39
- import { MyJwtStrategy } from './jwt.strategy';
37
+ import { Inject, Module } from '@fluojs/core';
38
+ import type { GuardContext } from '@fluojs/http';
39
+ import { DefaultJwtVerifier, JwtModule } from '@fluojs/jwt';
40
+ import { AuthenticationRequiredError, PassportModule, type AuthStrategy } from '@fluojs/passport';
41
+
42
+ @Inject(DefaultJwtVerifier)
43
+ export class BearerJwtStrategy implements AuthStrategy {
44
+ constructor(private readonly verifier: DefaultJwtVerifier) {}
45
+
46
+ async authenticate(context: GuardContext) {
47
+ const authorization = context.requestContext.request.headers.authorization;
48
+ const [scheme, token] = typeof authorization === 'string' ? authorization.split(' ') : [];
49
+
50
+ if (scheme !== 'Bearer' || !token) {
51
+ throw new AuthenticationRequiredError('Bearer access token is required.');
52
+ }
53
+
54
+ return this.verifier.verifyAccessToken(token);
55
+ }
56
+ }
40
57
 
41
58
  @Module({
42
59
  imports: [
60
+ JwtModule.forRoot({
61
+ algorithms: ['HS256'],
62
+ audience: 'my-app',
63
+ issuer: 'my-api',
64
+ secret: 'your-secure-secret',
65
+ }),
43
66
  PassportModule.forRoot(
44
67
  { defaultStrategy: 'jwt' },
45
- [{ name: 'jwt', token: MyJwtStrategy }]
68
+ [{ name: 'jwt', token: BearerJwtStrategy }],
46
69
  ),
47
70
  ],
48
- providers: [MyJwtStrategy],
71
+ providers: [BearerJwtStrategy],
49
72
  })
50
73
  export class AuthModule {}
51
74
  ```
52
75
 
53
- Register strategies through `PassportModule.forRoot(...)`.
76
+ JWT-based passport strategies require both pieces of module wiring: `JwtModule.forRoot(...)` registers `DefaultJwtVerifier`, and `PassportModule.forRoot(...)` registers the named strategy that `@UseAuth('jwt')` resolves. Returning the `DefaultJwtVerifier.verifyAccessToken(...)` result preserves the normalized principal contract (`subject`, `claims`, `issuer`, `audience`, `roles`, and `scopes`) that `AuthGuard` writes to `requestContext.principal`.
54
77
 
55
78
  ### 2. Protect Routes
56
79
 
@@ -91,6 +114,7 @@ Use `CookieAuthModule.forRoot(...)` when your app authenticates requests from HT
91
114
 
92
115
  ```typescript
93
116
  import { Module } from '@fluojs/core';
117
+ import { JwtModule } from '@fluojs/jwt';
94
118
  import {
95
119
  CookieAuthModule,
96
120
  CookieAuthStrategy,
@@ -101,6 +125,10 @@ import {
101
125
  @Module({
102
126
  imports: [
103
127
  CookieAuthModule.forRoot(),
128
+ JwtModule.forRoot({
129
+ algorithms: ['HS256'],
130
+ secret: 'your-secure-secret',
131
+ }),
104
132
  PassportModule.forRoot(
105
133
  { defaultStrategy: COOKIE_AUTH_STRATEGY_NAME },
106
134
  [{ name: COOKIE_AUTH_STRATEGY_NAME, token: CookieAuthStrategy }],
@@ -110,10 +138,12 @@ import {
110
138
  export class AuthModule {}
111
139
  ```
112
140
 
113
- Import `CookieAuthModule.forRoot(...)` alongside `PassportModule.forRoot(...)` when you want cookie-auth support in an application module.
141
+ Import `CookieAuthModule.forRoot(...)`, `JwtModule.forRoot(...)`, and `PassportModule.forRoot(...)` together when you want cookie-auth support in an application module. The cookie preset provides `CookieAuthStrategy` and cookie options; JWT verification still comes from `@fluojs/jwt`, and the passport registry still comes from `PassportModule.forRoot(...)`.
114
142
 
115
143
  `CookieAuthStrategy` preserves the normalized JWT principal contract from `@fluojs/jwt`, including `subject`, `claims`, `issuer`, `audience`, `roles`, and `scopes`.
116
144
 
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
+
117
147
  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
148
 
119
149
  Use `@UseOptionalAuth(...)` only on routes that intentionally support both signed-in and guest callers:
@@ -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,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"}
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;AAUxF;;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;CA6CvE;AAED;;GAEG;AACH,eAAO,MAAM,yBAAyB,WAAW,CAAC"}
@@ -10,6 +10,9 @@ import { AuthenticationRequiredError } from '../errors.js';
10
10
  const unauthenticatedCookieAuthResult = {
11
11
  authenticated: false
12
12
  };
13
+ function isNonEmptyString(value) {
14
+ return typeof value === 'string' && value.length > 0;
15
+ }
13
16
 
14
17
  /**
15
18
  * Provides cookie-auth strategy options through dependency injection.
@@ -71,12 +74,15 @@ class CookieAuthStrategy {
71
74
  return unauthenticatedCookieAuthResult;
72
75
  }
73
76
  const accessToken = cookies[this.options.accessTokenCookieName];
74
- if (!accessToken) {
77
+ if (accessToken === undefined || accessToken === '') {
75
78
  if (this.options.requireAccessToken) {
76
79
  throw new AuthenticationRequiredError('Access token cookie is required.');
77
80
  }
78
81
  return unauthenticatedCookieAuthResult;
79
82
  }
83
+ if (!isNonEmptyString(accessToken)) {
84
+ throw new AuthenticationRequiredError('Access token cookie must be a non-empty string.');
85
+ }
80
86
  try {
81
87
  const principal = await this.verifier.verifyAccessToken(accessToken);
82
88
  return {
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "guard",
10
10
  "strategy"
11
11
  ],
12
- "version": "1.0.0-beta.6",
12
+ "version": "1.0.0",
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.4",
40
- "@fluojs/di": "^1.0.0-beta.6",
41
- "@fluojs/http": "^1.0.0-beta.10",
42
- "@fluojs/jwt": "^1.0.0-beta.3",
43
- "@fluojs/runtime": "^1.0.0-beta.11"
39
+ "@fluojs/core": "^1.0.0",
40
+ "@fluojs/di": "^1.0.0",
41
+ "@fluojs/http": "^1.0.0",
42
+ "@fluojs/jwt": "^1.0.0",
43
+ "@fluojs/runtime": "^1.0.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "vitest": "^3.2.4"