@owlmeans/server-oidc-provider 0.1.2 → 0.1.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 +33 -816
- package/build/service.d.ts.map +1 -1
- package/build/service.js +12 -17
- package/build/service.js.map +1 -1
- package/build/utils/config.js +1 -1
- package/build/utils/config.js.map +1 -1
- package/package.json +17 -13
- package/src/service.ts +14 -19
- package/src/utils/config.ts +1 -1
- package/tests/config.spec.d.ts +2 -0
- package/tests/config.spec.d.ts.map +1 -0
- package/tests/config.spec.ts +41 -0
- package/tests/tsconfig.json +12 -0
- package/tsconfig.json +6 -5
package/README.md
CHANGED
|
@@ -1,848 +1,65 @@
|
|
|
1
1
|
# @owlmeans/server-oidc-provider
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Embedded OIDC identity provider — wraps the `oidc-provider` library as an OwlMeans service.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
The `@owlmeans/server-oidc-provider` package delivers a full-featured OIDC identity provider with:
|
|
5
|
+
> Use this package only when your service IS the identity provider (Keycloak alternative). For consuming an external IdP, use [`@owlmeans/server-oidc-rp`](../server-oidc-rp).
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
- **Fastify Integration**: Seamless integration with OwlMeans server API framework
|
|
11
|
-
- **Account Management**: Pluggable account service for user authentication
|
|
12
|
-
- **Adapter Support**: Configurable storage adapters for sessions and tokens
|
|
13
|
-
- **Client Management**: Dynamic client registration and configuration
|
|
14
|
-
- **Interaction Flows**: Customizable authentication and consent flows
|
|
15
|
-
- **Security Features**: JWT signing, HTTPS support, and secure cookie handling
|
|
16
|
-
- **Proxy Support**: Works behind reverse proxies and load balancers
|
|
7
|
+
## Overview
|
|
17
8
|
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
- **@owlmeans/web-oidc-provider**: Web UI for OIDC provider
|
|
23
|
-
- **@owlmeans/web-oidc-rp**: Web-based relying party
|
|
9
|
+
- `createOidcProviderService(alias?)` — factory for the embedded provider service
|
|
10
|
+
- `appendOidcProviderService(context, alias?)` — registers the service on a context
|
|
11
|
+
- `createOidcProviderMiddleware(webAlias?, oidcAlias?)` — middleware mounting the OIDC endpoints (authorize, token, userinfo, jwks)
|
|
12
|
+
- Hosts the interaction UI route alias `oidc:interaction`
|
|
24
13
|
|
|
25
14
|
## Installation
|
|
26
15
|
|
|
27
16
|
```bash
|
|
28
|
-
|
|
17
|
+
bun add @owlmeans/server-oidc-provider
|
|
29
18
|
```
|
|
30
19
|
|
|
31
|
-
##
|
|
32
|
-
|
|
33
|
-
This package requires:
|
|
34
|
-
- `oidc-provider`: Industry-standard OIDC provider implementation
|
|
35
|
-
- `@owlmeans/server-api`: Server API framework with Fastify
|
|
36
|
-
- `@owlmeans/server-context`: Server context management
|
|
37
|
-
- `@owlmeans/oidc`: Core OIDC functionality
|
|
38
|
-
- `@owlmeans/config`: Configuration management
|
|
39
|
-
- `jose`: JWT operations
|
|
40
|
-
- `fastify`: HTTP server framework (peer dependency)
|
|
41
|
-
|
|
42
|
-
## Core Concepts
|
|
43
|
-
|
|
44
|
-
### OIDC Provider Service
|
|
45
|
-
|
|
46
|
-
The OIDC provider service manages the complete lifecycle of an OpenID Connect identity provider, including client management, user authentication, token issuance, and session management.
|
|
47
|
-
|
|
48
|
-
### Account Service
|
|
49
|
-
|
|
50
|
-
Pluggable account service interface that handles user authentication and account loading for the OIDC provider.
|
|
51
|
-
|
|
52
|
-
### Adapter Service
|
|
20
|
+
## Usage
|
|
53
21
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
### Interaction Flows
|
|
57
|
-
|
|
58
|
-
Customizable authentication and consent flows that work with the OwlMeans client module system.
|
|
59
|
-
|
|
60
|
-
## API Reference
|
|
61
|
-
|
|
62
|
-
### Types
|
|
63
|
-
|
|
64
|
-
#### `OidcProviderService`
|
|
65
|
-
|
|
66
|
-
Main OIDC provider service interface.
|
|
22
|
+
Register the service and mount the middleware:
|
|
67
23
|
|
|
68
24
|
```typescript
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
#### `OidcConfig`
|
|
78
|
-
|
|
79
|
-
Configuration interface for OIDC provider.
|
|
25
|
+
import {
|
|
26
|
+
createOidcProviderService,
|
|
27
|
+
appendOidcProviderService,
|
|
28
|
+
createOidcProviderMiddleware
|
|
29
|
+
} from '@owlmeans/server-oidc-provider'
|
|
80
30
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
authService?: string // Authentication service name
|
|
84
|
-
basePath?: string // Base path for OIDC endpoints
|
|
85
|
-
frontBase?: string // Frontend base URL
|
|
86
|
-
clients: ClientMetadata[] // OIDC client configurations
|
|
87
|
-
customConfiguration?: Configuration // Custom oidc-provider config
|
|
88
|
-
behindProxy?: boolean // Behind reverse proxy flag
|
|
89
|
-
defaultKeys: { // Default signing keys
|
|
90
|
-
RS256: {
|
|
91
|
-
pk: string // Private key (PEM format)
|
|
92
|
-
pub?: string // Public key (PEM format)
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
accountService?: string // Account service name
|
|
96
|
-
adapterService?: string // Adapter service name
|
|
97
|
-
}
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
#### `OidcAccountService`
|
|
101
|
-
|
|
102
|
-
Interface for user account management.
|
|
103
|
-
|
|
104
|
-
```typescript
|
|
105
|
-
interface OidcAccountService extends InitializedService {
|
|
106
|
-
loadById<C extends Config, T extends Context<C>>(
|
|
107
|
-
ctx: T,
|
|
108
|
-
id: string
|
|
109
|
-
): Promise<Account | undefined>
|
|
110
|
-
}
|
|
31
|
+
appendOidcProviderService<C, T>(context)
|
|
32
|
+
context.registerMiddleware(createOidcProviderMiddleware())
|
|
111
33
|
```
|
|
112
34
|
|
|
113
|
-
|
|
35
|
+
The provider exposes the standard OIDC endpoints under the configured base path; the interaction screen is mounted at the path defined by `INTERACTION_PATH` from `@owlmeans/oidc`.
|
|
114
36
|
|
|
115
|
-
|
|
37
|
+
## API
|
|
116
38
|
|
|
117
|
-
|
|
118
|
-
interface OidcAdapterService extends InitializedService {
|
|
119
|
-
instance(name: string): Adapter
|
|
120
|
-
}
|
|
121
|
-
```
|
|
39
|
+
### `createOidcProviderService(alias?): OidcProviderService`
|
|
122
40
|
|
|
123
|
-
|
|
41
|
+
Creates the embedded OIDC provider service. `alias` defaults to `DEFAULT_ALIAS` (`'oidc-provider'`).
|
|
124
42
|
|
|
125
|
-
|
|
43
|
+
### `appendOidcProviderService<C, T>(context, alias?): T`
|
|
126
44
|
|
|
127
|
-
|
|
128
|
-
interface Config extends ServerConfig, OidcConfigAppend {
|
|
129
|
-
debug: ServerConfig["debug"] & {
|
|
130
|
-
oidc?: boolean // General OIDC debugging
|
|
131
|
-
oidcServer?: boolean // Server-specific debugging
|
|
132
|
-
oidcData?: boolean // Data operation debugging
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
```
|
|
45
|
+
Registers the provider service on the context.
|
|
136
46
|
|
|
137
|
-
###
|
|
138
|
-
|
|
139
|
-
#### `createOidcProviderService(alias?: string): OidcProviderService`
|
|
140
|
-
|
|
141
|
-
Creates an OIDC provider service instance.
|
|
142
|
-
|
|
143
|
-
**Parameters:**
|
|
144
|
-
- `alias`: Service alias (default: 'oidc-provider')
|
|
145
|
-
|
|
146
|
-
**Returns:** OidcProviderService instance
|
|
147
|
-
|
|
148
|
-
**Features:**
|
|
149
|
-
- Automatic provider configuration based on context settings
|
|
150
|
-
- Integration with account and adapter services
|
|
151
|
-
- Fastify middleware integration
|
|
152
|
-
- JWT key management
|
|
153
|
-
- Client configuration management
|
|
154
|
-
|
|
155
|
-
**Example:**
|
|
156
|
-
```typescript
|
|
157
|
-
import { createOidcProviderService } from '@owlmeans/server-oidc-provider'
|
|
158
|
-
|
|
159
|
-
const oidcProvider = createOidcProviderService('main-oidc')
|
|
160
|
-
```
|
|
47
|
+
### `createOidcProviderMiddleware(webAlias?, oidcAlias?)`
|
|
161
48
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
Creates middleware to integrate OIDC provider with API server.
|
|
165
|
-
|
|
166
|
-
**Parameters:**
|
|
167
|
-
- `web`: API server service alias (default: 'api-server')
|
|
168
|
-
- `oidc`: OIDC provider service alias (default: 'oidc-provider')
|
|
169
|
-
|
|
170
|
-
**Returns:** Context middleware
|
|
171
|
-
|
|
172
|
-
**Example:**
|
|
173
|
-
```typescript
|
|
174
|
-
import { createOidcProviderMiddleware } from '@owlmeans/server-oidc-provider'
|
|
175
|
-
|
|
176
|
-
const oidcMiddleware = createOidcProviderMiddleware('api', 'oidc')
|
|
177
|
-
context.registerMiddleware(oidcMiddleware)
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
### Service Methods
|
|
181
|
-
|
|
182
|
-
#### `update(api: ApiServer): Promise<void>`
|
|
183
|
-
|
|
184
|
-
Updates the OIDC provider with the API server, registering all OIDC endpoints.
|
|
185
|
-
|
|
186
|
-
**Parameters:**
|
|
187
|
-
- `api`: API server instance to integrate with
|
|
188
|
-
|
|
189
|
-
**Process:**
|
|
190
|
-
1. Configures oidc-provider instance with context settings
|
|
191
|
-
2. Sets up account and adapter services
|
|
192
|
-
3. Configures interaction flows
|
|
193
|
-
4. Registers OIDC endpoints with Fastify server
|
|
194
|
-
|
|
195
|
-
**Example:**
|
|
196
|
-
```typescript
|
|
197
|
-
const oidcService = context.service<OidcProviderService>('oidc-provider')
|
|
198
|
-
const apiServer = context.service<ApiServer>('api')
|
|
199
|
-
|
|
200
|
-
await oidcService.update(apiServer)
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
#### `instance(): Provider`
|
|
204
|
-
|
|
205
|
-
Gets the underlying oidc-provider instance for direct access.
|
|
206
|
-
|
|
207
|
-
**Returns:** oidc-provider Provider instance
|
|
208
|
-
|
|
209
|
-
**Example:**
|
|
210
|
-
```typescript
|
|
211
|
-
const provider = oidcService.instance()
|
|
212
|
-
|
|
213
|
-
// Access oidc-provider methods directly
|
|
214
|
-
const client = await provider.Client.find('client-id')
|
|
215
|
-
const interaction = await provider.interactionDetails(req, res)
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
#### `getInteraction(id: string): Promise<Interaction | null>`
|
|
219
|
-
|
|
220
|
-
Retrieves an interaction by its unique identifier.
|
|
221
|
-
|
|
222
|
-
**Parameters:**
|
|
223
|
-
- `id`: Interaction identifier
|
|
224
|
-
|
|
225
|
-
**Returns:** Promise resolving to Interaction object or null
|
|
226
|
-
|
|
227
|
-
**Example:**
|
|
228
|
-
```typescript
|
|
229
|
-
const interaction = await oidcService.getInteraction('interaction-uuid')
|
|
230
|
-
if (interaction) {
|
|
231
|
-
console.log('Interaction details:', interaction.params)
|
|
232
|
-
}
|
|
233
|
-
```
|
|
49
|
+
Returns middleware that mounts the OIDC authorize/token/userinfo/jwks endpoints onto the web app.
|
|
234
50
|
|
|
235
51
|
### Constants
|
|
236
52
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
const OIDC_ACCOUNT_SERVICE = 'oidc-account-service' // Default account service name
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
## Usage Examples
|
|
243
|
-
|
|
244
|
-
### Basic OIDC Provider Setup
|
|
245
|
-
|
|
246
|
-
```typescript
|
|
247
|
-
import { createOidcProviderService, createOidcProviderMiddleware } from '@owlmeans/server-oidc-provider'
|
|
248
|
-
import { makeServerContext } from '@owlmeans/server-context'
|
|
249
|
-
import { createApiServer } from '@owlmeans/server-api'
|
|
250
|
-
|
|
251
|
-
// Create server configuration with OIDC settings
|
|
252
|
-
const config = {
|
|
253
|
-
service: 'auth-server',
|
|
254
|
-
oidc: {
|
|
255
|
-
basePath: 'oidc',
|
|
256
|
-
clients: [
|
|
257
|
-
{
|
|
258
|
-
client_id: 'my-web-app',
|
|
259
|
-
client_secret: 'secure-client-secret',
|
|
260
|
-
redirect_uris: ['https://myapp.com/callback'],
|
|
261
|
-
response_types: ['code'],
|
|
262
|
-
grant_types: ['authorization_code', 'refresh_token'],
|
|
263
|
-
scope: 'openid profile email'
|
|
264
|
-
}
|
|
265
|
-
],
|
|
266
|
-
defaultKeys: {
|
|
267
|
-
RS256: {
|
|
268
|
-
pk: `-----BEGIN PRIVATE KEY-----
|
|
269
|
-
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
|
|
270
|
-
-----END PRIVATE KEY-----`,
|
|
271
|
-
pub: `-----BEGIN PUBLIC KEY-----
|
|
272
|
-
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtfGqPGwp...
|
|
273
|
-
-----END PUBLIC KEY-----`
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Create context and services
|
|
280
|
-
const context = makeServerContext(config)
|
|
281
|
-
const apiServer = createApiServer('api')
|
|
282
|
-
const oidcProvider = createOidcProviderService('oidc')
|
|
53
|
+
- `DEFAULT_ALIAS` — `'oidc-provider'`
|
|
54
|
+
- `OIDC_ACCOUNT_SERVICE` — `'oidc-account-service'` — alias for the user account adapter
|
|
283
55
|
|
|
284
|
-
|
|
285
|
-
context.registerService(apiServer)
|
|
286
|
-
context.registerService(oidcProvider)
|
|
287
|
-
context.registerMiddleware(createOidcProviderMiddleware('api', 'oidc'))
|
|
288
|
-
|
|
289
|
-
// Initialize and start
|
|
290
|
-
await context.configure().init()
|
|
291
|
-
await apiServer.listen()
|
|
292
|
-
|
|
293
|
-
// OIDC endpoints now available at /oidc/*
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
### Custom Account Service Implementation
|
|
297
|
-
|
|
298
|
-
```typescript
|
|
299
|
-
import { createService } from '@owlmeans/context'
|
|
300
|
-
import type { OidcAccountService } from '@owlmeans/server-oidc-provider'
|
|
301
|
-
|
|
302
|
-
// Implement account service
|
|
303
|
-
const accountService: OidcAccountService = createService('oidc-account-service', {
|
|
304
|
-
loadById: async (ctx, id) => {
|
|
305
|
-
// Load user account from your database
|
|
306
|
-
const user = await getUserById(id)
|
|
307
|
-
|
|
308
|
-
if (!user) {
|
|
309
|
-
return undefined
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// Return oidc-provider Account object
|
|
313
|
-
return {
|
|
314
|
-
accountId: user.id,
|
|
315
|
-
|
|
316
|
-
// Standard claims
|
|
317
|
-
claims: async (use, scope, claims, rejected) => {
|
|
318
|
-
const standardClaims = {
|
|
319
|
-
sub: user.id,
|
|
320
|
-
email: user.email,
|
|
321
|
-
email_verified: user.emailVerified,
|
|
322
|
-
name: user.fullName,
|
|
323
|
-
given_name: user.firstName,
|
|
324
|
-
family_name: user.lastName,
|
|
325
|
-
picture: user.avatar
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Filter claims based on scope and requested claims
|
|
329
|
-
const result = {}
|
|
330
|
-
if (scope.includes('email')) {
|
|
331
|
-
result.email = standardClaims.email
|
|
332
|
-
result.email_verified = standardClaims.email_verified
|
|
333
|
-
}
|
|
334
|
-
if (scope.includes('profile')) {
|
|
335
|
-
result.name = standardClaims.name
|
|
336
|
-
result.given_name = standardClaims.given_name
|
|
337
|
-
result.family_name = standardClaims.family_name
|
|
338
|
-
result.picture = standardClaims.picture
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
return result
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
})
|
|
346
|
-
|
|
347
|
-
// Register account service
|
|
348
|
-
context.registerService(accountService)
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
### Custom Storage Adapter
|
|
352
|
-
|
|
353
|
-
```typescript
|
|
354
|
-
import { createService } from '@owlmeans/context'
|
|
355
|
-
import type { OidcAdapterService } from '@owlmeans/server-oidc-provider'
|
|
356
|
-
|
|
357
|
-
// Implement Redis-based adapter service
|
|
358
|
-
const adapterService: OidcAdapterService = createService('oidc-adapter-service', {
|
|
359
|
-
instance: (name) => {
|
|
360
|
-
// Return adapter for specific model (Session, AccessToken, etc.)
|
|
361
|
-
return {
|
|
362
|
-
async upsert(id, payload, expiresIn) {
|
|
363
|
-
const key = `oidc:${name}:${id}`
|
|
364
|
-
await redis.setex(key, expiresIn, JSON.stringify(payload))
|
|
365
|
-
},
|
|
366
|
-
|
|
367
|
-
async find(id) {
|
|
368
|
-
const key = `oidc:${name}:${id}`
|
|
369
|
-
const data = await redis.get(key)
|
|
370
|
-
return data ? JSON.parse(data) : undefined
|
|
371
|
-
},
|
|
372
|
-
|
|
373
|
-
async findByUserCode(userCode) {
|
|
374
|
-
// Implement user code lookup for device flow
|
|
375
|
-
const keys = await redis.keys(`oidc:${name}:*`)
|
|
376
|
-
for (const key of keys) {
|
|
377
|
-
const data = await redis.get(key)
|
|
378
|
-
const payload = JSON.parse(data)
|
|
379
|
-
if (payload.userCode === userCode) {
|
|
380
|
-
return payload
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
return undefined
|
|
384
|
-
},
|
|
385
|
-
|
|
386
|
-
async findByUid(uid) {
|
|
387
|
-
// Implement UID-based lookup
|
|
388
|
-
const keys = await redis.keys(`oidc:${name}:*`)
|
|
389
|
-
for (const key of keys) {
|
|
390
|
-
const data = await redis.get(key)
|
|
391
|
-
const payload = JSON.parse(data)
|
|
392
|
-
if (payload.uid === uid) {
|
|
393
|
-
return payload
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
return undefined
|
|
397
|
-
},
|
|
398
|
-
|
|
399
|
-
async destroy(id) {
|
|
400
|
-
const key = `oidc:${name}:${id}`
|
|
401
|
-
await redis.del(key)
|
|
402
|
-
},
|
|
403
|
-
|
|
404
|
-
async revokeByGrantId(grantId) {
|
|
405
|
-
const keys = await redis.keys(`oidc:${name}:*`)
|
|
406
|
-
for (const key of keys) {
|
|
407
|
-
const data = await redis.get(key)
|
|
408
|
-
const payload = JSON.parse(data)
|
|
409
|
-
if (payload.grantId === grantId) {
|
|
410
|
-
await redis.del(key)
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
},
|
|
414
|
-
|
|
415
|
-
async consume(id) {
|
|
416
|
-
const key = `oidc:${name}:${id}`
|
|
417
|
-
const data = await redis.get(key)
|
|
418
|
-
if (data) {
|
|
419
|
-
const payload = JSON.parse(data)
|
|
420
|
-
payload.consumed = Math.floor(Date.now() / 1000)
|
|
421
|
-
await redis.setex(key, payload.exp - Math.floor(Date.now() / 1000), JSON.stringify(payload))
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
})
|
|
427
|
-
|
|
428
|
-
// Register adapter service
|
|
429
|
-
context.registerService(adapterService)
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
### Advanced OIDC Configuration
|
|
433
|
-
|
|
434
|
-
```typescript
|
|
435
|
-
const advancedConfig = {
|
|
436
|
-
oidc: {
|
|
437
|
-
basePath: 'auth',
|
|
438
|
-
behindProxy: true,
|
|
439
|
-
accountService: 'custom-account-service',
|
|
440
|
-
adapterService: 'redis-adapter-service',
|
|
441
|
-
|
|
442
|
-
clients: [
|
|
443
|
-
// Web application
|
|
444
|
-
{
|
|
445
|
-
client_id: 'web-app',
|
|
446
|
-
client_secret: 'web-app-secret',
|
|
447
|
-
redirect_uris: [
|
|
448
|
-
'https://app.example.com/callback',
|
|
449
|
-
'https://staging.app.example.com/callback'
|
|
450
|
-
],
|
|
451
|
-
post_logout_redirect_uris: [
|
|
452
|
-
'https://app.example.com/logout'
|
|
453
|
-
],
|
|
454
|
-
response_types: ['code'],
|
|
455
|
-
grant_types: ['authorization_code', 'refresh_token'],
|
|
456
|
-
scope: 'openid profile email offline_access',
|
|
457
|
-
token_endpoint_auth_method: 'client_secret_basic'
|
|
458
|
-
},
|
|
459
|
-
|
|
460
|
-
// Mobile application (public client)
|
|
461
|
-
{
|
|
462
|
-
client_id: 'mobile-app',
|
|
463
|
-
redirect_uris: ['com.example.app://callback'],
|
|
464
|
-
response_types: ['code'],
|
|
465
|
-
grant_types: ['authorization_code', 'refresh_token'],
|
|
466
|
-
scope: 'openid profile email offline_access',
|
|
467
|
-
token_endpoint_auth_method: 'none', // Public client
|
|
468
|
-
require_auth_time: true
|
|
469
|
-
},
|
|
470
|
-
|
|
471
|
-
// SPA (Single Page Application)
|
|
472
|
-
{
|
|
473
|
-
client_id: 'spa-app',
|
|
474
|
-
redirect_uris: ['https://spa.example.com/callback'],
|
|
475
|
-
response_types: ['code'],
|
|
476
|
-
grant_types: ['authorization_code'],
|
|
477
|
-
scope: 'openid profile email',
|
|
478
|
-
token_endpoint_auth_method: 'none',
|
|
479
|
-
require_auth_time: false
|
|
480
|
-
}
|
|
481
|
-
],
|
|
482
|
-
|
|
483
|
-
// Custom oidc-provider configuration
|
|
484
|
-
customConfiguration: {
|
|
485
|
-
features: {
|
|
486
|
-
devInteractions: { enabled: false },
|
|
487
|
-
deviceFlow: { enabled: true },
|
|
488
|
-
revocation: { enabled: true },
|
|
489
|
-
introspection: { enabled: true },
|
|
490
|
-
userinfo: { enabled: true }
|
|
491
|
-
},
|
|
492
|
-
|
|
493
|
-
ttl: {
|
|
494
|
-
AccessToken: 60 * 60, // 1 hour
|
|
495
|
-
AuthorizationCode: 10 * 60, // 10 minutes
|
|
496
|
-
IdToken: 60 * 60, // 1 hour
|
|
497
|
-
DeviceCode: 10 * 60, // 10 minutes
|
|
498
|
-
RefreshToken: 14 * 24 * 60 * 60 // 14 days
|
|
499
|
-
},
|
|
500
|
-
|
|
501
|
-
claims: {
|
|
502
|
-
openid: ['sub'],
|
|
503
|
-
email: ['email', 'email_verified'],
|
|
504
|
-
profile: ['name', 'family_name', 'given_name', 'picture']
|
|
505
|
-
},
|
|
506
|
-
|
|
507
|
-
scopes: ['openid', 'email', 'profile', 'offline_access']
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
### Interaction Flow Integration
|
|
514
|
-
|
|
515
|
-
```typescript
|
|
516
|
-
import { modules } from '@owlmeans/oidc'
|
|
517
|
-
import { module, handleRequest } from '@owlmeans/module'
|
|
518
|
-
import { route, frontend } from '@owlmeans/route'
|
|
519
|
-
|
|
520
|
-
// Register OIDC interaction modules
|
|
521
|
-
context.registerModules(modules)
|
|
522
|
-
|
|
523
|
-
// Custom login interaction handler
|
|
524
|
-
const loginModule = module(
|
|
525
|
-
route('oidc-login', '/oidc/interaction/:uid/login', frontend()),
|
|
526
|
-
{
|
|
527
|
-
handle: handleRequest(async (req, ctx) => {
|
|
528
|
-
const { uid } = req.params
|
|
529
|
-
const oidcService = ctx.service<OidcProviderService>('oidc-provider')
|
|
530
|
-
|
|
531
|
-
// Get interaction details
|
|
532
|
-
const interaction = await oidcService.getInteraction(uid)
|
|
533
|
-
|
|
534
|
-
if (!interaction) {
|
|
535
|
-
throw new Error('Interaction not found')
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
// Return login form data
|
|
539
|
-
return {
|
|
540
|
-
interaction: {
|
|
541
|
-
uid: interaction.uid,
|
|
542
|
-
params: interaction.params,
|
|
543
|
-
prompt: interaction.prompt
|
|
544
|
-
},
|
|
545
|
-
client: interaction.params.client_id
|
|
546
|
-
}
|
|
547
|
-
})
|
|
548
|
-
}
|
|
549
|
-
)
|
|
550
|
-
|
|
551
|
-
context.registerModule(loginModule)
|
|
552
|
-
```
|
|
553
|
-
|
|
554
|
-
### Multi-Tenant OIDC Setup
|
|
555
|
-
|
|
556
|
-
```typescript
|
|
557
|
-
// Multi-tenant configuration with entity-specific clients
|
|
558
|
-
const multiTenantConfig = {
|
|
559
|
-
oidc: {
|
|
560
|
-
clients: [
|
|
561
|
-
// Tenant A
|
|
562
|
-
{
|
|
563
|
-
client_id: 'tenant-a-web',
|
|
564
|
-
client_secret: 'tenant-a-secret',
|
|
565
|
-
redirect_uris: ['https://tenant-a.example.com/callback'],
|
|
566
|
-
response_types: ['code'],
|
|
567
|
-
grant_types: ['authorization_code', 'refresh_token'],
|
|
568
|
-
scope: 'openid profile email'
|
|
569
|
-
},
|
|
570
|
-
|
|
571
|
-
// Tenant B
|
|
572
|
-
{
|
|
573
|
-
client_id: 'tenant-b-web',
|
|
574
|
-
client_secret: 'tenant-b-secret',
|
|
575
|
-
redirect_uris: ['https://tenant-b.example.com/callback'],
|
|
576
|
-
response_types: ['code'],
|
|
577
|
-
grant_types: ['authorization_code', 'refresh_token'],
|
|
578
|
-
scope: 'openid profile email'
|
|
579
|
-
}
|
|
580
|
-
],
|
|
581
|
-
|
|
582
|
-
// Tenant-aware account service
|
|
583
|
-
accountService: 'multi-tenant-account-service'
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
// Multi-tenant account service
|
|
588
|
-
const multiTenantAccountService = createService('multi-tenant-account-service', {
|
|
589
|
-
loadById: async (ctx, id) => {
|
|
590
|
-
// Extract tenant from ID or use other tenant identification
|
|
591
|
-
const [tenantId, userId] = id.split(':')
|
|
592
|
-
|
|
593
|
-
const user = await getUserByIdAndTenant(userId, tenantId)
|
|
594
|
-
|
|
595
|
-
if (!user) {
|
|
596
|
-
return undefined
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
return {
|
|
600
|
-
accountId: id,
|
|
601
|
-
claims: async (use, scope) => {
|
|
602
|
-
return {
|
|
603
|
-
sub: id,
|
|
604
|
-
email: user.email,
|
|
605
|
-
name: user.name,
|
|
606
|
-
tenant: tenantId // Custom claim
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
})
|
|
612
|
-
```
|
|
613
|
-
|
|
614
|
-
### Development and Testing Configuration
|
|
615
|
-
|
|
616
|
-
```typescript
|
|
617
|
-
// Development configuration with debugging
|
|
618
|
-
const devConfig = {
|
|
619
|
-
debug: {
|
|
620
|
-
oidc: true,
|
|
621
|
-
oidcServer: true,
|
|
622
|
-
oidcData: true
|
|
623
|
-
},
|
|
624
|
-
|
|
625
|
-
oidc: {
|
|
626
|
-
customConfiguration: {
|
|
627
|
-
features: {
|
|
628
|
-
devInteractions: { enabled: true }, // Enable dev interactions
|
|
629
|
-
},
|
|
630
|
-
|
|
631
|
-
// More permissive settings for development
|
|
632
|
-
conformIdTokenClaims: false,
|
|
633
|
-
|
|
634
|
-
// Custom cookies for development
|
|
635
|
-
cookies: {
|
|
636
|
-
keys: ['dev-cookie-secret'],
|
|
637
|
-
secure: false, // Allow non-HTTPS in development
|
|
638
|
-
sameSite: 'lax'
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
```
|
|
644
|
-
|
|
645
|
-
## Advanced Features
|
|
646
|
-
|
|
647
|
-
### Custom Claims and Scopes
|
|
648
|
-
|
|
649
|
-
```typescript
|
|
650
|
-
const customConfig = {
|
|
651
|
-
oidc: {
|
|
652
|
-
customConfiguration: {
|
|
653
|
-
// Define custom scopes
|
|
654
|
-
scopes: ['openid', 'email', 'profile', 'admin', 'api:read', 'api:write'],
|
|
655
|
-
|
|
656
|
-
// Map scopes to claims
|
|
657
|
-
claims: {
|
|
658
|
-
openid: ['sub'],
|
|
659
|
-
email: ['email', 'email_verified'],
|
|
660
|
-
profile: ['name', 'family_name', 'given_name', 'picture', 'locale'],
|
|
661
|
-
admin: ['role', 'permissions'],
|
|
662
|
-
'api:read': ['api_access'],
|
|
663
|
-
'api:write': ['api_access', 'write_permissions']
|
|
664
|
-
},
|
|
665
|
-
|
|
666
|
-
// Custom claim mapping
|
|
667
|
-
extraParams: ['tenant_id', 'organization'],
|
|
668
|
-
|
|
669
|
-
// Custom token format
|
|
670
|
-
formats: {
|
|
671
|
-
AccessToken: 'jwt',
|
|
672
|
-
ClientCredentials: 'jwt'
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
```
|
|
678
|
-
|
|
679
|
-
### Dynamic Client Registration
|
|
680
|
-
|
|
681
|
-
```typescript
|
|
682
|
-
const dynamicClientConfig = {
|
|
683
|
-
oidc: {
|
|
684
|
-
customConfiguration: {
|
|
685
|
-
features: {
|
|
686
|
-
registration: { enabled: true },
|
|
687
|
-
registrationManagement: { enabled: true }
|
|
688
|
-
},
|
|
689
|
-
|
|
690
|
-
// Client validation
|
|
691
|
-
clientDefaults: {
|
|
692
|
-
grant_types: ['authorization_code'],
|
|
693
|
-
response_types: ['code'],
|
|
694
|
-
token_endpoint_auth_method: 'client_secret_basic'
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
// Handle dynamic client registration
|
|
701
|
-
const clientRegistrationModule = module(
|
|
702
|
-
route('client-registration', '/oidc/reg', backend('POST')),
|
|
703
|
-
{
|
|
704
|
-
handle: handleRequest(async (req, ctx) => {
|
|
705
|
-
const oidcService = ctx.service<OidcProviderService>('oidc-provider')
|
|
706
|
-
const provider = oidcService.instance()
|
|
707
|
-
|
|
708
|
-
// Custom client validation logic
|
|
709
|
-
const clientMetadata = req.body
|
|
710
|
-
|
|
711
|
-
// Register client with oidc-provider
|
|
712
|
-
const client = await provider.Client.create(clientMetadata)
|
|
713
|
-
|
|
714
|
-
return {
|
|
715
|
-
client_id: client.clientId,
|
|
716
|
-
client_secret: client.clientSecret,
|
|
717
|
-
registration_access_token: client.registrationAccessToken
|
|
718
|
-
}
|
|
719
|
-
})
|
|
720
|
-
}
|
|
721
|
-
)
|
|
722
|
-
```
|
|
723
|
-
|
|
724
|
-
### Integration with External Identity Providers
|
|
725
|
-
|
|
726
|
-
```typescript
|
|
727
|
-
// Federation with external IdPs
|
|
728
|
-
const federatedConfig = {
|
|
729
|
-
oidc: {
|
|
730
|
-
customConfiguration: {
|
|
731
|
-
// Custom interaction handling for federation
|
|
732
|
-
interactions: {
|
|
733
|
-
url: async (ctx, interaction) => {
|
|
734
|
-
// Custom logic to determine if user should be redirected to external IdP
|
|
735
|
-
if (interaction.params.federated_idp) {
|
|
736
|
-
return `/oidc/federate/${interaction.params.federated_idp}/${interaction.uid}`
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
return `/oidc/interaction/${interaction.uid}`
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
```
|
|
746
|
-
|
|
747
|
-
## Security Considerations
|
|
748
|
-
|
|
749
|
-
### Key Management
|
|
750
|
-
- Use strong RSA keys (2048-bit minimum) for JWT signing
|
|
751
|
-
- Rotate signing keys periodically
|
|
752
|
-
- Store private keys securely using environment variables or secret management
|
|
753
|
-
- Use different keys for different environments
|
|
754
|
-
|
|
755
|
-
### HTTPS and Proxy Configuration
|
|
756
|
-
- Always use HTTPS in production
|
|
757
|
-
- Configure `behindProxy` correctly when behind reverse proxies
|
|
758
|
-
- Use secure cookie settings in production
|
|
759
|
-
- Implement proper CORS policies
|
|
760
|
-
|
|
761
|
-
### Client Security
|
|
762
|
-
- Use strong client secrets for confidential clients
|
|
763
|
-
- Validate redirect URIs strictly
|
|
764
|
-
- Implement proper logout handling
|
|
765
|
-
- Use appropriate token lifetimes
|
|
766
|
-
|
|
767
|
-
### Session Management
|
|
768
|
-
- Use secure session storage
|
|
769
|
-
- Implement proper session cleanup
|
|
770
|
-
- Monitor for session hijacking attempts
|
|
771
|
-
- Use appropriate session timeouts
|
|
772
|
-
|
|
773
|
-
## Best Practices
|
|
774
|
-
|
|
775
|
-
1. **Configuration**: Use environment-specific configurations
|
|
776
|
-
2. **Account Service**: Implement robust user authentication
|
|
777
|
-
3. **Adapter Service**: Use persistent storage for production
|
|
778
|
-
4. **Client Management**: Validate client configurations thoroughly
|
|
779
|
-
5. **Security**: Follow OIDC security best practices
|
|
780
|
-
6. **Monitoring**: Implement logging and monitoring
|
|
781
|
-
7. **Testing**: Test all authentication flows thoroughly
|
|
782
|
-
|
|
783
|
-
## Error Handling
|
|
784
|
-
|
|
785
|
-
```typescript
|
|
786
|
-
try {
|
|
787
|
-
const oidcService = createOidcProviderService()
|
|
788
|
-
await oidcService.update(apiServer)
|
|
789
|
-
} catch (error) {
|
|
790
|
-
console.error('OIDC Provider setup failed:', error)
|
|
791
|
-
|
|
792
|
-
// Handle specific configuration errors
|
|
793
|
-
if (error.message.includes('missing key')) {
|
|
794
|
-
console.error('JWT signing key not configured')
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
if (error.message.includes('client')) {
|
|
798
|
-
console.error('Client configuration invalid')
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
```
|
|
802
|
-
|
|
803
|
-
## Integration with OwlMeans Ecosystem
|
|
804
|
-
|
|
805
|
-
### Server API Integration
|
|
806
|
-
```typescript
|
|
807
|
-
import { createApiServer } from '@owlmeans/server-api'
|
|
808
|
-
|
|
809
|
-
// OIDC provider integrates seamlessly with server API
|
|
810
|
-
const apiServer = createApiServer('api')
|
|
811
|
-
const oidcProvider = createOidcProviderService('oidc')
|
|
812
|
-
|
|
813
|
-
context.registerService(apiServer)
|
|
814
|
-
context.registerService(oidcProvider)
|
|
815
|
-
```
|
|
816
|
-
|
|
817
|
-
### Authentication Integration
|
|
818
|
-
```typescript
|
|
819
|
-
import { makeAuthService } from '@owlmeans/server-auth'
|
|
820
|
-
|
|
821
|
-
// Use OIDC tokens for API authentication
|
|
822
|
-
const authService = makeAuthService('auth')
|
|
823
|
-
// Configure auth service to validate OIDC tokens
|
|
824
|
-
```
|
|
825
|
-
|
|
826
|
-
### Context Integration
|
|
827
|
-
```typescript
|
|
828
|
-
import { makeServerContext } from '@owlmeans/server-context'
|
|
829
|
-
|
|
830
|
-
// Full integration with server context management
|
|
831
|
-
const context = makeServerContext(configWithOidc)
|
|
832
|
-
```
|
|
833
|
-
|
|
834
|
-
## Performance Considerations
|
|
56
|
+
### Types
|
|
835
57
|
|
|
836
|
-
|
|
837
|
-
- **Token Formats**: Consider JWT vs opaque tokens based on use case
|
|
838
|
-
- **Caching**: Implement appropriate caching for user accounts
|
|
839
|
-
- **Database Optimization**: Optimize account and client lookups
|
|
840
|
-
- **Connection Pooling**: Use connection pooling for database operations
|
|
58
|
+
`OidcProviderService` and provider config types are exported from the root entry.
|
|
841
59
|
|
|
842
60
|
## Related Packages
|
|
843
61
|
|
|
844
|
-
- [`@owlmeans/oidc`](../oidc)
|
|
845
|
-
- [`@owlmeans/server-
|
|
846
|
-
- [`@owlmeans/server-
|
|
847
|
-
- [`@owlmeans/
|
|
848
|
-
- [`@owlmeans/web-oidc-provider`](../web-oidc-provider) - Web UI for OIDC provider
|
|
62
|
+
- [`@owlmeans/oidc`](../oidc) — shared protocol constants and module declarations
|
|
63
|
+
- [`@owlmeans/server-oidc-rp`](../server-oidc-rp) — relying-party counterpart for consuming external IdPs
|
|
64
|
+
- [`@owlmeans/server-api`](../server-api) — the underlying server API the middleware mounts onto
|
|
65
|
+
- [`@owlmeans/web-oidc-provider`](../web-oidc-provider) — browser UI for the provider's interaction screens
|
package/build/service.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAA0C,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAS9G,eAAO,MAAM,yBAAyB,GAAI,QAAO,MAAsB,KAAG,
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAA0C,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAS9G,eAAO,MAAM,yBAAyB,GAAI,QAAO,MAAsB,KAAG,mBAwFzE,CAAA;AAED,eAAO,MAAM,yBAAyB,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,EAC9E,KAAK,CAAC,EAAE,QAAO,MAAsB,KACpC,CAOF,CAAA"}
|
package/build/service.js
CHANGED
|
@@ -26,7 +26,7 @@ export const createOidcProviderService = (alias = DEFAULT_ALIAS) => {
|
|
|
26
26
|
},
|
|
27
27
|
interactions: {
|
|
28
28
|
url: async (_, interaction) => {
|
|
29
|
-
const module = context.
|
|
29
|
+
const module = context.entrypoint(INTERACTION);
|
|
30
30
|
const [uri] = await module.call({ params: { uid: interaction.uid } });
|
|
31
31
|
return uri;
|
|
32
32
|
}
|
|
@@ -35,18 +35,22 @@ export const createOidcProviderService = (alias = DEFAULT_ALIAS) => {
|
|
|
35
35
|
oidc.proxy = cfg.behindProxy ?? unsecure;
|
|
36
36
|
const base = SEP + (cfg.basePath ?? DEFAULT_PATH);
|
|
37
37
|
api.server.use(base, oidc.callback());
|
|
38
|
-
oidc.use(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (
|
|
42
|
-
|
|
43
|
-
ctx.response.set('Content-Security-Policy', csp.replace(/form-action 'self'/, 'form-action *'));
|
|
38
|
+
// oidc-provider v9: provider.use() middleware no longer runs post-response after a matched
|
|
39
|
+
// route. CSP rewrite is handled at the Fastify layer via an onSend hook instead.
|
|
40
|
+
api.server.addHook('onSend', async (request, reply, payload) => {
|
|
41
|
+
if (!request.url.startsWith(base)) {
|
|
42
|
+
return payload;
|
|
44
43
|
}
|
|
44
|
+
const csp = reply.getHeader('content-security-policy');
|
|
45
|
+
if (typeof csp === 'string' && csp.includes("form-action 'self'")) {
|
|
46
|
+
reply.header('Content-Security-Policy', csp.replace(/form-action 'self'/, 'form-action *'));
|
|
47
|
+
}
|
|
48
|
+
return payload;
|
|
45
49
|
});
|
|
46
50
|
if (context.cfg.debug?.all || context.cfg.debug?.oidc) {
|
|
47
51
|
oidc.on('grant.error', (_, error) => {
|
|
48
52
|
console.warn('GRANT ERROR .......: ');
|
|
49
|
-
console.info(oidc.issuer
|
|
53
|
+
console.info(oidc.issuer);
|
|
50
54
|
console.error('!!!! GRANT ERROR: ', error);
|
|
51
55
|
});
|
|
52
56
|
oidc.on('server_error', (ctx, error) => {
|
|
@@ -59,15 +63,6 @@ export const createOidcProviderService = (alias = DEFAULT_ALIAS) => {
|
|
|
59
63
|
console.info(ctx.oidc.grant);
|
|
60
64
|
console.error('!!!! USER INFO ERROR: ', error);
|
|
61
65
|
});
|
|
62
|
-
oidc.use(async (_, next) => {
|
|
63
|
-
console.log('OIDC REQ .......: ');
|
|
64
|
-
console.log(_.request.toJSON());
|
|
65
|
-
await next();
|
|
66
|
-
console.log(_.oidc);
|
|
67
|
-
console.log('OIDC RES .......: ');
|
|
68
|
-
console.log(_.response.toJSON());
|
|
69
|
-
console.log('OIDC RESPONSE BODY .......: ', _.response.body);
|
|
70
|
-
});
|
|
71
66
|
}
|
|
72
67
|
_initializedOidc = service.oidc = oidc;
|
|
73
68
|
},
|
package/build/service.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAChE,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AACjE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAE1D,OAAO,QAAQ,MAAM,eAAe,CAAA;AAGpC,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AACrC,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAEjD,IAAI,gBAAgB,GAAyB,SAAS,CAAA;AACtD,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,QAAgB,aAAa,EAAuB,EAAE;IAC9F,MAAM,OAAO,GAAwB,aAAa,CAAsB,KAAK,EAAE;QAC7E,MAAM,EAAE,KAAK,EAAC,GAAG,EAAC,EAAE;YAClB,MAAM,OAAO,GAAG,aAAa,CAAkB,OAAO,CAAC,GAAc,EAAE,KAAK,CAAC,CAAA;YAC7E,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAA;YAE5B,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAe,CAAA;YAC/F,MAAM,MAAM,GAAG,kBAAkB,CAAkB,OAAO,CAAC,CAAA;YAC3D,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC,QAAQ,IAAI,YAAY,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;YACtF,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;YAE5F,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,GAAG,EAAE;gBAC7B,GAAG,MAAM,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC;gBAEzC,OAAO,EAAE,GAAG,CAAC,cAAc,IAAI,IAAI;oBACjC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,OAAO,CAAqB,GAAG,CAAC,cAAe,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;oBACjF,CAAC,CAAC,SAAS;gBAEb,WAAW,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE;oBACnC,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAChC,GAAG,CAAC,cAAc,IAAI,oBAAoB,CAC3C,CAAA;oBAED,OAAO,UAAU,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;gBACzC,CAAC;gBAED,YAAY,EAAE;oBACZ,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE;wBAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
|
package/build/utils/config.js
CHANGED
|
@@ -30,7 +30,7 @@ export const combineConfig = async (context, _unsecure) => {
|
|
|
30
30
|
},
|
|
31
31
|
jwks: {
|
|
32
32
|
keys: [
|
|
33
|
-
await jose.exportJWK(await jose.importPKCS8(cfg.defaultKeys.RS256.pk, 'RS256'))
|
|
33
|
+
await jose.exportJWK(await jose.importPKCS8(cfg.defaultKeys.RS256.pk, 'RS256', { extractable: true }))
|
|
34
34
|
]
|
|
35
35
|
}
|
|
36
36
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAE5B,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,EAAE,OAAgB,EAAE,SAAkB,EAA0B,EAAE;IAClG,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAA;IAE5B,MAAM,aAAa,GAAkB;QACnC,GAAG,GAAG,CAAC,mBAAmB;QAC1B,OAAO,EAAE;YACP,GAAG,GAAG,CAAC,OAAO;YACd,GAAG,CAAC,GAAG,CAAC,mBAAmB,EAAE,OAAO,IAAI,EAAE,CAAC;SAC5C,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC9C,MAAM,EAAE;YACN,KAAK,EAAE,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,GAAG,CAAC,mBAAmB,EAAE,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC;YACnF,OAAO,EAAE;gBACP,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,oBAAoB;gBAC3F,GAAG,GAAG,CAAC,mBAAmB,EAAE,MAAM,EAAE,OAAO,IAAI,EAAE;aAClD;YACD,GAAG,GAAG,CAAC,mBAAmB,EAAE,MAAM;SACnC;QACD,MAAM,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,gBAAgB,EAAE,GAAG,GAAG,CAAC,mBAAmB,EAAE,MAAM,IAAI,EAAE,CAAC;QACzF,QAAQ,EAAE;YACR,GAAG,GAAG,CAAC,mBAAmB,EAAE,QAAQ;YACpC,eAAe,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;YACnC,qBAAqB;YACrB,eAAe;YACf,kEAAkE;YAClE,gCAAgC;YAChC,mBAAmB;YACnB,2DAA2D;YAC3D,KAAK;SACN;QACD,IAAI,EAAE;YACJ,IAAI,EAAE;gBACJ,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAE5B,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,EAAE,OAAgB,EAAE,SAAkB,EAA0B,EAAE;IAClG,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAA;IAE5B,MAAM,aAAa,GAAkB;QACnC,GAAG,GAAG,CAAC,mBAAmB;QAC1B,OAAO,EAAE;YACP,GAAG,GAAG,CAAC,OAAO;YACd,GAAG,CAAC,GAAG,CAAC,mBAAmB,EAAE,OAAO,IAAI,EAAE,CAAC;SAC5C,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC9C,MAAM,EAAE;YACN,KAAK,EAAE,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,GAAG,CAAC,mBAAmB,EAAE,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC;YACnF,OAAO,EAAE;gBACP,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,oBAAoB;gBAC3F,GAAG,GAAG,CAAC,mBAAmB,EAAE,MAAM,EAAE,OAAO,IAAI,EAAE;aAClD;YACD,GAAG,GAAG,CAAC,mBAAmB,EAAE,MAAM;SACnC;QACD,MAAM,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,gBAAgB,EAAE,GAAG,GAAG,CAAC,mBAAmB,EAAE,MAAM,IAAI,EAAE,CAAC;QACzF,QAAQ,EAAE;YACR,GAAG,GAAG,CAAC,mBAAmB,EAAE,QAAQ;YACpC,eAAe,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;YACnC,qBAAqB;YACrB,eAAe;YACf,kEAAkE;YAClE,gCAAgC;YAChC,mBAAmB;YACnB,2DAA2D;YAC3D,KAAK;SACN;QACD,IAAI,EAAE;YACJ,IAAI,EAAE;gBACJ,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;aACvG;SACF;KACF,CAAA;IAED,OAAO,aAAa,CAAA;AACtB,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/server-oidc-provider",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "tsc -b",
|
|
8
8
|
"dev": "sleep 306 && nodemon -e ts,tsx,json --watch src --exec \"tsc -p ./tsconfig.json\"",
|
|
9
|
-
"watch": "tsc -b -w --preserveWatchOutput --pretty"
|
|
9
|
+
"watch": "tsc -b -w --preserveWatchOutput --pretty",
|
|
10
|
+
"test": "bun test ./tests"
|
|
10
11
|
},
|
|
11
12
|
"main": "build/index.js",
|
|
12
13
|
"module": "build/index.js",
|
|
@@ -21,27 +22,30 @@
|
|
|
21
22
|
}
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
25
|
+
"@owlmeans/dep-config": "workspace:*",
|
|
26
|
+
"@owlmeans/context": "^0.1.3",
|
|
27
|
+
"@types/bun": "^1.3.0",
|
|
24
28
|
"@types/node": "^24.10.1",
|
|
25
|
-
"@types/oidc-provider": "
|
|
29
|
+
"@types/oidc-provider": "9.5.0",
|
|
26
30
|
"nodemon": "^3.1.11",
|
|
27
31
|
"npm-check": "^6.0.1",
|
|
28
|
-
"typescript": "^
|
|
32
|
+
"typescript": "^6.0.2"
|
|
29
33
|
},
|
|
30
34
|
"peerDependencies": {
|
|
31
35
|
"fastify": "*"
|
|
32
36
|
},
|
|
33
37
|
"dependencies": {
|
|
34
38
|
"@noble/hashes": "^1.5.0",
|
|
35
|
-
"@owlmeans/client-
|
|
36
|
-
"@owlmeans/config": "^0.1.
|
|
37
|
-
"@owlmeans/context": "^0.1.
|
|
38
|
-
"@owlmeans/oidc": "^0.1.
|
|
39
|
-
"@owlmeans/route": "^0.1.
|
|
40
|
-
"@owlmeans/server-api": "^0.1.
|
|
41
|
-
"@owlmeans/server-context": "^0.1.
|
|
39
|
+
"@owlmeans/client-entrypoint": "^0.1.3",
|
|
40
|
+
"@owlmeans/config": "^0.1.3",
|
|
41
|
+
"@owlmeans/context": "^0.1.3",
|
|
42
|
+
"@owlmeans/oidc": "^0.1.3",
|
|
43
|
+
"@owlmeans/route": "^0.1.3",
|
|
44
|
+
"@owlmeans/server-api": "^0.1.3",
|
|
45
|
+
"@owlmeans/server-context": "^0.1.3",
|
|
42
46
|
"@scure/base": "^1.1.9",
|
|
43
|
-
"jose": "
|
|
44
|
-
"oidc-provider": "8.
|
|
47
|
+
"jose": "6.2.3",
|
|
48
|
+
"oidc-provider": "9.8.4"
|
|
45
49
|
},
|
|
46
50
|
"publishConfig": {
|
|
47
51
|
"access": "public"
|
package/src/service.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { DEFAULT_PATH, INTERACTION } from '@owlmeans/oidc'
|
|
|
4
4
|
import type { Config, Context, OidcAccountService, OidcAdapterService, OidcProviderService } from './types.js'
|
|
5
5
|
import Provider from 'oidc-provider'
|
|
6
6
|
import type { BasicRoute } from '@owlmeans/route'
|
|
7
|
-
import type {
|
|
7
|
+
import type { ClientEntrypoint } from '@owlmeans/client-entrypoint'
|
|
8
8
|
import { SEP } from '@owlmeans/route'
|
|
9
9
|
import { makeSecurityHelper } from '@owlmeans/config'
|
|
10
10
|
import { combineConfig } from './utils/config.js'
|
|
@@ -38,7 +38,7 @@ export const createOidcProviderService = (alias: string = DEFAULT_ALIAS): OidcPr
|
|
|
38
38
|
|
|
39
39
|
interactions: {
|
|
40
40
|
url: async (_, interaction) => {
|
|
41
|
-
const module = context.
|
|
41
|
+
const module = context.entrypoint<ClientEntrypoint>(INTERACTION)
|
|
42
42
|
const [uri] = await module.call<string>({ params: { uid: interaction.uid } })
|
|
43
43
|
return uri
|
|
44
44
|
}
|
|
@@ -49,20 +49,25 @@ export const createOidcProviderService = (alias: string = DEFAULT_ALIAS): OidcPr
|
|
|
49
49
|
const base = SEP + (cfg.basePath ?? DEFAULT_PATH)
|
|
50
50
|
|
|
51
51
|
api.server.use(base, oidc.callback())
|
|
52
|
-
oidc.use(async (ctx, next) => {
|
|
53
|
-
await next()
|
|
54
|
-
const csp = ctx.response.headers['content-security-policy']
|
|
55
|
-
if (csp != null) {
|
|
56
|
-
// @TODO Make it a little bit nicer - preferably using helmet :)
|
|
57
52
|
|
|
58
|
-
|
|
53
|
+
// oidc-provider v9: provider.use() middleware no longer runs post-response after a matched
|
|
54
|
+
// route. CSP rewrite is handled at the Fastify layer via an onSend hook instead.
|
|
55
|
+
api.server.addHook('onSend', async (request, reply, payload) => {
|
|
56
|
+
if (!request.url.startsWith(base)) {
|
|
57
|
+
return payload
|
|
59
58
|
}
|
|
59
|
+
const csp = reply.getHeader('content-security-policy')
|
|
60
|
+
if (typeof csp === 'string' && csp.includes("form-action 'self'")) {
|
|
61
|
+
reply.header('Content-Security-Policy', csp.replace(/form-action 'self'/, 'form-action *'))
|
|
62
|
+
}
|
|
63
|
+
return payload
|
|
60
64
|
})
|
|
65
|
+
|
|
61
66
|
if (context.cfg.debug?.all || context.cfg.debug?.oidc) {
|
|
62
67
|
|
|
63
68
|
oidc.on('grant.error', (_, error) => {
|
|
64
69
|
console.warn('GRANT ERROR .......: ')
|
|
65
|
-
console.info(oidc.issuer
|
|
70
|
+
console.info(oidc.issuer)
|
|
66
71
|
console.error('!!!! GRANT ERROR: ', error)
|
|
67
72
|
})
|
|
68
73
|
|
|
@@ -78,16 +83,6 @@ export const createOidcProviderService = (alias: string = DEFAULT_ALIAS): OidcPr
|
|
|
78
83
|
console.error('!!!! USER INFO ERROR: ', error)
|
|
79
84
|
})
|
|
80
85
|
|
|
81
|
-
oidc.use(async (_, next) => {
|
|
82
|
-
console.log('OIDC REQ .......: ')
|
|
83
|
-
console.log(_.request.toJSON())
|
|
84
|
-
await next()
|
|
85
|
-
console.log(_.oidc)
|
|
86
|
-
console.log('OIDC RES .......: ')
|
|
87
|
-
console.log(_.response.toJSON())
|
|
88
|
-
console.log('OIDC RESPONSE BODY .......: ', _.response.body)
|
|
89
|
-
})
|
|
90
|
-
|
|
91
86
|
}
|
|
92
87
|
|
|
93
88
|
_initializedOidc = service.oidc = oidc
|
package/src/utils/config.ts
CHANGED
|
@@ -34,7 +34,7 @@ export const combineConfig = async (context: Context, _unsecure: boolean): Promi
|
|
|
34
34
|
},
|
|
35
35
|
jwks: {
|
|
36
36
|
keys: [
|
|
37
|
-
await jose.exportJWK(await jose.importPKCS8(cfg.defaultKeys.RS256.pk, 'RS256'))
|
|
37
|
+
await jose.exportJWK(await jose.importPKCS8(cfg.defaultKeys.RS256.pk, 'RS256', { extractable: true }))
|
|
38
38
|
]
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.spec.d.ts","sourceRoot":"","sources":["config.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, it, expect } from 'bun:test'
|
|
2
|
+
import { generateKeyPairSync } from 'node:crypto'
|
|
3
|
+
import { AppType, Layer, makeBasicContext } from '@owlmeans/context'
|
|
4
|
+
import type { BasicContext } from '@owlmeans/context'
|
|
5
|
+
import { combineConfig } from '../src/utils/config.js'
|
|
6
|
+
import type { Config } from '../src/types.js'
|
|
7
|
+
|
|
8
|
+
const { privateKey } = generateKeyPairSync('rsa', { modulusLength: 2048 })
|
|
9
|
+
const TEST_PKCS8_PEM = privateKey.export({ type: 'pkcs8', format: 'pem' }).toString()
|
|
10
|
+
|
|
11
|
+
const makeTestContext = () => {
|
|
12
|
+
const cfg: Config = {
|
|
13
|
+
ready: false,
|
|
14
|
+
service: 'server-oidc-provider-tests',
|
|
15
|
+
layer: Layer.Service,
|
|
16
|
+
type: AppType.Backend,
|
|
17
|
+
services: {},
|
|
18
|
+
debug: { all: false },
|
|
19
|
+
oidc: {
|
|
20
|
+
clients: [],
|
|
21
|
+
defaultKeys: {
|
|
22
|
+
RS256: { pk: TEST_PKCS8_PEM },
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
} as unknown as Config
|
|
26
|
+
|
|
27
|
+
return makeBasicContext(cfg) as BasicContext<Config>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe('combineConfig (jose v6 extractable key)', () => {
|
|
31
|
+
it('produces a JWKS with an RS256 key', async () => {
|
|
32
|
+
const context = makeTestContext()
|
|
33
|
+
const config = await combineConfig(context as any, true)
|
|
34
|
+
|
|
35
|
+
expect(config.jwks).toBeDefined()
|
|
36
|
+
expect(Array.isArray(config.jwks?.keys)).toBe(true)
|
|
37
|
+
const keys = config.jwks?.keys ?? []
|
|
38
|
+
expect(keys.length).toBeGreaterThan(0)
|
|
39
|
+
expect(keys[0].kty).toBe('RSA')
|
|
40
|
+
})
|
|
41
|
+
})
|
package/tsconfig.json
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"extends": [
|
|
3
|
-
"
|
|
3
|
+
"@owlmeans/dep-config/tsconfig.base.json",
|
|
4
|
+
"@owlmeans/dep-config/tsconfig.node.json"
|
|
4
5
|
],
|
|
5
6
|
"compilerOptions": {
|
|
6
|
-
"rootDir": "./src/",
|
|
7
|
-
"outDir": "./build/"
|
|
8
|
-
"moduleResolution": "Bundler",
|
|
7
|
+
"rootDir": "./src/",
|
|
8
|
+
"outDir": "./build/"
|
|
9
9
|
},
|
|
10
10
|
"exclude": [
|
|
11
11
|
"./dist/**/*",
|
|
12
12
|
"./build/**/*",
|
|
13
|
+
"./tests/**/*",
|
|
13
14
|
"./*.ts"
|
|
14
15
|
]
|
|
15
|
-
}
|
|
16
|
+
}
|