@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 +37 -7
- package/README.md +37 -7
- package/dist/cookie/cookie-auth.d.ts.map +1 -1
- package/dist/cookie/cookie-auth.js +7 -1
- package/package.json +6 -6
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 {
|
|
39
|
-
import {
|
|
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:
|
|
68
|
+
[{ name: 'jwt', token: BearerJwtStrategy }],
|
|
46
69
|
),
|
|
47
70
|
],
|
|
48
|
-
providers: [
|
|
71
|
+
providers: [BearerJwtStrategy],
|
|
49
72
|
})
|
|
50
73
|
export class AuthModule {}
|
|
51
74
|
```
|
|
52
75
|
|
|
53
|
-
|
|
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(...)
|
|
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 {
|
|
39
|
-
import {
|
|
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:
|
|
68
|
+
[{ name: 'jwt', token: BearerJwtStrategy }],
|
|
46
69
|
),
|
|
47
70
|
],
|
|
48
|
-
providers: [
|
|
71
|
+
providers: [BearerJwtStrategy],
|
|
49
72
|
})
|
|
50
73
|
export class AuthModule {}
|
|
51
74
|
```
|
|
52
75
|
|
|
53
|
-
|
|
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(...)`
|
|
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;
|
|
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 (
|
|
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
|
|
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
|
|
40
|
-
"@fluojs/di": "^1.0.0
|
|
41
|
-
"@fluojs/http": "^1.0.0
|
|
42
|
-
"@fluojs/jwt": "^1.0.0
|
|
43
|
-
"@fluojs/runtime": "^1.0.0
|
|
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"
|