@builder6/oidc-provider 3.2.3 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +421 -12
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,30 +1,439 @@
|
|
|
1
|
-
# Builder6 OIDC Provider
|
|
1
|
+
# Builder6 OIDC Provider Module
|
|
2
2
|
|
|
3
|
+
Builder6 OIDC Provider 模块为 Builder6 平台提供 OpenID Connect (OIDC) 身份提供者(Identity Provider, IdP)功能,允许 Builder6 作为认证服务器为其他应用提供单点登录(SSO)服务。
|
|
3
4
|
|
|
4
|
-
##
|
|
5
|
+
## 功能特性
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
- **OIDC 标准支持**: 完全符合 OpenID Connect 1.0 规范
|
|
8
|
+
- **多租户支持**: 支持多租户 OIDC 配置
|
|
9
|
+
- **授权码流程**: 支持 Authorization Code Flow
|
|
10
|
+
- **隐式流程**: 支持 Implicit Flow
|
|
11
|
+
- **混合流程**: 支持 Hybrid Flow
|
|
12
|
+
- **客户端凭据流**: 支持 Client Credentials Flow
|
|
13
|
+
- **刷新令牌**: 支持 Refresh Token
|
|
14
|
+
- **用户信息端点**: 提供标准的 UserInfo 端点
|
|
15
|
+
- **Discovery 端点**: 自动发现配置端点
|
|
16
|
+
- **JWT 令牌**: 使用 JWT 格式的 ID Token 和 Access Token
|
|
17
|
+
- **密码加密**: 使用 bcrypt 加密客户端密钥
|
|
18
|
+
|
|
19
|
+
## 安装
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @builder6/oidc-provider
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
或
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
yarn add @builder6/oidc-provider
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 环境变量
|
|
32
|
+
|
|
33
|
+
### 基本配置
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# OIDC 客户端 ID
|
|
37
|
+
B6_OIDC_PROVIDER_CLIENT_ID=your-client-id
|
|
38
|
+
|
|
39
|
+
# OIDC 客户端密钥
|
|
40
|
+
B6_OIDC_PROVIDER_CLIENT_SECRET=your-secret
|
|
41
|
+
|
|
42
|
+
# 允许的重定向 URI(逗号分隔)
|
|
43
|
+
B6_OIDC_PROVIDER_REDIRECT_URIS=http://localhost:5100/callback,https://app.example.com/callback
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 高级配置
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Token 过期时间(秒)
|
|
50
|
+
B6_OIDC_PROVIDER_ACCESS_TOKEN_TTL=3600
|
|
51
|
+
B6_OIDC_PROVIDER_ID_TOKEN_TTL=3600
|
|
52
|
+
B6_OIDC_PROVIDER_REFRESH_TOKEN_TTL=86400
|
|
53
|
+
|
|
54
|
+
# 支持的授权类型(空格分隔)
|
|
55
|
+
B6_OIDC_PROVIDER_GRANT_TYPES=authorization_code refresh_token
|
|
56
|
+
|
|
57
|
+
# 支持的响应类型(空格分隔)
|
|
58
|
+
B6_OIDC_PROVIDER_RESPONSE_TYPES=code id_token token
|
|
59
|
+
|
|
60
|
+
# 支持的作用域(空格分隔)
|
|
61
|
+
B6_OIDC_PROVIDER_SCOPES=openid profile email phone address
|
|
62
|
+
|
|
63
|
+
# Issuer URL(通常自动设置)
|
|
64
|
+
B6_OIDC_PROVIDER_ISSUER=https://your-domain.com
|
|
65
|
+
|
|
66
|
+
# 签名算法
|
|
67
|
+
B6_OIDC_PROVIDER_ID_TOKEN_SIGNING_ALG=RS256
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 多客户端配置
|
|
71
|
+
|
|
72
|
+
可以通过数据库配置多个 OIDC 客户端,每个客户端有独立的配置。
|
|
73
|
+
|
|
74
|
+
## 使用示例
|
|
75
|
+
|
|
76
|
+
### 在 NestJS 应用中集成
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { Module } from '@nestjs/common';
|
|
80
|
+
import { OidcProviderModule } from '@builder6/oidc-provider';
|
|
81
|
+
|
|
82
|
+
@Module({
|
|
83
|
+
imports: [OidcProviderModule],
|
|
84
|
+
})
|
|
85
|
+
export class AppModule {}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 配置 OIDC 客户端
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import { OidcProviderService } from '@builder6/oidc-provider';
|
|
92
|
+
|
|
93
|
+
constructor(private oidcProviderService: OidcProviderService) {}
|
|
94
|
+
|
|
95
|
+
async registerClient() {
|
|
96
|
+
const client = await this.oidcProviderService.registerClient({
|
|
97
|
+
client_id: 'my-app',
|
|
98
|
+
client_secret: 'secret-key',
|
|
99
|
+
redirect_uris: [
|
|
100
|
+
'https://my-app.com/callback',
|
|
101
|
+
'https://my-app.com/silent-renew'
|
|
102
|
+
],
|
|
103
|
+
grant_types: ['authorization_code', 'refresh_token'],
|
|
104
|
+
response_types: ['code'],
|
|
105
|
+
token_endpoint_auth_method: 'client_secret_post',
|
|
106
|
+
application_type: 'web'
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return client;
|
|
110
|
+
}
|
|
10
111
|
```
|
|
11
112
|
|
|
12
113
|
## OpenID Configuration URI
|
|
13
114
|
|
|
14
|
-
|
|
115
|
+
OIDC Discovery 端点提供服务器的配置信息:
|
|
116
|
+
|
|
117
|
+
```
|
|
15
118
|
http://localhost:5100/api/v6/idp/common/.well-known/openid-configuration
|
|
16
119
|
```
|
|
17
120
|
|
|
18
|
-
|
|
121
|
+
该端点返回的配置包括:
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"issuer": "http://localhost:5100/api/v6/idp/common",
|
|
126
|
+
"authorization_endpoint": "http://localhost:5100/api/v6/idp/common/auth",
|
|
127
|
+
"token_endpoint": "http://localhost:5100/api/v6/idp/common/token",
|
|
128
|
+
"userinfo_endpoint": "http://localhost:5100/api/v6/idp/common/userinfo",
|
|
129
|
+
"jwks_uri": "http://localhost:5100/api/v6/idp/common/jwks",
|
|
130
|
+
"scopes_supported": ["openid", "profile", "email"],
|
|
131
|
+
"response_types_supported": ["code", "id_token", "token"],
|
|
132
|
+
"grant_types_supported": ["authorization_code", "refresh_token"],
|
|
133
|
+
"id_token_signing_alg_values_supported": ["RS256"]
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## OIDC 端点
|
|
138
|
+
|
|
139
|
+
### 认证端点
|
|
140
|
+
|
|
141
|
+
**GET/POST `/api/v6/idp/common/auth`**
|
|
142
|
+
|
|
143
|
+
发起 OIDC 认证流程。
|
|
144
|
+
|
|
145
|
+
参数:
|
|
146
|
+
- `client_id`: 客户端 ID
|
|
147
|
+
- `redirect_uri`: 重定向 URI
|
|
148
|
+
- `response_type`: 响应类型(code, id_token, token)
|
|
149
|
+
- `scope`: 作用域(openid 必需)
|
|
150
|
+
- `state`: 状态参数(推荐)
|
|
151
|
+
- `nonce`: Nonce 值(使用 id_token 时必需)
|
|
152
|
+
|
|
153
|
+
### 令牌端点
|
|
154
|
+
|
|
155
|
+
**POST `/api/v6/idp/common/token`**
|
|
156
|
+
|
|
157
|
+
交换授权码或刷新令牌。
|
|
158
|
+
|
|
159
|
+
参数:
|
|
160
|
+
- `grant_type`: authorization_code 或 refresh_token
|
|
161
|
+
- `code`: 授权码(grant_type=authorization_code)
|
|
162
|
+
- `refresh_token`: 刷新令牌(grant_type=refresh_token)
|
|
163
|
+
- `client_id`: 客户端 ID
|
|
164
|
+
- `client_secret`: 客户端密钥
|
|
165
|
+
- `redirect_uri`: 重定向 URI
|
|
166
|
+
|
|
167
|
+
### 用户信息端点
|
|
168
|
+
|
|
169
|
+
**GET `/api/v6/idp/common/userinfo`**
|
|
170
|
+
|
|
171
|
+
获取认证用户的信息。
|
|
172
|
+
|
|
173
|
+
需要在 Authorization 头中提供有效的访问令牌:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
Authorization: Bearer ACCESS_TOKEN
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### JWKS 端点
|
|
180
|
+
|
|
181
|
+
**GET `/api/v6/idp/common/jwks`**
|
|
182
|
+
|
|
183
|
+
获取用于验证 JWT 令牌的公钥集。
|
|
184
|
+
|
|
185
|
+
## 授权流程示例
|
|
186
|
+
|
|
187
|
+
### 授权码流程(Authorization Code Flow)
|
|
188
|
+
|
|
189
|
+
这是最常用的授权流程,适用于服务器端应用。
|
|
190
|
+
|
|
191
|
+
#### 1. 重定向到认证端点
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
GET http://localhost:5100/api/v6/idp/common/auth?
|
|
195
|
+
response_type=code&
|
|
196
|
+
client_id=your-client-id&
|
|
197
|
+
redirect_uri=https://your-app.com/callback&
|
|
198
|
+
scope=openid profile email&
|
|
199
|
+
state=random-state
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
#### 2. 用户登录并授权
|
|
203
|
+
|
|
204
|
+
用户在 Builder6 登录页面登录并授权应用访问其信息。
|
|
205
|
+
|
|
206
|
+
#### 3. 重定向回应用
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
https://your-app.com/callback?
|
|
210
|
+
code=AUTHORIZATION_CODE&
|
|
211
|
+
state=random-state
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
#### 4. 交换访问令牌
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
curl -X POST http://localhost:5100/api/v6/idp/common/token \
|
|
218
|
+
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
219
|
+
-d "grant_type=authorization_code" \
|
|
220
|
+
-d "code=AUTHORIZATION_CODE" \
|
|
221
|
+
-d "client_id=your-client-id" \
|
|
222
|
+
-d "client_secret=your-secret" \
|
|
223
|
+
-d "redirect_uri=https://your-app.com/callback"
|
|
224
|
+
```
|
|
19
225
|
|
|
20
|
-
|
|
226
|
+
响应:
|
|
21
227
|
|
|
22
|
-
|
|
228
|
+
```json
|
|
229
|
+
{
|
|
230
|
+
"access_token": "ACCESS_TOKEN",
|
|
231
|
+
"token_type": "Bearer",
|
|
232
|
+
"expires_in": 3600,
|
|
233
|
+
"refresh_token": "REFRESH_TOKEN",
|
|
234
|
+
"id_token": "ID_TOKEN"
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### 5. 获取用户信息
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
curl http://localhost:5100/api/v6/idp/common/userinfo \
|
|
242
|
+
-H "Authorization: Bearer ACCESS_TOKEN"
|
|
243
|
+
```
|
|
23
244
|
|
|
24
|
-
|
|
245
|
+
响应:
|
|
25
246
|
|
|
26
|
-
```
|
|
247
|
+
```json
|
|
248
|
+
{
|
|
249
|
+
"sub": "user-id",
|
|
250
|
+
"name": "John Doe",
|
|
251
|
+
"email": "john@example.com",
|
|
252
|
+
"email_verified": true,
|
|
253
|
+
"picture": "https://example.com/avatar.jpg"
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## 调试 OIDC Provider
|
|
258
|
+
|
|
259
|
+
### 使用 OIDC Debugger
|
|
260
|
+
|
|
261
|
+
访问 [https://oidcdebugger.com/](https://oidcdebugger.com/) 进行调试。
|
|
262
|
+
|
|
263
|
+
配置:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
# Authorization Endpoint
|
|
267
|
+
http://localhost:5100/api/v6/idp/common/auth
|
|
268
|
+
|
|
269
|
+
# 环境变量
|
|
27
270
|
B6_OIDC_PROVIDER_CLIENT_ID=test
|
|
28
271
|
B6_OIDC_PROVIDER_CLIENT_SECRET=secret
|
|
29
272
|
B6_OIDC_PROVIDER_REDIRECT_URIS=https://oidcdebugger.com/debug
|
|
30
273
|
```
|
|
274
|
+
|
|
275
|
+
### 测试客户端配置
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
// 快速测试配置
|
|
279
|
+
const testConfig = {
|
|
280
|
+
client_id: 'test',
|
|
281
|
+
client_secret: 'secret',
|
|
282
|
+
redirect_uris: ['https://oidcdebugger.com/debug'],
|
|
283
|
+
grant_types: ['authorization_code'],
|
|
284
|
+
response_types: ['code']
|
|
285
|
+
};
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## 客户端集成示例
|
|
289
|
+
|
|
290
|
+
### JavaScript/TypeScript
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
import { Issuer, generators } from 'openid-client';
|
|
294
|
+
|
|
295
|
+
// 发现 OIDC 配置
|
|
296
|
+
const issuer = await Issuer.discover(
|
|
297
|
+
'http://localhost:5100/api/v6/idp/common'
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
// 创建客户端
|
|
301
|
+
const client = new issuer.Client({
|
|
302
|
+
client_id: 'your-client-id',
|
|
303
|
+
client_secret: 'your-secret',
|
|
304
|
+
redirect_uris: ['https://your-app.com/callback'],
|
|
305
|
+
response_types: ['code']
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// 生成认证 URL
|
|
309
|
+
const code_verifier = generators.codeVerifier();
|
|
310
|
+
const code_challenge = generators.codeChallenge(code_verifier);
|
|
311
|
+
|
|
312
|
+
const authUrl = client.authorizationUrl({
|
|
313
|
+
scope: 'openid profile email',
|
|
314
|
+
code_challenge,
|
|
315
|
+
code_challenge_method: 'S256'
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// 处理回调
|
|
319
|
+
const params = client.callbackParams(req);
|
|
320
|
+
const tokenSet = await client.callback(
|
|
321
|
+
'https://your-app.com/callback',
|
|
322
|
+
params,
|
|
323
|
+
{ code_verifier }
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
console.log('Access Token:', tokenSet.access_token);
|
|
327
|
+
console.log('ID Token:', tokenSet.id_token);
|
|
328
|
+
console.log('Refresh Token:', tokenSet.refresh_token);
|
|
329
|
+
|
|
330
|
+
// 获取用户信息
|
|
331
|
+
const userinfo = await client.userinfo(tokenSet.access_token);
|
|
332
|
+
console.log('User Info:', userinfo);
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Python
|
|
336
|
+
|
|
337
|
+
```python
|
|
338
|
+
from authlib.integrations.requests_client import OAuth2Session
|
|
339
|
+
|
|
340
|
+
client = OAuth2Session(
|
|
341
|
+
client_id='your-client-id',
|
|
342
|
+
client_secret='your-secret',
|
|
343
|
+
redirect_uri='https://your-app.com/callback'
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
# 获取授权 URL
|
|
347
|
+
authorization_endpoint = 'http://localhost:5100/api/v6/idp/common/auth'
|
|
348
|
+
auth_url, state = client.create_authorization_url(
|
|
349
|
+
authorization_endpoint,
|
|
350
|
+
scope='openid profile email'
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
# 交换令牌
|
|
354
|
+
token_endpoint = 'http://localhost:5100/api/v6/idp/common/token'
|
|
355
|
+
token = client.fetch_token(
|
|
356
|
+
token_endpoint,
|
|
357
|
+
authorization_response=callback_url
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
# 获取用户信息
|
|
361
|
+
userinfo_endpoint = 'http://localhost:5100/api/v6/idp/common/userinfo'
|
|
362
|
+
userinfo = client.get(userinfo_endpoint).json()
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## 多租户支持
|
|
366
|
+
|
|
367
|
+
OIDC Provider 支持多租户模式,每个租户可以有独立的 OIDC 配置:
|
|
368
|
+
|
|
369
|
+
```
|
|
370
|
+
# 租户特定的配置端点
|
|
371
|
+
http://localhost:5100/api/v6/idp/{tenantId}/.well-known/openid-configuration
|
|
372
|
+
|
|
373
|
+
# 租户特定的认证端点
|
|
374
|
+
http://localhost:5100/api/v6/idp/{tenantId}/auth
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## 安全特性
|
|
378
|
+
|
|
379
|
+
- **PKCE 支持**: 支持 Proof Key for Code Exchange,增强公共客户端安全性
|
|
380
|
+
- **State 参数**: 防止 CSRF 攻击
|
|
381
|
+
- **Nonce 验证**: 防止重放攻击
|
|
382
|
+
- **密钥加密**: 使用 bcrypt 加密客户端密钥
|
|
383
|
+
- **令牌过期**: 可配置的令牌过期时间
|
|
384
|
+
- **Refresh Token 轮换**: 可选的刷新令牌轮换机制
|
|
385
|
+
|
|
386
|
+
## 使用场景
|
|
387
|
+
|
|
388
|
+
- **企业 SSO**: 为企业内部应用提供统一认证
|
|
389
|
+
- **多应用集成**: 多个应用共享一套用户体系
|
|
390
|
+
- **第三方应用接入**: 允许第三方应用安全接入
|
|
391
|
+
- **移动应用认证**: 为移动应用提供 OAuth 认证
|
|
392
|
+
- **微服务认证**: 微服务架构中的统一认证网关
|
|
393
|
+
|
|
394
|
+
## 依赖项
|
|
395
|
+
|
|
396
|
+
### 主要依赖
|
|
397
|
+
|
|
398
|
+
- `oidc-provider`: ^9.5.2 - OIDC 提供者核心库
|
|
399
|
+
- `bcryptjs`: ^3.0.3 - 密码加密
|
|
400
|
+
- `lodash`: ^4.17.21 - 工具函数库
|
|
401
|
+
|
|
402
|
+
### Peer Dependencies
|
|
403
|
+
|
|
404
|
+
- `@builder6/core`: ^3.0.10 - 核心功能模块
|
|
405
|
+
- `@builder6/moleculer`: ^3.0.10 - 微服务框架
|
|
406
|
+
- `@nestjs/common`: ^11.0.0 - NestJS 核心
|
|
407
|
+
- `@nestjs/core`: ^11.0.0 - NestJS 核心
|
|
408
|
+
- `@nestjs/swagger`: ^11.0.7 - API 文档
|
|
409
|
+
|
|
410
|
+
## 开发
|
|
411
|
+
|
|
412
|
+
### 构建
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
npm run build
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### 监听模式
|
|
419
|
+
|
|
420
|
+
```bash
|
|
421
|
+
npm run build:watch
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### 格式化代码
|
|
425
|
+
|
|
426
|
+
```bash
|
|
427
|
+
npm run format
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
## 参考资源
|
|
431
|
+
|
|
432
|
+
- [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html)
|
|
433
|
+
- [OAuth 2.0](https://oauth.net/2/)
|
|
434
|
+
- [OIDC Provider Library](https://github.com/panva/node-oidc-provider)
|
|
435
|
+
- [OIDC Debugger](https://oidcdebugger.com/)
|
|
436
|
+
|
|
437
|
+
## License
|
|
438
|
+
|
|
439
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@builder6/oidc-provider",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": [
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"oidc-provider": "^9.5.2"
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
21
|
-
"@builder6/core": "^
|
|
22
|
-
"@builder6/moleculer": "^
|
|
21
|
+
"@builder6/core": "^4.0.0",
|
|
22
|
+
"@builder6/moleculer": "^4.0.0",
|
|
23
23
|
"@nestjs/common": "^11.0.0",
|
|
24
24
|
"@nestjs/core": "^11.0.0",
|
|
25
25
|
"@nestjs/swagger": "^11.0.7"
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"publishConfig": {
|
|
28
28
|
"access": "public"
|
|
29
29
|
},
|
|
30
|
-
"gitHead": "
|
|
30
|
+
"gitHead": "6d6e13fed8d4b3c6de14f84dcb6287c5661ea6f6",
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/oidc-provider": "^9.5.0"
|
|
33
33
|
}
|