@nya-account/node-sdk 2.0.2 → 2.0.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.md +187 -187
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +125 -16
- package/dist/index.js.map +1 -1
- package/package.json +74 -75
package/README.md
CHANGED
|
@@ -1,187 +1,187 @@
|
|
|
1
|
-
# @nya-account/node-sdk
|
|
2
|
-
|
|
3
|
-
Official Node.js SDK for [Nya Account](https://account.lolinya.net) SSO system.
|
|
4
|
-
|
|
5
|
-
Provides a complete OAuth 2.1 / OIDC client with PKCE, JWT verification, and Express middleware.
|
|
6
|
-
|
|
7
|
-
## Installation
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npm install @nya-account/node-sdk
|
|
11
|
-
# or
|
|
12
|
-
pnpm add @nya-account/node-sdk
|
|
13
|
-
# or
|
|
14
|
-
yarn add @nya-account/node-sdk
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Quick Start
|
|
18
|
-
|
|
19
|
-
```typescript
|
|
20
|
-
import { NyaAccountClient } from '@nya-account/node-sdk'
|
|
21
|
-
|
|
22
|
-
const client = new NyaAccountClient({
|
|
23
|
-
// See https://account.lolinya.net/docs/developer/service-endpoints#integration-endpoints
|
|
24
|
-
issuer: 'https://account-api.edge.lolinya.net',
|
|
25
|
-
clientId: 'my-app',
|
|
26
|
-
clientSecret: 'my-secret'
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
// Create authorization URL (with PKCE)
|
|
30
|
-
const { url, codeVerifier, state } = await client.createAuthorizationUrl({
|
|
31
|
-
redirectUri: 'https://myapp.com/callback',
|
|
32
|
-
scope: 'openid profile email'
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
// Exchange code for tokens
|
|
36
|
-
const tokens = await client.exchangeCode({
|
|
37
|
-
code: callbackCode,
|
|
38
|
-
redirectUri: 'https://myapp.com/callback',
|
|
39
|
-
codeVerifier
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
// Get user info
|
|
43
|
-
const userInfo = await client.getUserInfo(tokens.accessToken)
|
|
44
|
-
|
|
45
|
-
// Revoke refresh token on logout
|
|
46
|
-
await client.revokeToken(tokens.refreshToken, { tokenTypeHint: 'refresh_token' })
|
|
47
|
-
|
|
48
|
-
// Build RP-initiated logout URL
|
|
49
|
-
const logoutUrl = await client.createEndSessionUrl({
|
|
50
|
-
idTokenHint: tokens.idToken,
|
|
51
|
-
postLogoutRedirectUri: 'https://myapp.com/logout/callback',
|
|
52
|
-
state: 'logout-csrf-state'
|
|
53
|
-
})
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Express Middleware
|
|
57
|
-
|
|
58
|
-
```typescript
|
|
59
|
-
import express from 'express'
|
|
60
|
-
import { NyaAccountClient } from '@nya-account/node-sdk'
|
|
61
|
-
import { getAuth } from '@nya-account/node-sdk/express'
|
|
62
|
-
|
|
63
|
-
const app = express()
|
|
64
|
-
const client = new NyaAccountClient({
|
|
65
|
-
issuer: 'https://account-api.edge.lolinya.net',
|
|
66
|
-
clientId: 'my-app',
|
|
67
|
-
clientSecret: 'my-secret'
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
// Protect all /api routes
|
|
71
|
-
app.use('/api', client.authenticate())
|
|
72
|
-
|
|
73
|
-
app.get('/api/me', (req, res) => {
|
|
74
|
-
const auth = getAuth(req)
|
|
75
|
-
res.json({ userId: auth?.sub, scopes: auth?.scope })
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
// Require specific scopes
|
|
79
|
-
app.get(
|
|
80
|
-
'/api/profile',
|
|
81
|
-
client.authenticate(),
|
|
82
|
-
client.requireScopes('profile'),
|
|
83
|
-
(req, res) => {
|
|
84
|
-
const auth = getAuth(req)
|
|
85
|
-
res.json({ name: auth?.sub })
|
|
86
|
-
}
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
// Use introspection for sensitive operations
|
|
90
|
-
app.post('/api/sensitive', client.authenticate({ strategy: 'introspection' }), handler)
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
## Configuration
|
|
94
|
-
|
|
95
|
-
| Option | Type | Default | Description |
|
|
96
|
-
| ------------------- | ---------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
97
|
-
| `issuer` | `string` | `'https://account-api.edge.lolinya.net'` | SSO service URL (Issuer URL). See [Service Endpoints](https://account.lolinya.net/docs/developer/service-endpoints#integration-endpoints) for available endpoints. |
|
|
98
|
-
| `clientId` | `string` | _required_ | OAuth client ID |
|
|
99
|
-
| `clientSecret` | `string` | _required_ | OAuth client secret |
|
|
100
|
-
| `timeout` | `number` | `10000` | HTTP request timeout in milliseconds |
|
|
101
|
-
| `discoveryCacheTtl` | `number` | `3600000` | Discovery document cache TTL in milliseconds (default: 1 hour) |
|
|
102
|
-
| `endpoints` | `EndpointConfig` | — | Explicitly specify endpoint URLs (auto-discovered via OIDC Discovery if omitted) |
|
|
103
|
-
|
|
104
|
-
## API Reference
|
|
105
|
-
|
|
106
|
-
### `NyaAccountClient`
|
|
107
|
-
|
|
108
|
-
#### Authorization
|
|
109
|
-
|
|
110
|
-
- **`createAuthorizationUrl(options)`** — Create an OAuth authorization URL with PKCE
|
|
111
|
-
- **`pushAuthorizationRequest(options)`** — Push authorization request to PAR endpoint (RFC 9126)
|
|
112
|
-
- **`createAuthorizationUrlWithPar(options)`** — Create authorization URL using PAR `request_uri`
|
|
113
|
-
|
|
114
|
-
#### Token Operations
|
|
115
|
-
|
|
116
|
-
- **`exchangeCode(options)`** — Exchange an authorization code for tokens
|
|
117
|
-
- **`refreshToken(refreshToken)`** — Refresh an Access Token
|
|
118
|
-
- **`revokeToken(token, options?)`** — Revoke a token (RFC 7009)
|
|
119
|
-
- **`introspectToken(token, options?)`** — Token introspection (RFC 7662)
|
|
120
|
-
|
|
121
|
-
#### User Info
|
|
122
|
-
|
|
123
|
-
- **`getUserInfo(accessToken)`** — Get user info via OIDC UserInfo endpoint
|
|
124
|
-
|
|
125
|
-
#### JWT Verification
|
|
126
|
-
|
|
127
|
-
- **`verifyAccessToken(token, options?)`** — Locally verify a JWT Access Token (RFC 9068)
|
|
128
|
-
- **`verifyIdToken(token, options?)`** — Locally verify an OIDC ID Token
|
|
129
|
-
|
|
130
|
-
#### Express Middleware
|
|
131
|
-
|
|
132
|
-
- **`authenticate(options?)`** — Middleware to verify Bearer Token (`local` or `introspection` strategy)
|
|
133
|
-
- **`requireScopes(...scopes)`** — Middleware to validate token scopes
|
|
134
|
-
|
|
135
|
-
#### Cache
|
|
136
|
-
|
|
137
|
-
- **`discover()`** — Fetch OIDC Discovery document (cached with TTL)
|
|
138
|
-
- **`clearCache()`** — Clear Discovery and JWT verifier cache
|
|
139
|
-
|
|
140
|
-
#### OIDC Logout
|
|
141
|
-
|
|
142
|
-
- **`createEndSessionUrl(options?)`** — Create OIDC RP-initiated logout URL (`end_session_endpoint`)
|
|
143
|
-
|
|
144
|
-
### Express Helpers
|
|
145
|
-
|
|
146
|
-
Available from `@nya-account/node-sdk/express`:
|
|
147
|
-
|
|
148
|
-
- **`getAuth(req)`** — Retrieve the verified Access Token payload from a request
|
|
149
|
-
- **`extractBearerToken(req)`** — Extract Bearer token from the Authorization header
|
|
150
|
-
- **`sendOAuthError(res, statusCode, error, errorDescription)`** — Send an OAuth-standard error response
|
|
151
|
-
|
|
152
|
-
### PKCE Utilities
|
|
153
|
-
|
|
154
|
-
- **`generatePkce()`** — Generate a code_verifier and code_challenge pair
|
|
155
|
-
- **`generateCodeVerifier()`** — Generate a PKCE code_verifier
|
|
156
|
-
- **`generateCodeChallenge(codeVerifier)`** — Generate an S256 code_challenge
|
|
157
|
-
|
|
158
|
-
## Error Handling
|
|
159
|
-
|
|
160
|
-
The SDK provides typed error classes:
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
import {
|
|
164
|
-
NyaAccountError, // Base error class
|
|
165
|
-
OAuthError, // OAuth protocol errors from the server
|
|
166
|
-
TokenVerificationError, // JWT verification failures
|
|
167
|
-
DiscoveryError // OIDC Discovery failures
|
|
168
|
-
} from '@nya-account/node-sdk'
|
|
169
|
-
|
|
170
|
-
try {
|
|
171
|
-
await client.verifyAccessToken(token)
|
|
172
|
-
} catch (error) {
|
|
173
|
-
if (error instanceof TokenVerificationError) {
|
|
174
|
-
console.log(error.code) // 'token_verification_failed'
|
|
175
|
-
console.log(error.description) // Human-readable description
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
## Requirements
|
|
181
|
-
|
|
182
|
-
- Node.js >= 20.0.0
|
|
183
|
-
- Express 4.x or 5.x (optional, for middleware features)
|
|
184
|
-
|
|
185
|
-
## License
|
|
186
|
-
|
|
187
|
-
[MIT](./LICENSE)
|
|
1
|
+
# @nya-account/node-sdk
|
|
2
|
+
|
|
3
|
+
Official Node.js SDK for [Nya Account](https://account.lolinya.net) SSO system.
|
|
4
|
+
|
|
5
|
+
Provides a complete OAuth 2.1 / OIDC client with PKCE, JWT verification, and Express middleware.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @nya-account/node-sdk
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @nya-account/node-sdk
|
|
13
|
+
# or
|
|
14
|
+
yarn add @nya-account/node-sdk
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { NyaAccountClient } from '@nya-account/node-sdk'
|
|
21
|
+
|
|
22
|
+
const client = new NyaAccountClient({
|
|
23
|
+
// See https://account.lolinya.net/docs/developer/service-endpoints#integration-endpoints
|
|
24
|
+
issuer: 'https://account-api.edge.lolinya.net',
|
|
25
|
+
clientId: 'my-app',
|
|
26
|
+
clientSecret: 'my-secret'
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
// Create authorization URL (with PKCE)
|
|
30
|
+
const { url, codeVerifier, state } = await client.createAuthorizationUrl({
|
|
31
|
+
redirectUri: 'https://myapp.com/callback',
|
|
32
|
+
scope: 'openid profile email'
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// Exchange code for tokens
|
|
36
|
+
const tokens = await client.exchangeCode({
|
|
37
|
+
code: callbackCode,
|
|
38
|
+
redirectUri: 'https://myapp.com/callback',
|
|
39
|
+
codeVerifier
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
// Get user info
|
|
43
|
+
const userInfo = await client.getUserInfo(tokens.accessToken)
|
|
44
|
+
|
|
45
|
+
// Revoke refresh token on logout
|
|
46
|
+
await client.revokeToken(tokens.refreshToken, { tokenTypeHint: 'refresh_token' })
|
|
47
|
+
|
|
48
|
+
// Build RP-initiated logout URL
|
|
49
|
+
const logoutUrl = await client.createEndSessionUrl({
|
|
50
|
+
idTokenHint: tokens.idToken,
|
|
51
|
+
postLogoutRedirectUri: 'https://myapp.com/logout/callback',
|
|
52
|
+
state: 'logout-csrf-state'
|
|
53
|
+
})
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Express Middleware
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import express from 'express'
|
|
60
|
+
import { NyaAccountClient } from '@nya-account/node-sdk'
|
|
61
|
+
import { getAuth } from '@nya-account/node-sdk/express'
|
|
62
|
+
|
|
63
|
+
const app = express()
|
|
64
|
+
const client = new NyaAccountClient({
|
|
65
|
+
issuer: 'https://account-api.edge.lolinya.net',
|
|
66
|
+
clientId: 'my-app',
|
|
67
|
+
clientSecret: 'my-secret'
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// Protect all /api routes
|
|
71
|
+
app.use('/api', client.authenticate())
|
|
72
|
+
|
|
73
|
+
app.get('/api/me', (req, res) => {
|
|
74
|
+
const auth = getAuth(req)
|
|
75
|
+
res.json({ userId: auth?.sub, scopes: auth?.scope })
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
// Require specific scopes
|
|
79
|
+
app.get(
|
|
80
|
+
'/api/profile',
|
|
81
|
+
client.authenticate(),
|
|
82
|
+
client.requireScopes('profile'),
|
|
83
|
+
(req, res) => {
|
|
84
|
+
const auth = getAuth(req)
|
|
85
|
+
res.json({ name: auth?.sub })
|
|
86
|
+
}
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
// Use introspection for sensitive operations
|
|
90
|
+
app.post('/api/sensitive', client.authenticate({ strategy: 'introspection' }), handler)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Configuration
|
|
94
|
+
|
|
95
|
+
| Option | Type | Default | Description |
|
|
96
|
+
| ------------------- | ---------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
97
|
+
| `issuer` | `string` | `'https://account-api.edge.lolinya.net'` | SSO service URL (Issuer URL). See [Service Endpoints](https://account.lolinya.net/docs/developer/service-endpoints#integration-endpoints) for available endpoints. |
|
|
98
|
+
| `clientId` | `string` | _required_ | OAuth client ID |
|
|
99
|
+
| `clientSecret` | `string` | _required_ | OAuth client secret |
|
|
100
|
+
| `timeout` | `number` | `10000` | HTTP request timeout in milliseconds |
|
|
101
|
+
| `discoveryCacheTtl` | `number` | `3600000` | Discovery document cache TTL in milliseconds (default: 1 hour) |
|
|
102
|
+
| `endpoints` | `EndpointConfig` | — | Explicitly specify endpoint URLs (auto-discovered via OIDC Discovery if omitted) |
|
|
103
|
+
|
|
104
|
+
## API Reference
|
|
105
|
+
|
|
106
|
+
### `NyaAccountClient`
|
|
107
|
+
|
|
108
|
+
#### Authorization
|
|
109
|
+
|
|
110
|
+
- **`createAuthorizationUrl(options)`** — Create an OAuth authorization URL with PKCE
|
|
111
|
+
- **`pushAuthorizationRequest(options)`** — Push authorization request to PAR endpoint (RFC 9126)
|
|
112
|
+
- **`createAuthorizationUrlWithPar(options)`** — Create authorization URL using PAR `request_uri`
|
|
113
|
+
|
|
114
|
+
#### Token Operations
|
|
115
|
+
|
|
116
|
+
- **`exchangeCode(options)`** — Exchange an authorization code for tokens
|
|
117
|
+
- **`refreshToken(refreshToken)`** — Refresh an Access Token
|
|
118
|
+
- **`revokeToken(token, options?)`** — Revoke a token (RFC 7009)
|
|
119
|
+
- **`introspectToken(token, options?)`** — Token introspection (RFC 7662)
|
|
120
|
+
|
|
121
|
+
#### User Info
|
|
122
|
+
|
|
123
|
+
- **`getUserInfo(accessToken)`** — Get user info via OIDC UserInfo endpoint
|
|
124
|
+
|
|
125
|
+
#### JWT Verification
|
|
126
|
+
|
|
127
|
+
- **`verifyAccessToken(token, options?)`** — Locally verify a JWT Access Token (RFC 9068)
|
|
128
|
+
- **`verifyIdToken(token, options?)`** — Locally verify an OIDC ID Token
|
|
129
|
+
|
|
130
|
+
#### Express Middleware
|
|
131
|
+
|
|
132
|
+
- **`authenticate(options?)`** — Middleware to verify Bearer Token (`local` or `introspection` strategy)
|
|
133
|
+
- **`requireScopes(...scopes)`** — Middleware to validate token scopes
|
|
134
|
+
|
|
135
|
+
#### Cache
|
|
136
|
+
|
|
137
|
+
- **`discover()`** — Fetch OIDC Discovery document (cached with TTL)
|
|
138
|
+
- **`clearCache()`** — Clear Discovery and JWT verifier cache
|
|
139
|
+
|
|
140
|
+
#### OIDC Logout
|
|
141
|
+
|
|
142
|
+
- **`createEndSessionUrl(options?)`** — Create OIDC RP-initiated logout URL (`end_session_endpoint`)
|
|
143
|
+
|
|
144
|
+
### Express Helpers
|
|
145
|
+
|
|
146
|
+
Available from `@nya-account/node-sdk/express`:
|
|
147
|
+
|
|
148
|
+
- **`getAuth(req)`** — Retrieve the verified Access Token payload from a request
|
|
149
|
+
- **`extractBearerToken(req)`** — Extract Bearer token from the Authorization header
|
|
150
|
+
- **`sendOAuthError(res, statusCode, error, errorDescription)`** — Send an OAuth-standard error response
|
|
151
|
+
|
|
152
|
+
### PKCE Utilities
|
|
153
|
+
|
|
154
|
+
- **`generatePkce()`** — Generate a code_verifier and code_challenge pair
|
|
155
|
+
- **`generateCodeVerifier()`** — Generate a PKCE code_verifier
|
|
156
|
+
- **`generateCodeChallenge(codeVerifier)`** — Generate an S256 code_challenge
|
|
157
|
+
|
|
158
|
+
## Error Handling
|
|
159
|
+
|
|
160
|
+
The SDK provides typed error classes:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import {
|
|
164
|
+
NyaAccountError, // Base error class
|
|
165
|
+
OAuthError, // OAuth protocol errors from the server
|
|
166
|
+
TokenVerificationError, // JWT verification failures
|
|
167
|
+
DiscoveryError // OIDC Discovery failures
|
|
168
|
+
} from '@nya-account/node-sdk'
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
await client.verifyAccessToken(token)
|
|
172
|
+
} catch (error) {
|
|
173
|
+
if (error instanceof TokenVerificationError) {
|
|
174
|
+
console.log(error.code) // 'token_verification_failed'
|
|
175
|
+
console.log(error.description) // Human-readable description
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Requirements
|
|
181
|
+
|
|
182
|
+
- Node.js >= 20.0.0
|
|
183
|
+
- Express 4.x or 5.x (optional, for middleware features)
|
|
184
|
+
|
|
185
|
+
## License
|
|
186
|
+
|
|
187
|
+
[MIT](./LICENSE)
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ interface NyaAccountConfig {
|
|
|
13
13
|
clientId: string;
|
|
14
14
|
/** OAuth client secret */
|
|
15
15
|
clientSecret: string;
|
|
16
|
+
/** Client authentication method for token endpoint (default: 'client_secret_post') */
|
|
17
|
+
tokenEndpointAuthMethod?: 'client_secret_post' | 'client_secret_basic';
|
|
16
18
|
/** HTTP request timeout in milliseconds (default: 10000) */
|
|
17
19
|
timeout?: number;
|
|
18
20
|
/** Discovery document cache TTL in milliseconds (default: 3600000 = 1 hour) */
|
|
@@ -147,6 +149,18 @@ declare class NyaAccountClient {
|
|
|
147
149
|
private readonly discoveryCacheTtl;
|
|
148
150
|
private jwtVerifier;
|
|
149
151
|
constructor(config: NyaAccountConfig);
|
|
152
|
+
/**
|
|
153
|
+
* 根据配置的认证方式返回请求体中的凭据参数
|
|
154
|
+
* client_secret_post: 凭据放入请求体
|
|
155
|
+
* client_secret_basic: 不放入请求体(通过 header 传递)
|
|
156
|
+
*/
|
|
157
|
+
private getClientAuthBody;
|
|
158
|
+
/**
|
|
159
|
+
* 根据配置的认证方式返回请求头
|
|
160
|
+
* client_secret_basic: Authorization: Basic base64(client_id:client_secret)
|
|
161
|
+
* client_secret_post: 不添加额外头
|
|
162
|
+
*/
|
|
163
|
+
private getClientAuthHeaders;
|
|
150
164
|
/**
|
|
151
165
|
* Fetch the OIDC Discovery document. Results are cached with a configurable TTL.
|
|
152
166
|
*/
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/core/types.d.ts","../src/client.d.ts","../src/core/errors.d.ts","../src/utils/pkce.d.ts"],"sourcesContent":null,"mappings":";;;;AAEA,IAAW,mBAAmB,CAAC,IAAG,MAAA,cAAA;AAClC,IAAM,iBAAA,CAAA,EAAA;AACN,IAAW,gBAAgB,CAAC,EAAG;AAC/B,IAAK,WAAA,CAAA,EAAA;AACL,IAAW,wBAAwB,CAAC,EAAG;AACvC,IAAM,oBAAA,CAAA,EAAA;AACN,IAAW,gCAAQ,CAAA,EAAA;AACnB,IAAW,kCAAc,CAAA,IAAA,MAAA,6BAAA;AACzB,IAAW,iCAAS,CAAA,EAAA;AACpB,IAAW,yBAAkB,CAAA,EAAA;AAC7B,IAAW,6BAAa,CAAA,EAAA;AACxB,IAAW,sBAAsB,CAAC,EAAG;AACrC,IAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/core/types.d.ts","../src/client.d.ts","../src/core/errors.d.ts","../src/utils/pkce.d.ts"],"sourcesContent":null,"mappings":";;;;AAEA,IAAW,mBAAmB,CAAC,IAAG,MAAA,cAAA;AAClC,IAAM,iBAAA,CAAA,EAAA;AACN,IAAW,gBAAgB,CAAC,EAAG;AAC/B,IAAK,WAAA,CAAA,EAAA;AACL,IAAW,wBAAwB,CAAC,EAAG;AACvC,IAAM,oBAAA,CAAA,EAAA;AACN,IAAW,gCAAQ,CAAA,EAAA;AACnB,IAAW,kCAAc,CAAA,IAAA,MAAA,6BAAA;AACzB,IAAW,iCAAS,CAAA,EAAA;AACpB,IAAW,yBAAkB,CAAA,EAAA;AAC7B,IAAW,6BAAa,CAAA,EAAA;AACxB,IAAW,sBAAsB,CAAC,EAAG;AACrC,IAAW,sBAAsB,CAAC,EAAG;AACrC,IAAW,WAAW,CAAC,EAAG;;;;ACZ1B,IAAW,mBAAmB;CAAC;CAAI,MAAG;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;CAAA,MAAA;AAAA;;;;;;;ACAtC,IAAW,kBAAkB,CAAC,IAAI,MAAM,KAAM;;;;AAI9C,IAAA,aAAA,CAAA,IAAA,MAAA,eAAA;;;;AAIA,IAAW,yBAAyB,CAAC,IAAI,MAAM,eAAS;;;;AAIxD,IAAW,iBAAc,CAAA,IAAA,MAAA,eAAA;;;;;;;ACXzB,IAAW,uBAAuB,CAAC,EAAG;;;;AAItC,IAAW,wBAAwB,CAAC,EAAG;;;;AAIvC,IAAW,eAAe,CAAC,IAAI,MAAM,QAAS"}
|
package/dist/index.js
CHANGED
|
@@ -272,7 +272,43 @@ var NyaAccountClient = class {
|
|
|
272
272
|
this.httpClient = axios.create({ timeout: config.timeout ?? 1e4 });
|
|
273
273
|
}
|
|
274
274
|
/**
|
|
275
|
+
|
|
276
|
+
* 根据配置的认证方式返回请求体中的凭据参数
|
|
277
|
+
|
|
278
|
+
* client_secret_post: 凭据放入请求体
|
|
279
|
+
|
|
280
|
+
* client_secret_basic: 不放入请求体(通过 header 传递)
|
|
281
|
+
|
|
282
|
+
*/
|
|
283
|
+
getClientAuthBody() {
|
|
284
|
+
if (this.config.tokenEndpointAuthMethod === "client_secret_basic") return {};
|
|
285
|
+
return {
|
|
286
|
+
client_id: this.config.clientId,
|
|
287
|
+
client_secret: this.config.clientSecret
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
|
|
292
|
+
* 根据配置的认证方式返回请求头
|
|
293
|
+
|
|
294
|
+
* client_secret_basic: Authorization: Basic base64(client_id:client_secret)
|
|
295
|
+
|
|
296
|
+
* client_secret_post: 不添加额外头
|
|
297
|
+
|
|
298
|
+
*/
|
|
299
|
+
getClientAuthHeaders() {
|
|
300
|
+
if (this.config.tokenEndpointAuthMethod === "client_secret_basic") {
|
|
301
|
+
const encodedId = encodeURIComponent(this.config.clientId);
|
|
302
|
+
const encodedSecret = encodeURIComponent(this.config.clientSecret);
|
|
303
|
+
const credentials = Buffer.from(`${encodedId}:${encodedSecret}`).toString("base64");
|
|
304
|
+
return { Authorization: `Basic ${credentials}` };
|
|
305
|
+
}
|
|
306
|
+
return {};
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
|
|
275
310
|
* Fetch the OIDC Discovery document. Results are cached with a configurable TTL.
|
|
311
|
+
|
|
276
312
|
*/
|
|
277
313
|
async discover() {
|
|
278
314
|
if (this.discoveryCache && Date.now() - this.discoveryCacheTimestamp < this.discoveryCacheTtl) return this.discoveryCache;
|
|
@@ -308,7 +344,9 @@ var NyaAccountClient = class {
|
|
|
308
344
|
}
|
|
309
345
|
}
|
|
310
346
|
/**
|
|
347
|
+
|
|
311
348
|
* Clear the cached Discovery document and JWT verifier, forcing a re-fetch on next use.
|
|
349
|
+
|
|
312
350
|
*/
|
|
313
351
|
clearCache() {
|
|
314
352
|
this.discoveryCache = null;
|
|
@@ -316,10 +354,15 @@ var NyaAccountClient = class {
|
|
|
316
354
|
this.jwtVerifier = null;
|
|
317
355
|
}
|
|
318
356
|
/**
|
|
357
|
+
|
|
319
358
|
* Create an OAuth authorization URL (automatically includes PKCE).
|
|
359
|
+
|
|
320
360
|
*
|
|
361
|
+
|
|
321
362
|
* The returned `codeVerifier` and `state` must be saved to the session
|
|
363
|
+
|
|
322
364
|
* for later use in token exchange and CSRF validation.
|
|
365
|
+
|
|
323
366
|
*/
|
|
324
367
|
async createAuthorizationUrl(options) {
|
|
325
368
|
const authorizationEndpoint = await this.resolveEndpoint("authorization");
|
|
@@ -343,9 +386,13 @@ var NyaAccountClient = class {
|
|
|
343
386
|
};
|
|
344
387
|
}
|
|
345
388
|
/**
|
|
389
|
+
|
|
346
390
|
* Push authorization parameters to PAR endpoint (RFC 9126).
|
|
391
|
+
|
|
347
392
|
*
|
|
393
|
+
|
|
348
394
|
* Returns a `request_uri` that can be used in the authorization endpoint.
|
|
395
|
+
|
|
349
396
|
*/
|
|
350
397
|
async pushAuthorizationRequest(options) {
|
|
351
398
|
const parEndpoint = await this.resolveEndpoint("pushedAuthorizationRequest");
|
|
@@ -353,8 +400,7 @@ var NyaAccountClient = class {
|
|
|
353
400
|
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
354
401
|
const state = options.state ?? randomBytes(16).toString("base64url");
|
|
355
402
|
const payload = {
|
|
356
|
-
|
|
357
|
-
client_secret: this.config.clientSecret,
|
|
403
|
+
...this.getClientAuthBody(),
|
|
358
404
|
redirect_uri: options.redirectUri,
|
|
359
405
|
response_type: "code",
|
|
360
406
|
scope: options.scope ?? "openid",
|
|
@@ -365,7 +411,7 @@ var NyaAccountClient = class {
|
|
|
365
411
|
if (options.nonce) payload.nonce = options.nonce;
|
|
366
412
|
if (options.request) payload.request = options.request;
|
|
367
413
|
try {
|
|
368
|
-
const response = await this.httpClient.post(parEndpoint, payload);
|
|
414
|
+
const response = await this.httpClient.post(parEndpoint, payload, { headers: this.getClientAuthHeaders() });
|
|
369
415
|
const raw = PushedAuthorizationResponseSchema.parse(response.data);
|
|
370
416
|
return {
|
|
371
417
|
requestUri: raw.request_uri,
|
|
@@ -378,7 +424,9 @@ var NyaAccountClient = class {
|
|
|
378
424
|
}
|
|
379
425
|
}
|
|
380
426
|
/**
|
|
427
|
+
|
|
381
428
|
* Create an authorization URL using PAR `request_uri`.
|
|
429
|
+
|
|
382
430
|
*/
|
|
383
431
|
async createAuthorizationUrlWithPar(options) {
|
|
384
432
|
const authorizationEndpoint = await this.resolveEndpoint("authorization");
|
|
@@ -396,7 +444,9 @@ var NyaAccountClient = class {
|
|
|
396
444
|
};
|
|
397
445
|
}
|
|
398
446
|
/**
|
|
447
|
+
|
|
399
448
|
* Create OIDC RP-Initiated Logout URL (`end_session_endpoint`).
|
|
449
|
+
|
|
400
450
|
*/
|
|
401
451
|
async createEndSessionUrl(options) {
|
|
402
452
|
const endSessionEndpoint = await this.resolveEndpoint("endSession");
|
|
@@ -408,7 +458,9 @@ var NyaAccountClient = class {
|
|
|
408
458
|
return `${endSessionEndpoint}?${params.toString()}`;
|
|
409
459
|
}
|
|
410
460
|
/**
|
|
461
|
+
|
|
411
462
|
* Exchange an authorization code for tokens (Authorization Code Grant).
|
|
463
|
+
|
|
412
464
|
*/
|
|
413
465
|
async exchangeCode(options) {
|
|
414
466
|
const tokenEndpoint = await this.resolveEndpoint("token");
|
|
@@ -417,17 +469,18 @@ var NyaAccountClient = class {
|
|
|
417
469
|
grant_type: "authorization_code",
|
|
418
470
|
code: options.code,
|
|
419
471
|
redirect_uri: options.redirectUri,
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
});
|
|
472
|
+
code_verifier: options.codeVerifier,
|
|
473
|
+
...this.getClientAuthBody()
|
|
474
|
+
}, { headers: this.getClientAuthHeaders() });
|
|
424
475
|
return this.mapTokenResponse(response.data);
|
|
425
476
|
} catch (error) {
|
|
426
477
|
throw this.handleTokenError(error);
|
|
427
478
|
}
|
|
428
479
|
}
|
|
429
480
|
/**
|
|
481
|
+
|
|
430
482
|
* Refresh an Access Token using a Refresh Token.
|
|
483
|
+
|
|
431
484
|
*/
|
|
432
485
|
async refreshToken(refreshToken) {
|
|
433
486
|
const tokenEndpoint = await this.resolveEndpoint("token");
|
|
@@ -435,19 +488,23 @@ var NyaAccountClient = class {
|
|
|
435
488
|
const response = await this.httpClient.post(tokenEndpoint, {
|
|
436
489
|
grant_type: "refresh_token",
|
|
437
490
|
refresh_token: refreshToken,
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
});
|
|
491
|
+
...this.getClientAuthBody()
|
|
492
|
+
}, { headers: this.getClientAuthHeaders() });
|
|
441
493
|
return this.mapTokenResponse(response.data);
|
|
442
494
|
} catch (error) {
|
|
443
495
|
throw this.handleTokenError(error);
|
|
444
496
|
}
|
|
445
497
|
}
|
|
446
498
|
/**
|
|
499
|
+
|
|
447
500
|
* Revoke a token (RFC 7009).
|
|
501
|
+
|
|
448
502
|
*
|
|
503
|
+
|
|
449
504
|
* Supports revoking Access Tokens or Refresh Tokens.
|
|
505
|
+
|
|
450
506
|
* Revoking a Refresh Token also revokes its entire token family.
|
|
507
|
+
|
|
451
508
|
*/
|
|
452
509
|
async revokeToken(token, options) {
|
|
453
510
|
const revocationEndpoint = await this.resolveEndpoint("revocation");
|
|
@@ -456,15 +513,18 @@ var NyaAccountClient = class {
|
|
|
456
513
|
await this.httpClient.post(revocationEndpoint, {
|
|
457
514
|
token,
|
|
458
515
|
token_type_hint: tokenTypeHint,
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
});
|
|
516
|
+
...this.getClientAuthBody()
|
|
517
|
+
}, { headers: this.getClientAuthHeaders() });
|
|
462
518
|
} catch {}
|
|
463
519
|
}
|
|
464
520
|
/**
|
|
521
|
+
|
|
465
522
|
* Token introspection (RFC 7662).
|
|
523
|
+
|
|
466
524
|
*
|
|
525
|
+
|
|
467
526
|
* Query the server for the current state of a token (active status, associated user info, etc.).
|
|
527
|
+
|
|
468
528
|
*/
|
|
469
529
|
async introspectToken(token, options) {
|
|
470
530
|
const introspectionEndpoint = await this.resolveEndpoint("introspection");
|
|
@@ -473,9 +533,8 @@ var NyaAccountClient = class {
|
|
|
473
533
|
const response = await this.httpClient.post(introspectionEndpoint, {
|
|
474
534
|
token,
|
|
475
535
|
token_type_hint: tokenTypeHint,
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
});
|
|
536
|
+
...this.getClientAuthBody()
|
|
537
|
+
}, { headers: this.getClientAuthHeaders() });
|
|
479
538
|
const raw = IntrospectionResponseSchema.parse(response.data);
|
|
480
539
|
return {
|
|
481
540
|
active: raw.active,
|
|
@@ -497,11 +556,17 @@ var NyaAccountClient = class {
|
|
|
497
556
|
}
|
|
498
557
|
}
|
|
499
558
|
/**
|
|
559
|
+
|
|
500
560
|
* Get user info using an Access Token (OIDC UserInfo Endpoint).
|
|
561
|
+
|
|
501
562
|
*
|
|
563
|
+
|
|
502
564
|
* The returned fields depend on the scopes included in the token:
|
|
565
|
+
|
|
503
566
|
* - `profile`: name, picture, updatedAt
|
|
567
|
+
|
|
504
568
|
* - `email`: email, emailVerified
|
|
569
|
+
|
|
505
570
|
*/
|
|
506
571
|
async getUserInfo(accessToken) {
|
|
507
572
|
const userinfoEndpoint = await this.resolveEndpoint("userinfo");
|
|
@@ -521,46 +586,77 @@ var NyaAccountClient = class {
|
|
|
521
586
|
}
|
|
522
587
|
}
|
|
523
588
|
/**
|
|
589
|
+
|
|
524
590
|
* Locally verify a JWT Access Token (RFC 9068).
|
|
591
|
+
|
|
525
592
|
*
|
|
593
|
+
|
|
526
594
|
* Uses remote JWKS for signature verification, and validates issuer, audience, expiry, etc.
|
|
595
|
+
|
|
527
596
|
*
|
|
597
|
+
|
|
528
598
|
* @param token JWT Access Token string
|
|
599
|
+
|
|
529
600
|
* @param options.audience Custom audience validation value (defaults to clientId)
|
|
601
|
+
|
|
530
602
|
*/
|
|
531
603
|
async verifyAccessToken(token, options) {
|
|
532
604
|
const verifier = await this.getJwtVerifier();
|
|
533
605
|
return verifier.verifyAccessToken(token, options?.audience);
|
|
534
606
|
}
|
|
535
607
|
/**
|
|
608
|
+
|
|
536
609
|
* Locally verify an OIDC ID Token.
|
|
610
|
+
|
|
537
611
|
*
|
|
612
|
+
|
|
538
613
|
* @param token JWT ID Token string
|
|
614
|
+
|
|
539
615
|
* @param options.audience Custom audience validation value (defaults to clientId)
|
|
616
|
+
|
|
540
617
|
* @param options.nonce Validate the nonce claim (required if nonce was sent during authorization)
|
|
618
|
+
|
|
541
619
|
*/
|
|
542
620
|
async verifyIdToken(token, options) {
|
|
543
621
|
const verifier = await this.getJwtVerifier();
|
|
544
622
|
return verifier.verifyIdToken(token, options);
|
|
545
623
|
}
|
|
546
624
|
/**
|
|
625
|
+
|
|
547
626
|
* Express middleware: verify the Bearer Token in the request.
|
|
627
|
+
|
|
548
628
|
*
|
|
629
|
+
|
|
549
630
|
* After successful verification, use `getAuth(req)` to retrieve the token payload.
|
|
631
|
+
|
|
550
632
|
*
|
|
633
|
+
|
|
551
634
|
* @param options.strategy Verification strategy: 'local' (default, JWT local verification) or 'introspection' (remote introspection)
|
|
635
|
+
|
|
552
636
|
*
|
|
637
|
+
|
|
553
638
|
* @example
|
|
639
|
+
|
|
554
640
|
* ```typescript
|
|
641
|
+
|
|
555
642
|
* import { getAuth } from '@nya-account/node-sdk/express'
|
|
643
|
+
|
|
556
644
|
*
|
|
645
|
+
|
|
557
646
|
* app.use('/api', client.authenticate())
|
|
647
|
+
|
|
558
648
|
*
|
|
649
|
+
|
|
559
650
|
* app.get('/api/me', (req, res) => {
|
|
651
|
+
|
|
560
652
|
* const auth = getAuth(req)
|
|
653
|
+
|
|
561
654
|
* res.json({ userId: auth?.sub })
|
|
655
|
+
|
|
562
656
|
* })
|
|
657
|
+
|
|
563
658
|
* ```
|
|
659
|
+
|
|
564
660
|
*/
|
|
565
661
|
authenticate(options) {
|
|
566
662
|
const strategy = options?.strategy ?? "local";
|
|
@@ -605,18 +701,31 @@ var NyaAccountClient = class {
|
|
|
605
701
|
};
|
|
606
702
|
}
|
|
607
703
|
/**
|
|
704
|
+
|
|
608
705
|
* Express middleware: validate that the token in the request contains the specified scopes.
|
|
706
|
+
|
|
609
707
|
*
|
|
708
|
+
|
|
610
709
|
* Must be used after the `authenticate()` middleware.
|
|
710
|
+
|
|
611
711
|
*
|
|
712
|
+
|
|
612
713
|
* @example
|
|
714
|
+
|
|
613
715
|
* ```typescript
|
|
716
|
+
|
|
614
717
|
* app.get('/api/profile',
|
|
718
|
+
|
|
615
719
|
* client.authenticate(),
|
|
720
|
+
|
|
616
721
|
* client.requireScopes('profile'),
|
|
722
|
+
|
|
617
723
|
* (req, res) => { ... }
|
|
724
|
+
|
|
618
725
|
* )
|
|
726
|
+
|
|
619
727
|
* ```
|
|
728
|
+
|
|
620
729
|
*/
|
|
621
730
|
requireScopes(...scopes) {
|
|
622
731
|
return (req, res, next) => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["code: string","description: string","error: string","errorDescription: string","codeVerifier: string","jwksUri: string","issuer: string","defaultAudience: string","token: string","audience?: string","options?: { audience?: string; nonce?: string }","error: unknown","tokenType: string","DISCOVERY_ENDPOINT_MAP: Record<EndpointName, keyof DiscoveryDocument>","config: NyaAccountConfig","options: CreateAuthorizationUrlOptions","options: PushAuthorizationRequestOptions","payload: Record<string, string>","options?: CreateEndSessionUrlOptions","options: ExchangeCodeOptions","refreshToken: string","token: string","options?: { tokenTypeHint?: 'access_token' | 'refresh_token' }","accessToken: string","options?: { audience?: string }","options?: { audience?: string; nonce?: string }","options?: AuthenticateOptions","req: Request","res: Response","next: NextFunction","payload: AccessTokenPayload","name: EndpointName","data: unknown","error: unknown"],"sources":["../src/core/schemas.ts","../src/core/errors.ts","../src/utils/pkce.ts","../src/utils/jwt.ts","../src/client.ts"],"sourcesContent":["import { z } from 'zod'\n\nexport const TokenTypeHintSchema = z.enum(['access_token', 'refresh_token'])\n\n// ============================================================\n// OAuth Token Response\n// ============================================================\n\nexport const TokenResponseSchema = z.object({\n access_token: z.string(),\n token_type: z.string(),\n expires_in: z.number(),\n refresh_token: z.string(),\n scope: z.string(),\n id_token: z.string().optional()\n})\n\nexport type TokenResponseRaw = z.infer<typeof TokenResponseSchema>\n\n// ============================================================\n// OAuth Error Response\n// ============================================================\n\nexport const OAuthErrorSchema = z.object({\n error: z.string(),\n error_description: z.string().optional()\n})\n\nexport type OAuthErrorRaw = z.infer<typeof OAuthErrorSchema>\n\n// ============================================================\n// Token Introspection Response (RFC 7662)\n// ============================================================\n\nexport const IntrospectionResponseSchema = z.object({\n active: z.boolean(),\n scope: z.string().optional(),\n client_id: z.string().optional(),\n username: z.string().optional(),\n token_type: z.string().optional(),\n exp: z.number().optional(),\n iat: z.number().optional(),\n sub: z.string().optional(),\n aud: z.string().optional(),\n iss: z.string().optional(),\n jti: z.string().optional(),\n sid: z.string().optional(),\n sv: z.number().optional()\n})\n\nexport type IntrospectionResponseRaw = z.infer<typeof IntrospectionResponseSchema>\n\n// ============================================================\n// OIDC UserInfo Response\n// ============================================================\n\nexport const UserInfoSchema = z.object({\n sub: z.string(),\n name: z.string().optional(),\n picture: z.string().optional(),\n email: z.string().optional(),\n email_verified: z.boolean().optional(),\n updated_at: z.number().optional()\n})\n\nexport type UserInfoRaw = z.infer<typeof UserInfoSchema>\n\n// ============================================================\n// OIDC Discovery Document\n// ============================================================\n\nexport const DiscoveryDocumentSchema = z.object({\n issuer: z.string(),\n authorization_endpoint: z.string(),\n token_endpoint: z.string(),\n userinfo_endpoint: z.string().optional(),\n jwks_uri: z.string(),\n revocation_endpoint: z.string().optional(),\n introspection_endpoint: z.string().optional(),\n pushed_authorization_request_endpoint: z.string().optional(),\n end_session_endpoint: z.string().optional(),\n response_types_supported: z.array(z.string()),\n grant_types_supported: z.array(z.string()),\n id_token_signing_alg_values_supported: z.array(z.string()),\n scopes_supported: z.array(z.string()),\n subject_types_supported: z.array(z.string()),\n token_endpoint_auth_methods_supported: z.array(z.string()),\n code_challenge_methods_supported: z.array(z.string()).optional(),\n claims_supported: z.array(z.string()).optional(),\n dpop_signing_alg_values_supported: z.array(z.string()).optional(),\n request_parameter_supported: z.boolean().optional(),\n request_uri_parameter_supported: z.boolean().optional()\n})\n\nexport type DiscoveryDocumentRaw = z.infer<typeof DiscoveryDocumentSchema>\n\nexport const PushedAuthorizationResponseSchema = z.object({\n request_uri: z.string().min(1),\n expires_in: z.number().int().positive()\n})\n\nexport type PushedAuthorizationResponseRaw = z.infer<\n typeof PushedAuthorizationResponseSchema\n>\n\n// ============================================================\n// JWT Access Token Payload (RFC 9068)\n// ============================================================\n\nexport const AccessTokenPayloadSchema = z.object({\n iss: z.string(),\n sub: z.string(),\n aud: z.string(),\n scope: z.string(),\n ver: z.string(),\n sid: z.string(),\n sv: z.number().int().nonnegative(),\n iat: z.number(),\n exp: z.number(),\n jti: z.string(),\n cnf: z.object({ jkt: z.string() }).optional()\n})\n\nexport type AccessTokenPayload = z.infer<typeof AccessTokenPayloadSchema>\n\n// ============================================================\n// JWT ID Token Payload (OIDC Core)\n// ============================================================\n\nexport const IdTokenPayloadSchema = z.object({\n iss: z.string(),\n sub: z.string(),\n aud: z.string(),\n sid: z.string().optional(),\n iat: z.number(),\n exp: z.number(),\n nonce: z.string().optional(),\n name: z.string().optional(),\n email: z.string().optional(),\n email_verified: z.boolean().optional(),\n updated_at: z.number().optional()\n})\n\nexport type IdTokenPayload = z.infer<typeof IdTokenPayloadSchema>\n","/**\r\n * Base error class for the SDK.\r\n */\r\nexport class NyaAccountError extends Error {\r\n readonly code: string\r\n readonly description: string\r\n\r\n constructor(code: string, description: string) {\r\n super(`[${code}] ${description}`)\r\n this.name = 'NyaAccountError'\r\n this.code = code\r\n this.description = description\r\n }\r\n}\r\n\r\n/**\r\n * OAuth protocol error (from server error / error_description response).\r\n */\r\nexport class OAuthError extends NyaAccountError {\r\n constructor(error: string, errorDescription: string) {\r\n super(error, errorDescription)\r\n this.name = 'OAuthError'\r\n }\r\n}\r\n\r\n/**\r\n * JWT verification error.\r\n */\r\nexport class TokenVerificationError extends NyaAccountError {\r\n constructor(description: string) {\r\n super('token_verification_failed', description)\r\n this.name = 'TokenVerificationError'\r\n }\r\n}\r\n\r\n/**\r\n * OIDC Discovery error.\r\n */\r\nexport class DiscoveryError extends NyaAccountError {\r\n constructor(description: string) {\r\n super('discovery_error', description)\r\n this.name = 'DiscoveryError'\r\n }\r\n}\r\n","import { randomBytes, createHash } from 'node:crypto'\r\nimport type { PkcePair } from '@/core/types'\r\n\r\n/**\r\n * Generate a PKCE code_verifier (43-128 character random string).\r\n */\r\nexport function generateCodeVerifier(): string {\r\n return randomBytes(32).toString('base64url')\r\n}\r\n\r\n/**\r\n * Generate an S256 code_challenge from a code_verifier.\r\n */\r\nexport function generateCodeChallenge(codeVerifier: string): string {\r\n return createHash('sha256').update(codeVerifier).digest('base64url')\r\n}\r\n\r\n/**\r\n * Generate a PKCE code_verifier and code_challenge pair.\r\n */\r\nexport function generatePkce(): PkcePair {\r\n const codeVerifier = generateCodeVerifier()\r\n const codeChallenge = generateCodeChallenge(codeVerifier)\r\n return { codeVerifier, codeChallenge }\r\n}\r\n","import { createRemoteJWKSet, jwtVerify, errors as joseErrors } from 'jose'\r\nimport {\r\n AccessTokenPayloadSchema,\r\n IdTokenPayloadSchema,\r\n type AccessTokenPayload,\r\n type IdTokenPayload,\r\n} from '@/core/schemas'\r\nimport { TokenVerificationError } from '@/core/errors'\r\n\r\n/**\r\n * JWT verifier using remote JWKS for signature verification.\r\n */\r\nexport class JwtVerifier {\r\n private jwks: ReturnType<typeof createRemoteJWKSet>\r\n private issuer: string\r\n private defaultAudience: string\r\n\r\n constructor(jwksUri: string, issuer: string, defaultAudience: string) {\r\n this.jwks = createRemoteJWKSet(new URL(jwksUri))\r\n this.issuer = issuer\r\n this.defaultAudience = defaultAudience\r\n }\r\n\r\n /**\r\n * Verify an OAuth 2.0 Access Token (JWT, RFC 9068).\r\n */\r\n async verifyAccessToken(token: string, audience?: string): Promise<AccessTokenPayload> {\r\n try {\r\n const { payload } = await jwtVerify(token, this.jwks, {\r\n algorithms: ['RS256'],\r\n issuer: this.issuer,\r\n audience: audience ?? this.defaultAudience,\r\n })\r\n return AccessTokenPayloadSchema.parse(payload)\r\n } catch (error) {\r\n throw this.wrapError(error, 'Access Token')\r\n }\r\n }\r\n\r\n /**\r\n * Verify an OIDC ID Token.\r\n */\r\n async verifyIdToken(token: string, options?: { audience?: string; nonce?: string }): Promise<IdTokenPayload> {\r\n try {\r\n const { payload } = await jwtVerify(token, this.jwks, {\r\n algorithms: ['RS256'],\r\n issuer: this.issuer,\r\n audience: options?.audience ?? this.defaultAudience,\r\n })\r\n\r\n const parsed = IdTokenPayloadSchema.parse(payload)\r\n\r\n if (options?.nonce !== undefined && parsed.nonce !== options.nonce) {\r\n throw new TokenVerificationError('ID Token nonce mismatch')\r\n }\r\n\r\n return parsed\r\n } catch (error) {\r\n if (error instanceof TokenVerificationError) {\r\n throw error\r\n }\r\n throw this.wrapError(error, 'ID Token')\r\n }\r\n }\r\n\r\n private wrapError(error: unknown, tokenType: string): TokenVerificationError {\r\n if (error instanceof TokenVerificationError) {\r\n return error\r\n }\r\n if (error instanceof joseErrors.JWTExpired) {\r\n return new TokenVerificationError(`${tokenType} has expired`)\r\n }\r\n if (error instanceof joseErrors.JWTClaimValidationFailed) {\r\n return new TokenVerificationError(`${tokenType} claim validation failed: ${error.message}`)\r\n }\r\n if (error instanceof joseErrors.JWSSignatureVerificationFailed) {\r\n return new TokenVerificationError(`${tokenType} signature verification failed`)\r\n }\r\n const message = error instanceof Error ? error.message : 'Unknown error'\r\n return new TokenVerificationError(`${tokenType} verification failed: ${message}`)\r\n }\r\n}\r\n","import axios, { AxiosError, type AxiosInstance } from 'axios'\nimport type { Request, Response, NextFunction } from 'express'\nimport { randomBytes } from 'node:crypto'\nimport {\n TokenResponseSchema,\n OAuthErrorSchema,\n IntrospectionResponseSchema,\n UserInfoSchema,\n DiscoveryDocumentSchema,\n PushedAuthorizationResponseSchema,\n TokenTypeHintSchema,\n type AccessTokenPayload,\n type IdTokenPayload\n} from '@/core/schemas'\nimport type {\n NyaAccountConfig,\n TokenResponse,\n UserInfo,\n IntrospectionResponse,\n DiscoveryDocument,\n AuthorizationUrlResult,\n CreateAuthorizationUrlOptions,\n PushAuthorizationRequestOptions,\n PushAuthorizationRequestResult,\n CreateEndSessionUrlOptions,\n ExchangeCodeOptions,\n AuthenticateOptions,\n EndpointConfig\n} from '@/core/types'\nimport { OAuthError, DiscoveryError, NyaAccountError } from '@/core/errors'\nimport { generateCodeVerifier, generateCodeChallenge } from '@/utils/pkce'\nimport { JwtVerifier } from '@/utils/jwt'\nimport {\n setAuth,\n getAuth,\n extractBearerToken,\n sendOAuthError\n} from '@/middleware/express'\n\ntype EndpointName = keyof EndpointConfig\n\nconst DISCOVERY_ENDPOINT_MAP: Record<EndpointName, keyof DiscoveryDocument> = {\n authorization: 'authorizationEndpoint',\n pushedAuthorizationRequest: 'pushedAuthorizationRequestEndpoint',\n token: 'tokenEndpoint',\n userinfo: 'userinfoEndpoint',\n revocation: 'revocationEndpoint',\n introspection: 'introspectionEndpoint',\n jwks: 'jwksUri',\n endSession: 'endSessionEndpoint'\n}\n\n/** Default issuer URL */\nconst DEFAULT_ISSUER = 'https://account-api.edge.lolinya.net'\n\n/** Default discovery cache TTL: 1 hour */\nconst DEFAULT_DISCOVERY_CACHE_TTL = 3600000\n\n/**\n * Nya Account Node.js SDK client.\n *\n * Provides full OAuth 2.1 / OIDC flow support:\n * - Authorization Code + PKCE\n * - Token exchange / refresh / revocation / introspection\n * - Local JWT verification (via JWKS)\n * - OIDC UserInfo\n * - OIDC Discovery auto-discovery\n * - Express middleware (Bearer Token auth + scope validation)\n *\n * @example\n * ```typescript\n * const client = new NyaAccountClient({\n * issuer: 'https://account.example.com',\n * clientId: 'my-app',\n * clientSecret: 'my-secret',\n * })\n *\n * // Create authorization URL (with PKCE)\n * const { url, codeVerifier, state } = await client.createAuthorizationUrl({\n * redirectUri: 'https://myapp.com/callback',\n * scope: 'openid profile email',\n * })\n *\n * // Exchange code for tokens\n * const tokens = await client.exchangeCode({\n * code: callbackCode,\n * redirectUri: 'https://myapp.com/callback',\n * codeVerifier,\n * })\n *\n * // Get user info\n * const userInfo = await client.getUserInfo(tokens.accessToken)\n * ```\n */\ntype ResolvedConfig = NyaAccountConfig & { issuer: string }\n\nexport class NyaAccountClient {\n private httpClient: AxiosInstance\n private config: ResolvedConfig\n private discoveryCache: DiscoveryDocument | null = null\n private discoveryCacheTimestamp = 0\n private readonly discoveryCacheTtl: number\n private jwtVerifier: JwtVerifier | null = null\n\n constructor(config: NyaAccountConfig) {\n this.config = {\n ...config,\n issuer: config.issuer ?? DEFAULT_ISSUER\n }\n this.discoveryCacheTtl = config.discoveryCacheTtl ?? DEFAULT_DISCOVERY_CACHE_TTL\n this.httpClient = axios.create({\n timeout: config.timeout ?? 10000\n })\n }\n\n // ============================================================\n // OIDC Discovery\n // ============================================================\n\n /**\n * Fetch the OIDC Discovery document. Results are cached with a configurable TTL.\n */\n async discover(): Promise<DiscoveryDocument> {\n if (\n this.discoveryCache &&\n Date.now() - this.discoveryCacheTimestamp < this.discoveryCacheTtl\n ) {\n return this.discoveryCache\n }\n\n try {\n const url = `${this.config.issuer}/.well-known/openid-configuration`\n const response = await this.httpClient.get(url)\n const raw = DiscoveryDocumentSchema.parse(response.data)\n\n this.discoveryCache = {\n issuer: raw.issuer,\n authorizationEndpoint: raw.authorization_endpoint,\n tokenEndpoint: raw.token_endpoint,\n userinfoEndpoint: raw.userinfo_endpoint,\n jwksUri: raw.jwks_uri,\n revocationEndpoint: raw.revocation_endpoint,\n introspectionEndpoint: raw.introspection_endpoint,\n pushedAuthorizationRequestEndpoint:\n raw.pushed_authorization_request_endpoint,\n endSessionEndpoint: raw.end_session_endpoint,\n responseTypesSupported: raw.response_types_supported,\n grantTypesSupported: raw.grant_types_supported,\n idTokenSigningAlgValuesSupported:\n raw.id_token_signing_alg_values_supported,\n scopesSupported: raw.scopes_supported,\n subjectTypesSupported: raw.subject_types_supported,\n tokenEndpointAuthMethodsSupported:\n raw.token_endpoint_auth_methods_supported,\n codeChallengeMethodsSupported: raw.code_challenge_methods_supported,\n claimsSupported: raw.claims_supported\n }\n this.discoveryCacheTimestamp = Date.now()\n\n return this.discoveryCache\n } catch (error) {\n if (error instanceof DiscoveryError) throw error\n const message = error instanceof Error ? error.message : 'Unknown error'\n throw new DiscoveryError(\n `Failed to fetch OIDC Discovery document: ${message}`\n )\n }\n }\n\n /**\n * Clear the cached Discovery document and JWT verifier, forcing a re-fetch on next use.\n */\n clearCache(): void {\n this.discoveryCache = null\n this.discoveryCacheTimestamp = 0\n this.jwtVerifier = null\n }\n\n // ============================================================\n // Authorization URL\n // ============================================================\n\n /**\n * Create an OAuth authorization URL (automatically includes PKCE).\n *\n * The returned `codeVerifier` and `state` must be saved to the session\n * for later use in token exchange and CSRF validation.\n */\n async createAuthorizationUrl(\n options: CreateAuthorizationUrlOptions\n ): Promise<AuthorizationUrlResult> {\n const authorizationEndpoint = await this.resolveEndpoint('authorization')\n\n const codeVerifier = generateCodeVerifier()\n const codeChallenge = generateCodeChallenge(codeVerifier)\n const state = options.state ?? randomBytes(16).toString('base64url')\n\n const params = new URLSearchParams({\n client_id: this.config.clientId,\n redirect_uri: options.redirectUri,\n response_type: 'code',\n scope: options.scope ?? 'openid',\n state,\n code_challenge: codeChallenge,\n code_challenge_method: 'S256'\n })\n\n if (options.nonce) {\n params.set('nonce', options.nonce)\n }\n\n return {\n url: `${authorizationEndpoint}?${params.toString()}`,\n codeVerifier,\n state\n }\n }\n\n /**\n * Push authorization parameters to PAR endpoint (RFC 9126).\n *\n * Returns a `request_uri` that can be used in the authorization endpoint.\n */\n async pushAuthorizationRequest(\n options: PushAuthorizationRequestOptions\n ): Promise<PushAuthorizationRequestResult> {\n const parEndpoint = await this.resolveEndpoint('pushedAuthorizationRequest')\n\n const codeVerifier = generateCodeVerifier()\n const codeChallenge = generateCodeChallenge(codeVerifier)\n const state = options.state ?? randomBytes(16).toString('base64url')\n\n const payload: Record<string, string> = {\n client_id: this.config.clientId,\n client_secret: this.config.clientSecret,\n redirect_uri: options.redirectUri,\n response_type: 'code',\n scope: options.scope ?? 'openid',\n state,\n code_challenge: codeChallenge,\n code_challenge_method: 'S256'\n }\n\n if (options.nonce) {\n payload.nonce = options.nonce\n }\n if (options.request) {\n payload.request = options.request\n }\n\n try {\n const response = await this.httpClient.post(parEndpoint, payload)\n const raw = PushedAuthorizationResponseSchema.parse(response.data)\n\n return {\n requestUri: raw.request_uri,\n expiresIn: raw.expires_in,\n codeVerifier,\n state\n }\n } catch (error) {\n throw this.handleTokenError(error)\n }\n }\n\n /**\n * Create an authorization URL using PAR `request_uri`.\n */\n async createAuthorizationUrlWithPar(\n options: PushAuthorizationRequestOptions\n ): Promise<AuthorizationUrlResult & { requestUri: string; expiresIn: number }> {\n const authorizationEndpoint = await this.resolveEndpoint('authorization')\n const pushed = await this.pushAuthorizationRequest(options)\n\n const params = new URLSearchParams({\n client_id: this.config.clientId,\n request_uri: pushed.requestUri\n })\n\n return {\n url: `${authorizationEndpoint}?${params.toString()}`,\n codeVerifier: pushed.codeVerifier,\n state: pushed.state,\n requestUri: pushed.requestUri,\n expiresIn: pushed.expiresIn\n }\n }\n\n /**\n * Create OIDC RP-Initiated Logout URL (`end_session_endpoint`).\n */\n async createEndSessionUrl(options?: CreateEndSessionUrlOptions): Promise<string> {\n const endSessionEndpoint = await this.resolveEndpoint('endSession')\n const params = new URLSearchParams()\n\n if (options?.idTokenHint) {\n params.set('id_token_hint', options.idTokenHint)\n }\n if (options?.postLogoutRedirectUri) {\n params.set('post_logout_redirect_uri', options.postLogoutRedirectUri)\n }\n if (options?.state) {\n params.set('state', options.state)\n }\n\n // 帮助服务端在未提供 id_token_hint 时识别客户端\n params.set('client_id', options?.clientId ?? this.config.clientId)\n\n return `${endSessionEndpoint}?${params.toString()}`\n }\n\n // ============================================================\n // Token Operations\n // ============================================================\n\n /**\n * Exchange an authorization code for tokens (Authorization Code Grant).\n */\n async exchangeCode(options: ExchangeCodeOptions): Promise<TokenResponse> {\n const tokenEndpoint = await this.resolveEndpoint('token')\n\n try {\n const response = await this.httpClient.post(tokenEndpoint, {\n grant_type: 'authorization_code',\n code: options.code,\n redirect_uri: options.redirectUri,\n client_id: this.config.clientId,\n client_secret: this.config.clientSecret,\n code_verifier: options.codeVerifier\n })\n\n return this.mapTokenResponse(response.data)\n } catch (error) {\n throw this.handleTokenError(error)\n }\n }\n\n /**\n * Refresh an Access Token using a Refresh Token.\n */\n async refreshToken(refreshToken: string): Promise<TokenResponse> {\n const tokenEndpoint = await this.resolveEndpoint('token')\n\n try {\n const response = await this.httpClient.post(tokenEndpoint, {\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: this.config.clientId,\n client_secret: this.config.clientSecret\n })\n\n return this.mapTokenResponse(response.data)\n } catch (error) {\n throw this.handleTokenError(error)\n }\n }\n\n /**\n * Revoke a token (RFC 7009).\n *\n * Supports revoking Access Tokens or Refresh Tokens.\n * Revoking a Refresh Token also revokes its entire token family.\n */\n async revokeToken(\n token: string,\n options?: { tokenTypeHint?: 'access_token' | 'refresh_token' }\n ): Promise<void> {\n const revocationEndpoint = await this.resolveEndpoint('revocation')\n\n try {\n const tokenTypeHint = options?.tokenTypeHint\n ? TokenTypeHintSchema.parse(options.tokenTypeHint)\n : undefined\n await this.httpClient.post(revocationEndpoint, {\n token,\n token_type_hint: tokenTypeHint,\n client_id: this.config.clientId,\n client_secret: this.config.clientSecret\n })\n } catch {\n // RFC 7009: revocation endpoint always returns 200, ignore errors\n }\n }\n\n /**\n * Token introspection (RFC 7662).\n *\n * Query the server for the current state of a token (active status, associated user info, etc.).\n */\n async introspectToken(\n token: string,\n options?: { tokenTypeHint?: 'access_token' | 'refresh_token' }\n ): Promise<IntrospectionResponse> {\n const introspectionEndpoint = await this.resolveEndpoint('introspection')\n\n try {\n const tokenTypeHint = options?.tokenTypeHint\n ? TokenTypeHintSchema.parse(options.tokenTypeHint)\n : undefined\n const response = await this.httpClient.post(introspectionEndpoint, {\n token,\n token_type_hint: tokenTypeHint,\n client_id: this.config.clientId,\n client_secret: this.config.clientSecret\n })\n\n const raw = IntrospectionResponseSchema.parse(response.data)\n return {\n active: raw.active,\n scope: raw.scope,\n clientId: raw.client_id,\n username: raw.username,\n tokenType: raw.token_type,\n exp: raw.exp,\n iat: raw.iat,\n sub: raw.sub,\n aud: raw.aud,\n iss: raw.iss,\n jti: raw.jti,\n sid: raw.sid,\n sv: raw.sv\n }\n } catch (error) {\n throw this.handleTokenError(error)\n }\n }\n\n // ============================================================\n // OIDC UserInfo\n // ============================================================\n\n /**\n * Get user info using an Access Token (OIDC UserInfo Endpoint).\n *\n * The returned fields depend on the scopes included in the token:\n * - `profile`: name, picture, updatedAt\n * - `email`: email, emailVerified\n */\n async getUserInfo(accessToken: string): Promise<UserInfo> {\n const userinfoEndpoint = await this.resolveEndpoint('userinfo')\n\n try {\n const response = await this.httpClient.get(userinfoEndpoint, {\n headers: { Authorization: `Bearer ${accessToken}` }\n })\n\n const raw = UserInfoSchema.parse(response.data)\n return {\n sub: raw.sub,\n name: raw.name,\n picture: raw.picture,\n email: raw.email,\n emailVerified: raw.email_verified,\n updatedAt: raw.updated_at\n }\n } catch (error) {\n throw this.handleTokenError(error)\n }\n }\n\n // ============================================================\n // Local JWT Verification\n // ============================================================\n\n /**\n * Locally verify a JWT Access Token (RFC 9068).\n *\n * Uses remote JWKS for signature verification, and validates issuer, audience, expiry, etc.\n *\n * @param token JWT Access Token string\n * @param options.audience Custom audience validation value (defaults to clientId)\n */\n async verifyAccessToken(\n token: string,\n options?: { audience?: string }\n ): Promise<AccessTokenPayload> {\n const verifier = await this.getJwtVerifier()\n return verifier.verifyAccessToken(token, options?.audience)\n }\n\n /**\n * Locally verify an OIDC ID Token.\n *\n * @param token JWT ID Token string\n * @param options.audience Custom audience validation value (defaults to clientId)\n * @param options.nonce Validate the nonce claim (required if nonce was sent during authorization)\n */\n async verifyIdToken(\n token: string,\n options?: { audience?: string; nonce?: string }\n ): Promise<IdTokenPayload> {\n const verifier = await this.getJwtVerifier()\n return verifier.verifyIdToken(token, options)\n }\n\n // ============================================================\n // Express Middleware\n // ============================================================\n\n /**\n * Express middleware: verify the Bearer Token in the request.\n *\n * After successful verification, use `getAuth(req)` to retrieve the token payload.\n *\n * @param options.strategy Verification strategy: 'local' (default, JWT local verification) or 'introspection' (remote introspection)\n *\n * @example\n * ```typescript\n * import { getAuth } from '@nya-account/node-sdk/express'\n *\n * app.use('/api', client.authenticate())\n *\n * app.get('/api/me', (req, res) => {\n * const auth = getAuth(req)\n * res.json({ userId: auth?.sub })\n * })\n * ```\n */\n authenticate(\n options?: AuthenticateOptions\n ): (req: Request, res: Response, next: NextFunction) => void {\n const strategy = options?.strategy ?? 'local'\n\n return (req: Request, res: Response, next: NextFunction) => {\n const token = extractBearerToken(req)\n if (!token) {\n sendOAuthError(\n res,\n 401,\n 'invalid_token',\n 'Missing Bearer token in Authorization header'\n )\n return\n }\n\n const handleVerification = async (): Promise<void> => {\n let payload: AccessTokenPayload\n\n if (strategy === 'introspection') {\n const introspection = await this.introspectToken(token)\n if (!introspection.active) {\n sendOAuthError(res, 401, 'invalid_token', 'Token is not active')\n return\n }\n const tokenType = introspection.tokenType?.toLowerCase()\n if (\n tokenType &&\n tokenType !== 'bearer' &&\n tokenType !== 'access_token'\n ) {\n sendOAuthError(\n res,\n 401,\n 'invalid_token',\n 'Token is not an access token'\n )\n return\n }\n payload = {\n iss: introspection.iss ?? '',\n sub: introspection.sub ?? '',\n aud: introspection.aud ?? '',\n scope: introspection.scope ?? '',\n ver: '1',\n iat: introspection.iat ?? 0,\n exp: introspection.exp ?? 0,\n jti: introspection.jti ?? '',\n sid: introspection.sid ?? '',\n sv: introspection.sv ?? 0\n }\n } else {\n payload = await this.verifyAccessToken(token)\n }\n\n setAuth(req, payload)\n next()\n }\n\n handleVerification().catch(() => {\n sendOAuthError(\n res,\n 401,\n 'invalid_token',\n 'Invalid or expired access token'\n )\n })\n }\n }\n\n /**\n * Express middleware: validate that the token in the request contains the specified scopes.\n *\n * Must be used after the `authenticate()` middleware.\n *\n * @example\n * ```typescript\n * app.get('/api/profile',\n * client.authenticate(),\n * client.requireScopes('profile'),\n * (req, res) => { ... }\n * )\n * ```\n */\n requireScopes(\n ...scopes: string[]\n ): (req: Request, res: Response, next: NextFunction) => void {\n return (req: Request, res: Response, next: NextFunction) => {\n const auth = getAuth(req)\n if (!auth) {\n sendOAuthError(res, 401, 'invalid_token', 'Request not authenticated')\n return\n }\n\n const tokenScopes = auth.scope.split(' ')\n const missingScopes = scopes.filter((s) => !tokenScopes.includes(s))\n\n if (missingScopes.length > 0) {\n sendOAuthError(\n res,\n 403,\n 'insufficient_scope',\n `Missing required scopes: ${missingScopes.join(' ')}`\n )\n return\n }\n\n next()\n }\n }\n\n // ============================================================\n // Private Methods\n // ============================================================\n\n private async resolveEndpoint(name: EndpointName): Promise<string> {\n const explicit = this.config.endpoints?.[name]\n if (explicit) {\n return explicit\n }\n\n const discovery = await this.discover()\n const discoveryKey = DISCOVERY_ENDPOINT_MAP[name]\n const endpoint = discovery[discoveryKey]\n\n if (!endpoint || typeof endpoint !== 'string') {\n throw new NyaAccountError(\n 'endpoint_not_found',\n `Endpoint '${name}' not found in Discovery document`\n )\n }\n\n return endpoint\n }\n\n private async getJwtVerifier(): Promise<JwtVerifier> {\n if (this.jwtVerifier) {\n return this.jwtVerifier\n }\n\n const jwksUri = await this.resolveEndpoint('jwks')\n const discovery = await this.discover()\n\n this.jwtVerifier = new JwtVerifier(\n jwksUri,\n discovery.issuer,\n this.config.clientId\n )\n return this.jwtVerifier\n }\n\n private mapTokenResponse(data: unknown): TokenResponse {\n const raw = TokenResponseSchema.parse(data)\n return {\n accessToken: raw.access_token,\n tokenType: raw.token_type,\n expiresIn: raw.expires_in,\n refreshToken: raw.refresh_token,\n scope: raw.scope,\n idToken: raw.id_token\n }\n }\n\n private handleTokenError(error: unknown): NyaAccountError {\n if (error instanceof NyaAccountError) {\n return error\n }\n\n if (error instanceof AxiosError && error.response) {\n const parsed = OAuthErrorSchema.safeParse(error.response.data)\n if (parsed.success) {\n return new OAuthError(\n parsed.data.error,\n parsed.data.error_description ?? 'Unknown error'\n )\n }\n }\n\n const message = error instanceof Error ? error.message : 'Unknown error'\n return new NyaAccountError('request_failed', `Request failed: ${message}`)\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["code: string","description: string","error: string","errorDescription: string","codeVerifier: string","jwksUri: string","issuer: string","defaultAudience: string","token: string","audience?: string","options?: { audience?: string; nonce?: string }","error: unknown","tokenType: string","DISCOVERY_ENDPOINT_MAP: Record<EndpointName, keyof DiscoveryDocument>","config: NyaAccountConfig","options: CreateAuthorizationUrlOptions","options: PushAuthorizationRequestOptions","payload: Record<string, string>","options?: CreateEndSessionUrlOptions","options: ExchangeCodeOptions","refreshToken: string","token: string","options?: { tokenTypeHint?: 'access_token' | 'refresh_token' }","accessToken: string","options?: { audience?: string }","options?: { audience?: string; nonce?: string }","options?: AuthenticateOptions","req: Request","res: Response","next: NextFunction","payload: AccessTokenPayload","name: EndpointName","data: unknown","error: unknown"],"sources":["../src/core/schemas.ts","../src/core/errors.ts","../src/utils/pkce.ts","../src/utils/jwt.ts","../src/client.ts"],"sourcesContent":["import { z } from 'zod'\r\n\r\nexport const TokenTypeHintSchema = z.enum(['access_token', 'refresh_token'])\r\n\r\n// ============================================================\r\n// OAuth Token Response\r\n// ============================================================\r\n\r\nexport const TokenResponseSchema = z.object({\r\n access_token: z.string(),\r\n token_type: z.string(),\r\n expires_in: z.number(),\r\n refresh_token: z.string(),\r\n scope: z.string(),\r\n id_token: z.string().optional()\r\n})\r\n\r\nexport type TokenResponseRaw = z.infer<typeof TokenResponseSchema>\r\n\r\n// ============================================================\r\n// OAuth Error Response\r\n// ============================================================\r\n\r\nexport const OAuthErrorSchema = z.object({\r\n error: z.string(),\r\n error_description: z.string().optional()\r\n})\r\n\r\nexport type OAuthErrorRaw = z.infer<typeof OAuthErrorSchema>\r\n\r\n// ============================================================\r\n// Token Introspection Response (RFC 7662)\r\n// ============================================================\r\n\r\nexport const IntrospectionResponseSchema = z.object({\r\n active: z.boolean(),\r\n scope: z.string().optional(),\r\n client_id: z.string().optional(),\r\n username: z.string().optional(),\r\n token_type: z.string().optional(),\r\n exp: z.number().optional(),\r\n iat: z.number().optional(),\r\n sub: z.string().optional(),\r\n aud: z.string().optional(),\r\n iss: z.string().optional(),\r\n jti: z.string().optional(),\r\n sid: z.string().optional(),\r\n sv: z.number().optional()\r\n})\r\n\r\nexport type IntrospectionResponseRaw = z.infer<typeof IntrospectionResponseSchema>\r\n\r\n// ============================================================\r\n// OIDC UserInfo Response\r\n// ============================================================\r\n\r\nexport const UserInfoSchema = z.object({\r\n sub: z.string(),\r\n name: z.string().optional(),\r\n picture: z.string().optional(),\r\n email: z.string().optional(),\r\n email_verified: z.boolean().optional(),\r\n updated_at: z.number().optional()\r\n})\r\n\r\nexport type UserInfoRaw = z.infer<typeof UserInfoSchema>\r\n\r\n// ============================================================\r\n// OIDC Discovery Document\r\n// ============================================================\r\n\r\nexport const DiscoveryDocumentSchema = z.object({\r\n issuer: z.string(),\r\n authorization_endpoint: z.string(),\r\n token_endpoint: z.string(),\r\n userinfo_endpoint: z.string().optional(),\r\n jwks_uri: z.string(),\r\n revocation_endpoint: z.string().optional(),\r\n introspection_endpoint: z.string().optional(),\r\n pushed_authorization_request_endpoint: z.string().optional(),\r\n end_session_endpoint: z.string().optional(),\r\n response_types_supported: z.array(z.string()),\r\n grant_types_supported: z.array(z.string()),\r\n id_token_signing_alg_values_supported: z.array(z.string()),\r\n scopes_supported: z.array(z.string()),\r\n subject_types_supported: z.array(z.string()),\r\n token_endpoint_auth_methods_supported: z.array(z.string()),\r\n code_challenge_methods_supported: z.array(z.string()).optional(),\r\n claims_supported: z.array(z.string()).optional(),\r\n dpop_signing_alg_values_supported: z.array(z.string()).optional(),\r\n request_parameter_supported: z.boolean().optional(),\r\n request_uri_parameter_supported: z.boolean().optional()\r\n})\r\n\r\nexport type DiscoveryDocumentRaw = z.infer<typeof DiscoveryDocumentSchema>\r\n\r\nexport const PushedAuthorizationResponseSchema = z.object({\r\n request_uri: z.string().min(1),\r\n expires_in: z.number().int().positive()\r\n})\r\n\r\nexport type PushedAuthorizationResponseRaw = z.infer<\r\n typeof PushedAuthorizationResponseSchema\r\n>\r\n\r\n// ============================================================\r\n// JWT Access Token Payload (RFC 9068)\r\n// ============================================================\r\n\r\nexport const AccessTokenPayloadSchema = z.object({\r\n iss: z.string(),\r\n sub: z.string(),\r\n aud: z.string(),\r\n scope: z.string(),\r\n ver: z.string(),\r\n sid: z.string(),\r\n sv: z.number().int().nonnegative(),\r\n iat: z.number(),\r\n exp: z.number(),\r\n jti: z.string(),\r\n cnf: z.object({ jkt: z.string() }).optional()\r\n})\r\n\r\nexport type AccessTokenPayload = z.infer<typeof AccessTokenPayloadSchema>\r\n\r\n// ============================================================\r\n// JWT ID Token Payload (OIDC Core)\r\n// ============================================================\r\n\r\nexport const IdTokenPayloadSchema = z.object({\r\n iss: z.string(),\r\n sub: z.string(),\r\n aud: z.string(),\r\n sid: z.string().optional(),\r\n iat: z.number(),\r\n exp: z.number(),\r\n nonce: z.string().optional(),\r\n name: z.string().optional(),\r\n email: z.string().optional(),\r\n email_verified: z.boolean().optional(),\r\n updated_at: z.number().optional()\r\n})\r\n\r\nexport type IdTokenPayload = z.infer<typeof IdTokenPayloadSchema>\r\n","/**\r\n * Base error class for the SDK.\r\n */\r\nexport class NyaAccountError extends Error {\r\n readonly code: string\r\n readonly description: string\r\n\r\n constructor(code: string, description: string) {\r\n super(`[${code}] ${description}`)\r\n this.name = 'NyaAccountError'\r\n this.code = code\r\n this.description = description\r\n }\r\n}\r\n\r\n/**\r\n * OAuth protocol error (from server error / error_description response).\r\n */\r\nexport class OAuthError extends NyaAccountError {\r\n constructor(error: string, errorDescription: string) {\r\n super(error, errorDescription)\r\n this.name = 'OAuthError'\r\n }\r\n}\r\n\r\n/**\r\n * JWT verification error.\r\n */\r\nexport class TokenVerificationError extends NyaAccountError {\r\n constructor(description: string) {\r\n super('token_verification_failed', description)\r\n this.name = 'TokenVerificationError'\r\n }\r\n}\r\n\r\n/**\r\n * OIDC Discovery error.\r\n */\r\nexport class DiscoveryError extends NyaAccountError {\r\n constructor(description: string) {\r\n super('discovery_error', description)\r\n this.name = 'DiscoveryError'\r\n }\r\n}\r\n","import { randomBytes, createHash } from 'node:crypto'\r\nimport type { PkcePair } from '@/core/types'\r\n\r\n/**\r\n * Generate a PKCE code_verifier (43-128 character random string).\r\n */\r\nexport function generateCodeVerifier(): string {\r\n return randomBytes(32).toString('base64url')\r\n}\r\n\r\n/**\r\n * Generate an S256 code_challenge from a code_verifier.\r\n */\r\nexport function generateCodeChallenge(codeVerifier: string): string {\r\n return createHash('sha256').update(codeVerifier).digest('base64url')\r\n}\r\n\r\n/**\r\n * Generate a PKCE code_verifier and code_challenge pair.\r\n */\r\nexport function generatePkce(): PkcePair {\r\n const codeVerifier = generateCodeVerifier()\r\n const codeChallenge = generateCodeChallenge(codeVerifier)\r\n return { codeVerifier, codeChallenge }\r\n}\r\n","import { createRemoteJWKSet, jwtVerify, errors as joseErrors } from 'jose'\r\nimport {\r\n AccessTokenPayloadSchema,\r\n IdTokenPayloadSchema,\r\n type AccessTokenPayload,\r\n type IdTokenPayload,\r\n} from '@/core/schemas'\r\nimport { TokenVerificationError } from '@/core/errors'\r\n\r\n/**\r\n * JWT verifier using remote JWKS for signature verification.\r\n */\r\nexport class JwtVerifier {\r\n private jwks: ReturnType<typeof createRemoteJWKSet>\r\n private issuer: string\r\n private defaultAudience: string\r\n\r\n constructor(jwksUri: string, issuer: string, defaultAudience: string) {\r\n this.jwks = createRemoteJWKSet(new URL(jwksUri))\r\n this.issuer = issuer\r\n this.defaultAudience = defaultAudience\r\n }\r\n\r\n /**\r\n * Verify an OAuth 2.0 Access Token (JWT, RFC 9068).\r\n */\r\n async verifyAccessToken(token: string, audience?: string): Promise<AccessTokenPayload> {\r\n try {\r\n const { payload } = await jwtVerify(token, this.jwks, {\r\n algorithms: ['RS256'],\r\n issuer: this.issuer,\r\n audience: audience ?? this.defaultAudience,\r\n })\r\n return AccessTokenPayloadSchema.parse(payload)\r\n } catch (error) {\r\n throw this.wrapError(error, 'Access Token')\r\n }\r\n }\r\n\r\n /**\r\n * Verify an OIDC ID Token.\r\n */\r\n async verifyIdToken(token: string, options?: { audience?: string; nonce?: string }): Promise<IdTokenPayload> {\r\n try {\r\n const { payload } = await jwtVerify(token, this.jwks, {\r\n algorithms: ['RS256'],\r\n issuer: this.issuer,\r\n audience: options?.audience ?? this.defaultAudience,\r\n })\r\n\r\n const parsed = IdTokenPayloadSchema.parse(payload)\r\n\r\n if (options?.nonce !== undefined && parsed.nonce !== options.nonce) {\r\n throw new TokenVerificationError('ID Token nonce mismatch')\r\n }\r\n\r\n return parsed\r\n } catch (error) {\r\n if (error instanceof TokenVerificationError) {\r\n throw error\r\n }\r\n throw this.wrapError(error, 'ID Token')\r\n }\r\n }\r\n\r\n private wrapError(error: unknown, tokenType: string): TokenVerificationError {\r\n if (error instanceof TokenVerificationError) {\r\n return error\r\n }\r\n if (error instanceof joseErrors.JWTExpired) {\r\n return new TokenVerificationError(`${tokenType} has expired`)\r\n }\r\n if (error instanceof joseErrors.JWTClaimValidationFailed) {\r\n return new TokenVerificationError(`${tokenType} claim validation failed: ${error.message}`)\r\n }\r\n if (error instanceof joseErrors.JWSSignatureVerificationFailed) {\r\n return new TokenVerificationError(`${tokenType} signature verification failed`)\r\n }\r\n const message = error instanceof Error ? error.message : 'Unknown error'\r\n return new TokenVerificationError(`${tokenType} verification failed: ${message}`)\r\n }\r\n}\r\n","import axios, { AxiosError, type AxiosInstance } from 'axios'\r\nimport type { Request, Response, NextFunction } from 'express'\r\nimport { randomBytes } from 'node:crypto'\r\nimport {\r\n TokenResponseSchema,\r\n OAuthErrorSchema,\r\n IntrospectionResponseSchema,\r\n UserInfoSchema,\r\n DiscoveryDocumentSchema,\r\n PushedAuthorizationResponseSchema,\r\n TokenTypeHintSchema,\r\n type AccessTokenPayload,\r\n type IdTokenPayload\r\n} from '@/core/schemas'\r\nimport type {\r\n NyaAccountConfig,\r\n TokenResponse,\r\n UserInfo,\r\n IntrospectionResponse,\r\n DiscoveryDocument,\r\n AuthorizationUrlResult,\r\n CreateAuthorizationUrlOptions,\r\n PushAuthorizationRequestOptions,\r\n PushAuthorizationRequestResult,\r\n CreateEndSessionUrlOptions,\r\n ExchangeCodeOptions,\r\n AuthenticateOptions,\r\n EndpointConfig\r\n} from '@/core/types'\r\nimport { OAuthError, DiscoveryError, NyaAccountError } from '@/core/errors'\r\nimport { generateCodeVerifier, generateCodeChallenge } from '@/utils/pkce'\r\nimport { JwtVerifier } from '@/utils/jwt'\r\nimport {\r\n setAuth,\r\n getAuth,\r\n extractBearerToken,\r\n sendOAuthError\r\n} from '@/middleware/express'\r\n\r\ntype EndpointName = keyof EndpointConfig\r\n\r\nconst DISCOVERY_ENDPOINT_MAP: Record<EndpointName, keyof DiscoveryDocument> = {\r\n authorization: 'authorizationEndpoint',\r\n pushedAuthorizationRequest: 'pushedAuthorizationRequestEndpoint',\r\n token: 'tokenEndpoint',\r\n userinfo: 'userinfoEndpoint',\r\n revocation: 'revocationEndpoint',\r\n introspection: 'introspectionEndpoint',\r\n jwks: 'jwksUri',\r\n endSession: 'endSessionEndpoint'\r\n}\r\n\r\n/** Default issuer URL */\r\nconst DEFAULT_ISSUER = 'https://account-api.edge.lolinya.net'\r\n\r\n/** Default discovery cache TTL: 1 hour */\r\nconst DEFAULT_DISCOVERY_CACHE_TTL = 3600000\r\n\r\n/**\r\n * Nya Account Node.js SDK client.\r\n *\r\n * Provides full OAuth 2.1 / OIDC flow support:\r\n * - Authorization Code + PKCE\r\n * - Token exchange / refresh / revocation / introspection\r\n * - Local JWT verification (via JWKS)\r\n * - OIDC UserInfo\r\n * - OIDC Discovery auto-discovery\r\n * - Express middleware (Bearer Token auth + scope validation)\r\n *\r\n * @example\r\n * ```typescript\r\n * const client = new NyaAccountClient({\r\n * issuer: 'https://account.example.com',\r\n * clientId: 'my-app',\r\n * clientSecret: 'my-secret',\r\n * })\r\n *\r\n * // Create authorization URL (with PKCE)\r\n * const { url, codeVerifier, state } = await client.createAuthorizationUrl({\r\n * redirectUri: 'https://myapp.com/callback',\r\n * scope: 'openid profile email',\r\n * })\r\n *\r\n * // Exchange code for tokens\r\n * const tokens = await client.exchangeCode({\r\n * code: callbackCode,\r\n * redirectUri: 'https://myapp.com/callback',\r\n * codeVerifier,\r\n * })\r\n *\r\n * // Get user info\r\n * const userInfo = await client.getUserInfo(tokens.accessToken)\r\n * ```\r\n */\r\ntype ResolvedConfig = NyaAccountConfig & { issuer: string }\r\n\r\nexport class NyaAccountClient {\r\n private httpClient: AxiosInstance\r\n private config: ResolvedConfig\r\n private discoveryCache: DiscoveryDocument | null = null\r\n private discoveryCacheTimestamp = 0\r\n private readonly discoveryCacheTtl: number\r\n private jwtVerifier: JwtVerifier | null = null\r\n\r\n constructor(config: NyaAccountConfig) {\r\n this.config = {\r\n ...config,\r\n issuer: config.issuer ?? DEFAULT_ISSUER\r\n }\r\n this.discoveryCacheTtl = config.discoveryCacheTtl ?? DEFAULT_DISCOVERY_CACHE_TTL\r\n this.httpClient = axios.create({\r\n timeout: config.timeout ?? 10000\r\n })\r\n }\r\n\r\n // ============================================================\r\n // Client Authentication Helpers\r\n // ============================================================\r\n\r\n /**\r\n * 根据配置的认证方式返回请求体中的凭据参数\r\n * client_secret_post: 凭据放入请求体\r\n * client_secret_basic: 不放入请求体(通过 header 传递)\r\n */\r\n private getClientAuthBody(): Record<string, string> {\r\n if (this.config.tokenEndpointAuthMethod === 'client_secret_basic') {\r\n return {}\r\n }\r\n return {\r\n client_id: this.config.clientId,\r\n client_secret: this.config.clientSecret\r\n }\r\n }\r\n\r\n /**\r\n * 根据配置的认证方式返回请求头\r\n * client_secret_basic: Authorization: Basic base64(client_id:client_secret)\r\n * client_secret_post: 不添加额外头\r\n */\r\n private getClientAuthHeaders(): Record<string, string> {\r\n if (this.config.tokenEndpointAuthMethod === 'client_secret_basic') {\r\n // RFC 6749 Appendix B: 凭据需先进行 URL 编码再 Base64\r\n const encodedId = encodeURIComponent(this.config.clientId)\r\n const encodedSecret = encodeURIComponent(this.config.clientSecret)\r\n const credentials = Buffer.from(`${encodedId}:${encodedSecret}`).toString('base64')\r\n return { Authorization: `Basic ${credentials}` }\r\n }\r\n return {}\r\n }\r\n\r\n // ============================================================\r\n // OIDC Discovery\r\n // ============================================================\r\n\r\n /**\r\n * Fetch the OIDC Discovery document. Results are cached with a configurable TTL.\r\n */\r\n async discover(): Promise<DiscoveryDocument> {\r\n if (\r\n this.discoveryCache &&\r\n Date.now() - this.discoveryCacheTimestamp < this.discoveryCacheTtl\r\n ) {\r\n return this.discoveryCache\r\n }\r\n\r\n try {\r\n const url = `${this.config.issuer}/.well-known/openid-configuration`\r\n const response = await this.httpClient.get(url)\r\n const raw = DiscoveryDocumentSchema.parse(response.data)\r\n\r\n this.discoveryCache = {\r\n issuer: raw.issuer,\r\n authorizationEndpoint: raw.authorization_endpoint,\r\n tokenEndpoint: raw.token_endpoint,\r\n userinfoEndpoint: raw.userinfo_endpoint,\r\n jwksUri: raw.jwks_uri,\r\n revocationEndpoint: raw.revocation_endpoint,\r\n introspectionEndpoint: raw.introspection_endpoint,\r\n pushedAuthorizationRequestEndpoint:\r\n raw.pushed_authorization_request_endpoint,\r\n endSessionEndpoint: raw.end_session_endpoint,\r\n responseTypesSupported: raw.response_types_supported,\r\n grantTypesSupported: raw.grant_types_supported,\r\n idTokenSigningAlgValuesSupported:\r\n raw.id_token_signing_alg_values_supported,\r\n scopesSupported: raw.scopes_supported,\r\n subjectTypesSupported: raw.subject_types_supported,\r\n tokenEndpointAuthMethodsSupported:\r\n raw.token_endpoint_auth_methods_supported,\r\n codeChallengeMethodsSupported: raw.code_challenge_methods_supported,\r\n claimsSupported: raw.claims_supported\r\n }\r\n this.discoveryCacheTimestamp = Date.now()\r\n\r\n return this.discoveryCache\r\n } catch (error) {\r\n if (error instanceof DiscoveryError) throw error\r\n const message = error instanceof Error ? error.message : 'Unknown error'\r\n throw new DiscoveryError(\r\n `Failed to fetch OIDC Discovery document: ${message}`\r\n )\r\n }\r\n }\r\n\r\n /**\r\n * Clear the cached Discovery document and JWT verifier, forcing a re-fetch on next use.\r\n */\r\n clearCache(): void {\r\n this.discoveryCache = null\r\n this.discoveryCacheTimestamp = 0\r\n this.jwtVerifier = null\r\n }\r\n\r\n // ============================================================\r\n // Authorization URL\r\n // ============================================================\r\n\r\n /**\r\n * Create an OAuth authorization URL (automatically includes PKCE).\r\n *\r\n * The returned `codeVerifier` and `state` must be saved to the session\r\n * for later use in token exchange and CSRF validation.\r\n */\r\n async createAuthorizationUrl(\r\n options: CreateAuthorizationUrlOptions\r\n ): Promise<AuthorizationUrlResult> {\r\n const authorizationEndpoint = await this.resolveEndpoint('authorization')\r\n\r\n const codeVerifier = generateCodeVerifier()\r\n const codeChallenge = generateCodeChallenge(codeVerifier)\r\n const state = options.state ?? randomBytes(16).toString('base64url')\r\n\r\n const params = new URLSearchParams({\r\n client_id: this.config.clientId,\r\n redirect_uri: options.redirectUri,\r\n response_type: 'code',\r\n scope: options.scope ?? 'openid',\r\n state,\r\n code_challenge: codeChallenge,\r\n code_challenge_method: 'S256'\r\n })\r\n\r\n if (options.nonce) {\r\n params.set('nonce', options.nonce)\r\n }\r\n\r\n return {\r\n url: `${authorizationEndpoint}?${params.toString()}`,\r\n codeVerifier,\r\n state\r\n }\r\n }\r\n\r\n /**\r\n * Push authorization parameters to PAR endpoint (RFC 9126).\r\n *\r\n * Returns a `request_uri` that can be used in the authorization endpoint.\r\n */\r\n async pushAuthorizationRequest(\r\n options: PushAuthorizationRequestOptions\r\n ): Promise<PushAuthorizationRequestResult> {\r\n const parEndpoint = await this.resolveEndpoint('pushedAuthorizationRequest')\r\n\r\n const codeVerifier = generateCodeVerifier()\r\n const codeChallenge = generateCodeChallenge(codeVerifier)\r\n const state = options.state ?? randomBytes(16).toString('base64url')\r\n\r\n const payload: Record<string, string> = {\r\n ...this.getClientAuthBody(),\r\n redirect_uri: options.redirectUri,\r\n response_type: 'code',\r\n scope: options.scope ?? 'openid',\r\n state,\r\n code_challenge: codeChallenge,\r\n code_challenge_method: 'S256'\r\n }\r\n\r\n if (options.nonce) {\r\n payload.nonce = options.nonce\r\n }\r\n if (options.request) {\r\n payload.request = options.request\r\n }\r\n\r\n try {\r\n const response = await this.httpClient.post(parEndpoint, payload, {\r\n headers: this.getClientAuthHeaders()\r\n })\r\n const raw = PushedAuthorizationResponseSchema.parse(response.data)\r\n\r\n return {\r\n requestUri: raw.request_uri,\r\n expiresIn: raw.expires_in,\r\n codeVerifier,\r\n state\r\n }\r\n } catch (error) {\r\n throw this.handleTokenError(error)\r\n }\r\n }\r\n\r\n /**\r\n * Create an authorization URL using PAR `request_uri`.\r\n */\r\n async createAuthorizationUrlWithPar(\r\n options: PushAuthorizationRequestOptions\r\n ): Promise<AuthorizationUrlResult & { requestUri: string; expiresIn: number }> {\r\n const authorizationEndpoint = await this.resolveEndpoint('authorization')\r\n const pushed = await this.pushAuthorizationRequest(options)\r\n\r\n const params = new URLSearchParams({\r\n client_id: this.config.clientId,\r\n request_uri: pushed.requestUri\r\n })\r\n\r\n return {\r\n url: `${authorizationEndpoint}?${params.toString()}`,\r\n codeVerifier: pushed.codeVerifier,\r\n state: pushed.state,\r\n requestUri: pushed.requestUri,\r\n expiresIn: pushed.expiresIn\r\n }\r\n }\r\n\r\n /**\r\n * Create OIDC RP-Initiated Logout URL (`end_session_endpoint`).\r\n */\r\n async createEndSessionUrl(options?: CreateEndSessionUrlOptions): Promise<string> {\r\n const endSessionEndpoint = await this.resolveEndpoint('endSession')\r\n const params = new URLSearchParams()\r\n\r\n if (options?.idTokenHint) {\r\n params.set('id_token_hint', options.idTokenHint)\r\n }\r\n if (options?.postLogoutRedirectUri) {\r\n params.set('post_logout_redirect_uri', options.postLogoutRedirectUri)\r\n }\r\n if (options?.state) {\r\n params.set('state', options.state)\r\n }\r\n\r\n // 帮助服务端在未提供 id_token_hint 时识别客户端\r\n params.set('client_id', options?.clientId ?? this.config.clientId)\r\n\r\n return `${endSessionEndpoint}?${params.toString()}`\r\n }\r\n\r\n // ============================================================\r\n // Token Operations\r\n // ============================================================\r\n\r\n /**\r\n * Exchange an authorization code for tokens (Authorization Code Grant).\r\n */\r\n async exchangeCode(options: ExchangeCodeOptions): Promise<TokenResponse> {\r\n const tokenEndpoint = await this.resolveEndpoint('token')\r\n\r\n try {\r\n const response = await this.httpClient.post(\r\n tokenEndpoint,\r\n {\r\n grant_type: 'authorization_code',\r\n code: options.code,\r\n redirect_uri: options.redirectUri,\r\n code_verifier: options.codeVerifier,\r\n ...this.getClientAuthBody()\r\n },\r\n { headers: this.getClientAuthHeaders() }\r\n )\r\n\r\n return this.mapTokenResponse(response.data)\r\n } catch (error) {\r\n throw this.handleTokenError(error)\r\n }\r\n }\r\n\r\n /**\r\n * Refresh an Access Token using a Refresh Token.\r\n */\r\n async refreshToken(refreshToken: string): Promise<TokenResponse> {\r\n const tokenEndpoint = await this.resolveEndpoint('token')\r\n\r\n try {\r\n const response = await this.httpClient.post(\r\n tokenEndpoint,\r\n {\r\n grant_type: 'refresh_token',\r\n refresh_token: refreshToken,\r\n ...this.getClientAuthBody()\r\n },\r\n { headers: this.getClientAuthHeaders() }\r\n )\r\n\r\n return this.mapTokenResponse(response.data)\r\n } catch (error) {\r\n throw this.handleTokenError(error)\r\n }\r\n }\r\n\r\n /**\r\n * Revoke a token (RFC 7009).\r\n *\r\n * Supports revoking Access Tokens or Refresh Tokens.\r\n * Revoking a Refresh Token also revokes its entire token family.\r\n */\r\n async revokeToken(\r\n token: string,\r\n options?: { tokenTypeHint?: 'access_token' | 'refresh_token' }\r\n ): Promise<void> {\r\n const revocationEndpoint = await this.resolveEndpoint('revocation')\r\n\r\n try {\r\n const tokenTypeHint = options?.tokenTypeHint\r\n ? TokenTypeHintSchema.parse(options.tokenTypeHint)\r\n : undefined\r\n await this.httpClient.post(\r\n revocationEndpoint,\r\n {\r\n token,\r\n token_type_hint: tokenTypeHint,\r\n ...this.getClientAuthBody()\r\n },\r\n { headers: this.getClientAuthHeaders() }\r\n )\r\n } catch {\r\n // RFC 7009: revocation endpoint always returns 200, ignore errors\r\n }\r\n }\r\n\r\n /**\r\n * Token introspection (RFC 7662).\r\n *\r\n * Query the server for the current state of a token (active status, associated user info, etc.).\r\n */\r\n async introspectToken(\r\n token: string,\r\n options?: { tokenTypeHint?: 'access_token' | 'refresh_token' }\r\n ): Promise<IntrospectionResponse> {\r\n const introspectionEndpoint = await this.resolveEndpoint('introspection')\r\n\r\n try {\r\n const tokenTypeHint = options?.tokenTypeHint\r\n ? TokenTypeHintSchema.parse(options.tokenTypeHint)\r\n : undefined\r\n const response = await this.httpClient.post(\r\n introspectionEndpoint,\r\n {\r\n token,\r\n token_type_hint: tokenTypeHint,\r\n ...this.getClientAuthBody()\r\n },\r\n { headers: this.getClientAuthHeaders() }\r\n )\r\n\r\n const raw = IntrospectionResponseSchema.parse(response.data)\r\n return {\r\n active: raw.active,\r\n scope: raw.scope,\r\n clientId: raw.client_id,\r\n username: raw.username,\r\n tokenType: raw.token_type,\r\n exp: raw.exp,\r\n iat: raw.iat,\r\n sub: raw.sub,\r\n aud: raw.aud,\r\n iss: raw.iss,\r\n jti: raw.jti,\r\n sid: raw.sid,\r\n sv: raw.sv\r\n }\r\n } catch (error) {\r\n throw this.handleTokenError(error)\r\n }\r\n }\r\n\r\n // ============================================================\r\n // OIDC UserInfo\r\n // ============================================================\r\n\r\n /**\r\n * Get user info using an Access Token (OIDC UserInfo Endpoint).\r\n *\r\n * The returned fields depend on the scopes included in the token:\r\n * - `profile`: name, picture, updatedAt\r\n * - `email`: email, emailVerified\r\n */\r\n async getUserInfo(accessToken: string): Promise<UserInfo> {\r\n const userinfoEndpoint = await this.resolveEndpoint('userinfo')\r\n\r\n try {\r\n const response = await this.httpClient.get(userinfoEndpoint, {\r\n headers: { Authorization: `Bearer ${accessToken}` }\r\n })\r\n\r\n const raw = UserInfoSchema.parse(response.data)\r\n return {\r\n sub: raw.sub,\r\n name: raw.name,\r\n picture: raw.picture,\r\n email: raw.email,\r\n emailVerified: raw.email_verified,\r\n updatedAt: raw.updated_at\r\n }\r\n } catch (error) {\r\n throw this.handleTokenError(error)\r\n }\r\n }\r\n\r\n // ============================================================\r\n // Local JWT Verification\r\n // ============================================================\r\n\r\n /**\r\n * Locally verify a JWT Access Token (RFC 9068).\r\n *\r\n * Uses remote JWKS for signature verification, and validates issuer, audience, expiry, etc.\r\n *\r\n * @param token JWT Access Token string\r\n * @param options.audience Custom audience validation value (defaults to clientId)\r\n */\r\n async verifyAccessToken(\r\n token: string,\r\n options?: { audience?: string }\r\n ): Promise<AccessTokenPayload> {\r\n const verifier = await this.getJwtVerifier()\r\n return verifier.verifyAccessToken(token, options?.audience)\r\n }\r\n\r\n /**\r\n * Locally verify an OIDC ID Token.\r\n *\r\n * @param token JWT ID Token string\r\n * @param options.audience Custom audience validation value (defaults to clientId)\r\n * @param options.nonce Validate the nonce claim (required if nonce was sent during authorization)\r\n */\r\n async verifyIdToken(\r\n token: string,\r\n options?: { audience?: string; nonce?: string }\r\n ): Promise<IdTokenPayload> {\r\n const verifier = await this.getJwtVerifier()\r\n return verifier.verifyIdToken(token, options)\r\n }\r\n\r\n // ============================================================\r\n // Express Middleware\r\n // ============================================================\r\n\r\n /**\r\n * Express middleware: verify the Bearer Token in the request.\r\n *\r\n * After successful verification, use `getAuth(req)` to retrieve the token payload.\r\n *\r\n * @param options.strategy Verification strategy: 'local' (default, JWT local verification) or 'introspection' (remote introspection)\r\n *\r\n * @example\r\n * ```typescript\r\n * import { getAuth } from '@nya-account/node-sdk/express'\r\n *\r\n * app.use('/api', client.authenticate())\r\n *\r\n * app.get('/api/me', (req, res) => {\r\n * const auth = getAuth(req)\r\n * res.json({ userId: auth?.sub })\r\n * })\r\n * ```\r\n */\r\n authenticate(\r\n options?: AuthenticateOptions\r\n ): (req: Request, res: Response, next: NextFunction) => void {\r\n const strategy = options?.strategy ?? 'local'\r\n\r\n return (req: Request, res: Response, next: NextFunction) => {\r\n const token = extractBearerToken(req)\r\n if (!token) {\r\n sendOAuthError(\r\n res,\r\n 401,\r\n 'invalid_token',\r\n 'Missing Bearer token in Authorization header'\r\n )\r\n return\r\n }\r\n\r\n const handleVerification = async (): Promise<void> => {\r\n let payload: AccessTokenPayload\r\n\r\n if (strategy === 'introspection') {\r\n const introspection = await this.introspectToken(token)\r\n if (!introspection.active) {\r\n sendOAuthError(res, 401, 'invalid_token', 'Token is not active')\r\n return\r\n }\r\n const tokenType = introspection.tokenType?.toLowerCase()\r\n if (\r\n tokenType &&\r\n tokenType !== 'bearer' &&\r\n tokenType !== 'access_token'\r\n ) {\r\n sendOAuthError(\r\n res,\r\n 401,\r\n 'invalid_token',\r\n 'Token is not an access token'\r\n )\r\n return\r\n }\r\n payload = {\r\n iss: introspection.iss ?? '',\r\n sub: introspection.sub ?? '',\r\n aud: introspection.aud ?? '',\r\n scope: introspection.scope ?? '',\r\n ver: '1',\r\n iat: introspection.iat ?? 0,\r\n exp: introspection.exp ?? 0,\r\n jti: introspection.jti ?? '',\r\n sid: introspection.sid ?? '',\r\n sv: introspection.sv ?? 0\r\n }\r\n } else {\r\n payload = await this.verifyAccessToken(token)\r\n }\r\n\r\n setAuth(req, payload)\r\n next()\r\n }\r\n\r\n handleVerification().catch(() => {\r\n sendOAuthError(\r\n res,\r\n 401,\r\n 'invalid_token',\r\n 'Invalid or expired access token'\r\n )\r\n })\r\n }\r\n }\r\n\r\n /**\r\n * Express middleware: validate that the token in the request contains the specified scopes.\r\n *\r\n * Must be used after the `authenticate()` middleware.\r\n *\r\n * @example\r\n * ```typescript\r\n * app.get('/api/profile',\r\n * client.authenticate(),\r\n * client.requireScopes('profile'),\r\n * (req, res) => { ... }\r\n * )\r\n * ```\r\n */\r\n requireScopes(\r\n ...scopes: string[]\r\n ): (req: Request, res: Response, next: NextFunction) => void {\r\n return (req: Request, res: Response, next: NextFunction) => {\r\n const auth = getAuth(req)\r\n if (!auth) {\r\n sendOAuthError(res, 401, 'invalid_token', 'Request not authenticated')\r\n return\r\n }\r\n\r\n const tokenScopes = auth.scope.split(' ')\r\n const missingScopes = scopes.filter((s) => !tokenScopes.includes(s))\r\n\r\n if (missingScopes.length > 0) {\r\n sendOAuthError(\r\n res,\r\n 403,\r\n 'insufficient_scope',\r\n `Missing required scopes: ${missingScopes.join(' ')}`\r\n )\r\n return\r\n }\r\n\r\n next()\r\n }\r\n }\r\n\r\n // ============================================================\r\n // Private Methods\r\n // ============================================================\r\n\r\n private async resolveEndpoint(name: EndpointName): Promise<string> {\r\n const explicit = this.config.endpoints?.[name]\r\n if (explicit) {\r\n return explicit\r\n }\r\n\r\n const discovery = await this.discover()\r\n const discoveryKey = DISCOVERY_ENDPOINT_MAP[name]\r\n const endpoint = discovery[discoveryKey]\r\n\r\n if (!endpoint || typeof endpoint !== 'string') {\r\n throw new NyaAccountError(\r\n 'endpoint_not_found',\r\n `Endpoint '${name}' not found in Discovery document`\r\n )\r\n }\r\n\r\n return endpoint\r\n }\r\n\r\n private async getJwtVerifier(): Promise<JwtVerifier> {\r\n if (this.jwtVerifier) {\r\n return this.jwtVerifier\r\n }\r\n\r\n const jwksUri = await this.resolveEndpoint('jwks')\r\n const discovery = await this.discover()\r\n\r\n this.jwtVerifier = new JwtVerifier(\r\n jwksUri,\r\n discovery.issuer,\r\n this.config.clientId\r\n )\r\n return this.jwtVerifier\r\n }\r\n\r\n private mapTokenResponse(data: unknown): TokenResponse {\r\n const raw = TokenResponseSchema.parse(data)\r\n return {\r\n accessToken: raw.access_token,\r\n tokenType: raw.token_type,\r\n expiresIn: raw.expires_in,\r\n refreshToken: raw.refresh_token,\r\n scope: raw.scope,\r\n idToken: raw.id_token\r\n }\r\n }\r\n\r\n private handleTokenError(error: unknown): NyaAccountError {\r\n if (error instanceof NyaAccountError) {\r\n return error\r\n }\r\n\r\n if (error instanceof AxiosError && error.response) {\r\n const parsed = OAuthErrorSchema.safeParse(error.response.data)\r\n if (parsed.success) {\r\n return new OAuthError(\r\n parsed.data.error,\r\n parsed.data.error_description ?? 'Unknown error'\r\n )\r\n }\r\n }\r\n\r\n const message = error instanceof Error ? error.message : 'Unknown error'\r\n return new NyaAccountError('request_failed', `Request failed: ${message}`)\r\n }\r\n}\r\n"],"mappings":";;;;;;;AAEA,MAAa,sBAAsB,EAAE,KAAK,CAAC,gBAAgB,eAAgB,EAAC;AAM5E,MAAa,sBAAsB,EAAE,OAAO;CACxC,cAAc,EAAE,QAAQ;CACxB,YAAY,EAAE,QAAQ;CACtB,YAAY,EAAE,QAAQ;CACtB,eAAe,EAAE,QAAQ;CACzB,OAAO,EAAE,QAAQ;CACjB,UAAU,EAAE,QAAQ,CAAC,UAAU;AAClC,EAAC;AAQF,MAAa,mBAAmB,EAAE,OAAO;CACrC,OAAO,EAAE,QAAQ;CACjB,mBAAmB,EAAE,QAAQ,CAAC,UAAU;AAC3C,EAAC;AAQF,MAAa,8BAA8B,EAAE,OAAO;CAChD,QAAQ,EAAE,SAAS;CACnB,OAAO,EAAE,QAAQ,CAAC,UAAU;CAC5B,WAAW,EAAE,QAAQ,CAAC,UAAU;CAChC,UAAU,EAAE,QAAQ,CAAC,UAAU;CAC/B,YAAY,EAAE,QAAQ,CAAC,UAAU;CACjC,KAAK,EAAE,QAAQ,CAAC,UAAU;CAC1B,KAAK,EAAE,QAAQ,CAAC,UAAU;CAC1B,KAAK,EAAE,QAAQ,CAAC,UAAU;CAC1B,KAAK,EAAE,QAAQ,CAAC,UAAU;CAC1B,KAAK,EAAE,QAAQ,CAAC,UAAU;CAC1B,KAAK,EAAE,QAAQ,CAAC,UAAU;CAC1B,KAAK,EAAE,QAAQ,CAAC,UAAU;CAC1B,IAAI,EAAE,QAAQ,CAAC,UAAU;AAC5B,EAAC;AAQF,MAAa,iBAAiB,EAAE,OAAO;CACnC,KAAK,EAAE,QAAQ;CACf,MAAM,EAAE,QAAQ,CAAC,UAAU;CAC3B,SAAS,EAAE,QAAQ,CAAC,UAAU;CAC9B,OAAO,EAAE,QAAQ,CAAC,UAAU;CAC5B,gBAAgB,EAAE,SAAS,CAAC,UAAU;CACtC,YAAY,EAAE,QAAQ,CAAC,UAAU;AACpC,EAAC;AAQF,MAAa,0BAA0B,EAAE,OAAO;CAC5C,QAAQ,EAAE,QAAQ;CAClB,wBAAwB,EAAE,QAAQ;CAClC,gBAAgB,EAAE,QAAQ;CAC1B,mBAAmB,EAAE,QAAQ,CAAC,UAAU;CACxC,UAAU,EAAE,QAAQ;CACpB,qBAAqB,EAAE,QAAQ,CAAC,UAAU;CAC1C,wBAAwB,EAAE,QAAQ,CAAC,UAAU;CAC7C,uCAAuC,EAAE,QAAQ,CAAC,UAAU;CAC5D,sBAAsB,EAAE,QAAQ,CAAC,UAAU;CAC3C,0BAA0B,EAAE,MAAM,EAAE,QAAQ,CAAC;CAC7C,uBAAuB,EAAE,MAAM,EAAE,QAAQ,CAAC;CAC1C,uCAAuC,EAAE,MAAM,EAAE,QAAQ,CAAC;CAC1D,kBAAkB,EAAE,MAAM,EAAE,QAAQ,CAAC;CACrC,yBAAyB,EAAE,MAAM,EAAE,QAAQ,CAAC;CAC5C,uCAAuC,EAAE,MAAM,EAAE,QAAQ,CAAC;CAC1D,kCAAkC,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU;CAChE,kBAAkB,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU;CAChD,mCAAmC,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU;CACjE,6BAA6B,EAAE,SAAS,CAAC,UAAU;CACnD,iCAAiC,EAAE,SAAS,CAAC,UAAU;AAC1D,EAAC;AAIF,MAAa,oCAAoC,EAAE,OAAO;CACtD,aAAa,EAAE,QAAQ,CAAC,IAAI,EAAE;CAC9B,YAAY,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU;AAC1C,EAAC;AAUF,MAAa,2BAA2B,EAAE,OAAO;CAC7C,KAAK,EAAE,QAAQ;CACf,KAAK,EAAE,QAAQ;CACf,KAAK,EAAE,QAAQ;CACf,OAAO,EAAE,QAAQ;CACjB,KAAK,EAAE,QAAQ;CACf,KAAK,EAAE,QAAQ;CACf,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,aAAa;CAClC,KAAK,EAAE,QAAQ;CACf,KAAK,EAAE,QAAQ;CACf,KAAK,EAAE,QAAQ;CACf,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAE,EAAC,CAAC,UAAU;AAChD,EAAC;AAQF,MAAa,uBAAuB,EAAE,OAAO;CACzC,KAAK,EAAE,QAAQ;CACf,KAAK,EAAE,QAAQ;CACf,KAAK,EAAE,QAAQ;CACf,KAAK,EAAE,QAAQ,CAAC,UAAU;CAC1B,KAAK,EAAE,QAAQ;CACf,KAAK,EAAE,QAAQ;CACf,OAAO,EAAE,QAAQ,CAAC,UAAU;CAC5B,MAAM,EAAE,QAAQ,CAAC,UAAU;CAC3B,OAAO,EAAE,QAAQ,CAAC,UAAU;CAC5B,gBAAgB,EAAE,SAAS,CAAC,UAAU;CACtC,YAAY,EAAE,QAAQ,CAAC,UAAU;AACpC,EAAC;;;;;;;;;AC1IF,IAAa,kBAAb,cAAqC,MAAM;CAIvC,YAAYA,MAAcC,aAAqB;AAC3C,SAAO,GAAG,KAAK,IAAI,YAAY,EAAE;OAJ5B,YAAA;OACA,mBAAA;AAIL,OAAK,OAAO;AACZ,OAAK,OAAO;AACZ,OAAK,cAAc;CACtB;AACJ;;;;;;AAKD,IAAa,aAAb,cAAgC,gBAAgB;CAC5C,YAAYC,OAAeC,kBAA0B;AACjD,QAAM,OAAO,iBAAiB;AAC9B,OAAK,OAAO;CACf;AACJ;;;;;;AAKD,IAAa,yBAAb,cAA4C,gBAAgB;CACxD,YAAYF,aAAqB;AAC7B,QAAM,6BAA6B,YAAY;AAC/C,OAAK,OAAO;CACf;AACJ;;;;;;AAKD,IAAa,iBAAb,cAAoC,gBAAgB;CAChD,YAAYA,aAAqB;AAC7B,QAAM,mBAAmB,YAAY;AACrC,OAAK,OAAO;CACf;AACJ;;;;;;;;;ACrCD,SAAgB,uBAA+B;AAC3C,QAAO,YAAY,GAAG,CAAC,SAAS,YAAY;AAC/C;;;;;;AAKD,SAAgB,sBAAsBG,cAA8B;AAChE,QAAO,WAAW,SAAS,CAAC,OAAO,aAAa,CAAC,OAAO,YAAY;AACvE;;;;;;AAKD,SAAgB,eAAyB;CACrC,MAAM,eAAe,sBAAsB;CAC3C,MAAM,gBAAgB,sBAAsB,aAAa;AACzD,QAAO;EAAE;EAAc;CAAe;AACzC;;;;;;;;;ACZD,IAAa,cAAb,MAAyB;CAKrB,YAAYC,SAAiBC,QAAgBC,iBAAyB;OAJ9D,YAAA;OACA,cAAA;OACA,uBAAA;AAGJ,OAAK,OAAO,mBAAmB,IAAI,IAAI,SAAS;AAChD,OAAK,SAAS;AACd,OAAK,kBAAkB;CAC1B;;;;;;CAKD,MAAM,kBAAkBC,OAAeC,UAAgD;AACnF,MAAI;GACA,MAAM,EAAE,SAAS,GAAG,MAAM,UAAU,OAAO,KAAK,MAAM;IAClD,YAAY,CAAC,OAAQ;IACrB,QAAQ,KAAK;IACb,UAAU,YAAY,KAAK;GAC9B,EAAC;AACF,UAAO,yBAAyB,MAAM,QAAQ;EACjD,SAAQ,OAAO;AACZ,SAAM,KAAK,UAAU,OAAO,eAAe;EAC9C;CACJ;;;;;;CAKD,MAAM,cAAcD,OAAeE,SAA0E;AACzG,MAAI;GACA,MAAM,EAAE,SAAS,GAAG,MAAM,UAAU,OAAO,KAAK,MAAM;IAClD,YAAY,CAAC,OAAQ;IACrB,QAAQ,KAAK;IACb,UAAU,SAAS,YAAY,KAAK;GACvC,EAAC;GAEF,MAAM,SAAS,qBAAqB,MAAM,QAAQ;AAElD,OAAI,SAAS,oBAAuB,OAAO,UAAU,QAAQ,MACzD,OAAM,IAAI,uBAAuB;AAGrC,UAAO;EACV,SAAQ,OAAO;AACZ,OAAI,iBAAiB,uBACjB,OAAM;AAEV,SAAM,KAAK,UAAU,OAAO,WAAW;EAC1C;CACJ;CAED,UAAkBC,OAAgBC,WAA2C;AACzE,MAAI,iBAAiB,uBACjB,QAAO;AAEX,MAAI,iBAAiB,OAAW,WAC5B,QAAO,IAAI,wBAAwB,EAAE,UAAU;AAEnD,MAAI,iBAAiB,OAAW,yBAC5B,QAAO,IAAI,wBAAwB,EAAE,UAAU,4BAA4B,MAAM,QAAQ;AAE7F,MAAI,iBAAiB,OAAW,+BAC5B,QAAO,IAAI,wBAAwB,EAAE,UAAU;EAEnD,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,SAAO,IAAI,wBAAwB,EAAE,UAAU,wBAAwB,QAAQ;CAClF;AACJ;;;;ACxCD,MAAMC,yBAAwE;CAC1E,eAAe;CACf,4BAA4B;CAC5B,OAAO;CACP,UAAU;CACV,YAAY;CACZ,eAAe;CACf,MAAM;CACN,YAAY;AACf;;AAGD,MAAM,iBAAiB;;AAGvB,MAAM,8BAA8B;AAwCpC,IAAa,mBAAb,MAA8B;CAQ1B,YAAYC,QAA0B;OAP9B,kBAAA;OACA,cAAA;OACA,iBAA2C;OAC3C,0BAA0B;OACjB,yBAAA;OACT,cAAkC;AAGtC,OAAK,SAAS;GACV,GAAG;GACH,QAAQ,OAAO,UAAU;EAC5B;AACD,OAAK,oBAAoB,OAAO,qBAAqB;AACrD,OAAK,aAAa,MAAM,OAAO,EAC3B,SAAS,OAAO,WAAW,IAC9B,EAAC;CACL;;;;;;;;;;CAWD,oBAAoD;AAChD,MAAI,KAAK,OAAO,4BAA4B,sBACxC,QAAO,CAAE;AAEb,SAAO;GACH,WAAW,KAAK,OAAO;GACvB,eAAe,KAAK,OAAO;EAC9B;CACJ;;;;;;;;;;CAOD,uBAAuD;AACnD,MAAI,KAAK,OAAO,4BAA4B,uBAAuB;GAE/D,MAAM,YAAY,mBAAmB,KAAK,OAAO,SAAS;GAC1D,MAAM,gBAAgB,mBAAmB,KAAK,OAAO,aAAa;GAClE,MAAM,cAAc,OAAO,MAAM,EAAE,UAAU,GAAG,cAAc,EAAE,CAAC,SAAS,SAAS;AACnF,UAAO,EAAE,gBAAgB,QAAQ,YAAY,EAAG;EACnD;AACD,SAAO,CAAE;CACZ;;;;;;CASD,MAAM,WAAuC;AACzC,MACI,KAAK,kBACL,KAAK,KAAK,GAAG,KAAK,0BAA0B,KAAK,kBAEjD,QAAO,KAAK;AAGhB,MAAI;GACA,MAAM,OAAO,EAAE,KAAK,OAAO,OAAO;GAClC,MAAM,WAAW,MAAM,KAAK,WAAW,IAAI,IAAI;GAC/C,MAAM,MAAM,wBAAwB,MAAM,SAAS,KAAK;AAExD,QAAK,iBAAiB;IAClB,QAAQ,IAAI;IACZ,uBAAuB,IAAI;IAC3B,eAAe,IAAI;IACnB,kBAAkB,IAAI;IACtB,SAAS,IAAI;IACb,oBAAoB,IAAI;IACxB,uBAAuB,IAAI;IAC3B,oCACI,IAAI;IACR,oBAAoB,IAAI;IACxB,wBAAwB,IAAI;IAC5B,qBAAqB,IAAI;IACzB,kCACI,IAAI;IACR,iBAAiB,IAAI;IACrB,uBAAuB,IAAI;IAC3B,mCACI,IAAI;IACR,+BAA+B,IAAI;IACnC,iBAAiB,IAAI;GACxB;AACD,QAAK,0BAA0B,KAAK,KAAK;AAEzC,UAAO,KAAK;EACf,SAAQ,OAAO;AACZ,OAAI,iBAAiB,eAAgB,OAAM;GAC3C,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,SAAM,IAAI,gBACL,2CAA2C,QAAQ;EAE3D;CACJ;;;;;;CAKD,aAAmB;AACf,OAAK,iBAAiB;AACtB,OAAK,0BAA0B;AAC/B,OAAK,cAAc;CACtB;;;;;;;;;;;;CAYD,MAAM,uBACFC,SAC+B;EAC/B,MAAM,wBAAwB,MAAM,KAAK,gBAAgB,gBAAgB;EAEzE,MAAM,eAAe,sBAAsB;EAC3C,MAAM,gBAAgB,sBAAsB,aAAa;EACzD,MAAM,QAAQ,QAAQ,SAAS,YAAY,GAAG,CAAC,SAAS,YAAY;EAEpE,MAAM,SAAS,IAAI,gBAAgB;GAC/B,WAAW,KAAK,OAAO;GACvB,cAAc,QAAQ;GACtB,eAAe;GACf,OAAO,QAAQ,SAAS;GACxB;GACA,gBAAgB;GAChB,uBAAuB;EAC1B;AAED,MAAI,QAAQ,MACR,QAAO,IAAI,SAAS,QAAQ,MAAM;AAGtC,SAAO;GACH,MAAM,EAAE,sBAAsB,GAAG,OAAO,UAAU,CAAC;GACnD;GACA;EACH;CACJ;;;;;;;;;;CAOD,MAAM,yBACFC,SACuC;EACvC,MAAM,cAAc,MAAM,KAAK,gBAAgB,6BAA6B;EAE5E,MAAM,eAAe,sBAAsB;EAC3C,MAAM,gBAAgB,sBAAsB,aAAa;EACzD,MAAM,QAAQ,QAAQ,SAAS,YAAY,GAAG,CAAC,SAAS,YAAY;EAEpE,MAAMC,UAAkC;GACpC,GAAG,KAAK,mBAAmB;GAC3B,cAAc,QAAQ;GACtB,eAAe;GACf,OAAO,QAAQ,SAAS;GACxB;GACA,gBAAgB;GAChB,uBAAuB;EAC1B;AAED,MAAI,QAAQ,MACR,SAAQ,QAAQ,QAAQ;AAE5B,MAAI,QAAQ,QACR,SAAQ,UAAU,QAAQ;AAG9B,MAAI;GACA,MAAM,WAAW,MAAM,KAAK,WAAW,KAAK,aAAa,SAAS,EAC9D,SAAS,KAAK,sBAAsB,CACvC,EAAC;GACF,MAAM,MAAM,kCAAkC,MAAM,SAAS,KAAK;AAElE,UAAO;IACH,YAAY,IAAI;IAChB,WAAW,IAAI;IACf;IACA;GACH;EACJ,SAAQ,OAAO;AACZ,SAAM,KAAK,iBAAiB,MAAM;EACrC;CACJ;;;;;;CAKD,MAAM,8BACFD,SAC2E;EAC3E,MAAM,wBAAwB,MAAM,KAAK,gBAAgB,gBAAgB;EACzE,MAAM,SAAS,MAAM,KAAK,yBAAyB,QAAQ;EAE3D,MAAM,SAAS,IAAI,gBAAgB;GAC/B,WAAW,KAAK,OAAO;GACvB,aAAa,OAAO;EACvB;AAED,SAAO;GACH,MAAM,EAAE,sBAAsB,GAAG,OAAO,UAAU,CAAC;GACnD,cAAc,OAAO;GACrB,OAAO,OAAO;GACd,YAAY,OAAO;GACnB,WAAW,OAAO;EACrB;CACJ;;;;;;CAKD,MAAM,oBAAoBE,SAAuD;EAC7E,MAAM,qBAAqB,MAAM,KAAK,gBAAgB,aAAa;EACnE,MAAM,SAAS,IAAI;AAEnB,MAAI,SAAS,YACT,QAAO,IAAI,iBAAiB,QAAQ,YAAY;AAEpD,MAAI,SAAS,sBACT,QAAO,IAAI,4BAA4B,QAAQ,sBAAsB;AAEzE,MAAI,SAAS,MACT,QAAO,IAAI,SAAS,QAAQ,MAAM;AAItC,SAAO,IAAI,aAAa,SAAS,YAAY,KAAK,OAAO,SAAS;AAElE,UAAQ,EAAE,mBAAmB,GAAG,OAAO,UAAU,CAAC;CACrD;;;;;;CASD,MAAM,aAAaC,SAAsD;EACrE,MAAM,gBAAgB,MAAM,KAAK,gBAAgB,QAAQ;AAEzD,MAAI;GACA,MAAM,WAAW,MAAM,KAAK,WAAW,KACnC,eACA;IACI,YAAY;IACZ,MAAM,QAAQ;IACd,cAAc,QAAQ;IACtB,eAAe,QAAQ;IACvB,GAAG,KAAK,mBAAmB;GAC9B,GACD,EAAE,SAAS,KAAK,sBAAsB,CAAE,EAC3C;AAED,UAAO,KAAK,iBAAiB,SAAS,KAAK;EAC9C,SAAQ,OAAO;AACZ,SAAM,KAAK,iBAAiB,MAAM;EACrC;CACJ;;;;;;CAKD,MAAM,aAAaC,cAA8C;EAC7D,MAAM,gBAAgB,MAAM,KAAK,gBAAgB,QAAQ;AAEzD,MAAI;GACA,MAAM,WAAW,MAAM,KAAK,WAAW,KACnC,eACA;IACI,YAAY;IACZ,eAAe;IACf,GAAG,KAAK,mBAAmB;GAC9B,GACD,EAAE,SAAS,KAAK,sBAAsB,CAAE,EAC3C;AAED,UAAO,KAAK,iBAAiB,SAAS,KAAK;EAC9C,SAAQ,OAAO;AACZ,SAAM,KAAK,iBAAiB,MAAM;EACrC;CACJ;;;;;;;;;;;;CAQD,MAAM,YACFC,OACAC,SACa;EACb,MAAM,qBAAqB,MAAM,KAAK,gBAAgB,aAAa;AAEnE,MAAI;GACA,MAAM,gBAAgB,SAAS,gBACzB,oBAAoB,MAAM,QAAQ,cAAc;AAEtD,SAAM,KAAK,WAAW,KAClB,oBACA;IACI;IACA,iBAAiB;IACjB,GAAG,KAAK,mBAAmB;GAC9B,GACD,EAAE,SAAS,KAAK,sBAAsB,CAAE,EAC3C;EACJ,QAAO,CAEP;CACJ;;;;;;;;;;CAOD,MAAM,gBACFD,OACAC,SAC8B;EAC9B,MAAM,wBAAwB,MAAM,KAAK,gBAAgB,gBAAgB;AAEzE,MAAI;GACA,MAAM,gBAAgB,SAAS,gBACzB,oBAAoB,MAAM,QAAQ,cAAc;GAEtD,MAAM,WAAW,MAAM,KAAK,WAAW,KACnC,uBACA;IACI;IACA,iBAAiB;IACjB,GAAG,KAAK,mBAAmB;GAC9B,GACD,EAAE,SAAS,KAAK,sBAAsB,CAAE,EAC3C;GAED,MAAM,MAAM,4BAA4B,MAAM,SAAS,KAAK;AAC5D,UAAO;IACH,QAAQ,IAAI;IACZ,OAAO,IAAI;IACX,UAAU,IAAI;IACd,UAAU,IAAI;IACd,WAAW,IAAI;IACf,KAAK,IAAI;IACT,KAAK,IAAI;IACT,KAAK,IAAI;IACT,KAAK,IAAI;IACT,KAAK,IAAI;IACT,KAAK,IAAI;IACT,KAAK,IAAI;IACT,IAAI,IAAI;GACX;EACJ,SAAQ,OAAO;AACZ,SAAM,KAAK,iBAAiB,MAAM;EACrC;CACJ;;;;;;;;;;;;;;CAaD,MAAM,YAAYC,aAAwC;EACtD,MAAM,mBAAmB,MAAM,KAAK,gBAAgB,WAAW;AAE/D,MAAI;GACA,MAAM,WAAW,MAAM,KAAK,WAAW,IAAI,kBAAkB,EACzD,SAAS,EAAE,gBAAgB,SAAS,YAAY,EAAG,EACtD,EAAC;GAEF,MAAM,MAAM,eAAe,MAAM,SAAS,KAAK;AAC/C,UAAO;IACH,KAAK,IAAI;IACT,MAAM,IAAI;IACV,SAAS,IAAI;IACb,OAAO,IAAI;IACX,eAAe,IAAI;IACnB,WAAW,IAAI;GAClB;EACJ,SAAQ,OAAO;AACZ,SAAM,KAAK,iBAAiB,MAAM;EACrC;CACJ;;;;;;;;;;;;;;;;CAcD,MAAM,kBACFF,OACAG,SAC2B;EAC3B,MAAM,WAAW,MAAM,KAAK,gBAAgB;AAC5C,SAAO,SAAS,kBAAkB,OAAO,SAAS,SAAS;CAC9D;;;;;;;;;;;;;;CASD,MAAM,cACFH,OACAI,SACuB;EACvB,MAAM,WAAW,MAAM,KAAK,gBAAgB;AAC5C,SAAO,SAAS,cAAc,OAAO,QAAQ;CAChD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyBD,aACIC,SACyD;EACzD,MAAM,WAAW,SAAS,YAAY;AAEtC,SAAO,CAACC,KAAcC,KAAeC,SAAuB;GACxD,MAAM,QAAQ,mBAAmB,IAAI;AACrC,QAAK,OAAO;AACR,mBACI,KACA,KACA,iBACA,+CACH;AACD;GACH;GAED,MAAM,qBAAqB,YAA2B;IAClD,IAAIC;AAEJ,QAAI,aAAa,iBAAiB;KAC9B,MAAM,gBAAgB,MAAM,KAAK,gBAAgB,MAAM;AACvD,UAAK,cAAc,QAAQ;AACvB,qBAAe,KAAK,KAAK,iBAAiB,sBAAsB;AAChE;KACH;KACD,MAAM,YAAY,cAAc,WAAW,aAAa;AACxD,SACI,aACA,cAAc,YACd,cAAc,gBAChB;AACE,qBACI,KACA,KACA,iBACA,+BACH;AACD;KACH;AACD,eAAU;MACN,KAAK,cAAc,OAAO;MAC1B,KAAK,cAAc,OAAO;MAC1B,KAAK,cAAc,OAAO;MAC1B,OAAO,cAAc,SAAS;MAC9B,KAAK;MACL,KAAK,cAAc,OAAO;MAC1B,KAAK,cAAc,OAAO;MAC1B,KAAK,cAAc,OAAO;MAC1B,KAAK,cAAc,OAAO;MAC1B,IAAI,cAAc,MAAM;KAC3B;IACJ,MACG,WAAU,MAAM,KAAK,kBAAkB,MAAM;AAGjD,YAAQ,KAAK,QAAQ;AACrB,UAAM;GACT;AAED,uBAAoB,CAAC,MAAM,MAAM;AAC7B,mBACI,KACA,KACA,iBACA,kCACH;GACJ,EAAC;EACL;CACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgBD,cACI,GAAG,QACsD;AACzD,SAAO,CAACH,KAAcC,KAAeC,SAAuB;GACxD,MAAM,OAAO,QAAQ,IAAI;AACzB,QAAK,MAAM;AACP,mBAAe,KAAK,KAAK,iBAAiB,4BAA4B;AACtE;GACH;GAED,MAAM,cAAc,KAAK,MAAM,MAAM,IAAI;GACzC,MAAM,gBAAgB,OAAO,OAAO,CAAC,OAAO,YAAY,SAAS,EAAE,CAAC;AAEpE,OAAI,cAAc,SAAS,GAAG;AAC1B,mBACI,KACA,KACA,uBACC,2BAA2B,cAAc,KAAK,IAAI,CAAC,EACvD;AACD;GACH;AAED,SAAM;EACT;CACJ;CAMD,MAAc,gBAAgBE,MAAqC;EAC/D,MAAM,WAAW,KAAK,OAAO,YAAY;AACzC,MAAI,SACA,QAAO;EAGX,MAAM,YAAY,MAAM,KAAK,UAAU;EACvC,MAAM,eAAe,uBAAuB;EAC5C,MAAM,WAAW,UAAU;AAE3B,OAAK,mBAAmB,aAAa,SACjC,OAAM,IAAI,gBACN,uBACC,YAAY,KAAK;AAI1B,SAAO;CACV;CAED,MAAc,iBAAuC;AACjD,MAAI,KAAK,YACL,QAAO,KAAK;EAGhB,MAAM,UAAU,MAAM,KAAK,gBAAgB,OAAO;EAClD,MAAM,YAAY,MAAM,KAAK,UAAU;AAEvC,OAAK,cAAc,IAAI,YACnB,SACA,UAAU,QACV,KAAK,OAAO;AAEhB,SAAO,KAAK;CACf;CAED,iBAAyBC,MAA8B;EACnD,MAAM,MAAM,oBAAoB,MAAM,KAAK;AAC3C,SAAO;GACH,aAAa,IAAI;GACjB,WAAW,IAAI;GACf,WAAW,IAAI;GACf,cAAc,IAAI;GAClB,OAAO,IAAI;GACX,SAAS,IAAI;EAChB;CACJ;CAED,iBAAyBC,OAAiC;AACtD,MAAI,iBAAiB,gBACjB,QAAO;AAGX,MAAI,iBAAiB,cAAc,MAAM,UAAU;GAC/C,MAAM,SAAS,iBAAiB,UAAU,MAAM,SAAS,KAAK;AAC9D,OAAI,OAAO,QACP,QAAO,IAAI,WACP,OAAO,KAAK,OACZ,OAAO,KAAK,qBAAqB;EAG5C;EAED,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,SAAO,IAAI,gBAAgB,mBAAmB,kBAAkB,QAAQ;CAC3E;AACJ"}
|
package/package.json
CHANGED
|
@@ -1,79 +1,78 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
"name": "@nya-account/node-sdk",
|
|
3
|
+
"version": "2.0.3",
|
|
4
|
+
"description": "Official Node.js SDK for Nya Account SSO — OAuth 2.1 / OIDC client with PKCE, JWT verification, and Express middleware",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Neko Along"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/alongw/sso.git",
|
|
12
|
+
"directory": "packages/node-sdk"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/alongw/sso/tree/main/packages/node-sdk#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/alongw/sso/issues"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"sso",
|
|
20
|
+
"oauth",
|
|
21
|
+
"oauth2",
|
|
22
|
+
"oidc",
|
|
23
|
+
"openid-connect",
|
|
24
|
+
"pkce",
|
|
25
|
+
"jwt",
|
|
26
|
+
"nya-account",
|
|
27
|
+
"authentication",
|
|
28
|
+
"authorization",
|
|
29
|
+
"express-middleware"
|
|
30
|
+
],
|
|
31
|
+
"main": "./dist/index.js",
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"type": "module",
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"import": "./dist/index.js",
|
|
37
|
+
"types": "./dist/index.d.ts"
|
|
8
38
|
},
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
"types": "
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"types": "./dist/express.d.ts"
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
"files": [
|
|
45
|
-
"dist",
|
|
46
|
-
"README.md",
|
|
47
|
-
"LICENSE"
|
|
48
|
-
],
|
|
49
|
-
"scripts": {
|
|
50
|
-
"build": "tsdown",
|
|
51
|
-
"dev": "tsdown --watch",
|
|
52
|
-
"type-check": "tsc --noEmit",
|
|
53
|
-
"prepublishOnly": "pnpm run build"
|
|
54
|
-
},
|
|
55
|
-
"engines": {
|
|
56
|
-
"node": ">=20.0.0"
|
|
57
|
-
},
|
|
58
|
-
"publishConfig": {
|
|
59
|
-
"access": "public"
|
|
60
|
-
},
|
|
61
|
-
"dependencies": {
|
|
62
|
-
"axios": "^1.7.9",
|
|
63
|
-
"jose": "^6.1.3",
|
|
64
|
-
"zod": "^3.24.1"
|
|
65
|
-
},
|
|
66
|
-
"devDependencies": {
|
|
67
|
-
"@types/express": "^5.0.6",
|
|
68
|
-
"@types/node": "^22.15.21",
|
|
69
|
-
"tsdown": "^0.9.4"
|
|
70
|
-
},
|
|
71
|
-
"peerDependencies": {
|
|
72
|
-
"express": "^4.0.0 || ^5.0.0"
|
|
73
|
-
},
|
|
74
|
-
"peerDependenciesMeta": {
|
|
75
|
-
"express": {
|
|
76
|
-
"optional": true
|
|
77
|
-
}
|
|
39
|
+
"./express": {
|
|
40
|
+
"import": "./dist/express.js",
|
|
41
|
+
"types": "./dist/express.d.ts"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"files": [
|
|
45
|
+
"dist",
|
|
46
|
+
"README.md",
|
|
47
|
+
"LICENSE"
|
|
48
|
+
],
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=20.0.0"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"axios": "^1.7.9",
|
|
57
|
+
"jose": "^6.1.3",
|
|
58
|
+
"zod": "^3.24.1"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@types/express": "^5.0.6",
|
|
62
|
+
"@types/node": "^22.15.21",
|
|
63
|
+
"tsdown": "^0.9.4"
|
|
64
|
+
},
|
|
65
|
+
"peerDependencies": {
|
|
66
|
+
"express": "^4.0.0 || ^5.0.0"
|
|
67
|
+
},
|
|
68
|
+
"peerDependenciesMeta": {
|
|
69
|
+
"express": {
|
|
70
|
+
"optional": true
|
|
78
71
|
}
|
|
72
|
+
},
|
|
73
|
+
"scripts": {
|
|
74
|
+
"build": "tsdown",
|
|
75
|
+
"dev": "tsdown --watch",
|
|
76
|
+
"type-check": "tsc --noEmit"
|
|
77
|
+
}
|
|
79
78
|
}
|