@owlmeans/server-oidc-provider 0.1.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.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +848 -0
  3. package/build/.gitkeep +0 -0
  4. package/build/consts.d.ts +3 -0
  5. package/build/consts.d.ts.map +1 -0
  6. package/build/consts.js +3 -0
  7. package/build/consts.js.map +1 -0
  8. package/build/index.d.ts +5 -0
  9. package/build/index.d.ts.map +1 -0
  10. package/build/index.js +4 -0
  11. package/build/index.js.map +1 -0
  12. package/build/middleware.d.ts +3 -0
  13. package/build/middleware.d.ts.map +1 -0
  14. package/build/middleware.js +24 -0
  15. package/build/middleware.js.map +1 -0
  16. package/build/service.d.ts +4 -0
  17. package/build/service.d.ts.map +1 -0
  18. package/build/service.js +78 -0
  19. package/build/service.js.map +1 -0
  20. package/build/types.d.ts +46 -0
  21. package/build/types.d.ts.map +1 -0
  22. package/build/types.js +2 -0
  23. package/build/types.js.map +1 -0
  24. package/build/utils/client.d.ts +4 -0
  25. package/build/utils/client.d.ts.map +1 -0
  26. package/build/utils/client.js +31 -0
  27. package/build/utils/client.js.map +1 -0
  28. package/build/utils/config.d.ts +4 -0
  29. package/build/utils/config.d.ts.map +1 -0
  30. package/build/utils/config.js +39 -0
  31. package/build/utils/config.js.map +1 -0
  32. package/build/utils/index.d.ts +3 -0
  33. package/build/utils/index.d.ts.map +1 -0
  34. package/build/utils/index.js +3 -0
  35. package/build/utils/index.js.map +1 -0
  36. package/package.json +49 -0
  37. package/src/consts.ts +4 -0
  38. package/src/index.ts +5 -0
  39. package/src/middleware.ts +29 -0
  40. package/src/service.ts +103 -0
  41. package/src/types.ts +55 -0
  42. package/src/utils/client.ts +40 -0
  43. package/src/utils/config.ts +43 -0
  44. package/src/utils/index.ts +3 -0
  45. package/tsconfig.json +15 -0
package/README.md ADDED
@@ -0,0 +1,848 @@
1
+ # @owlmeans/server-oidc-provider
2
+
3
+ Server-side OpenID Connect Provider implementation for OwlMeans applications. This package provides a complete OIDC identity provider service built on the industry-standard `oidc-provider` library, with seamless integration into the OwlMeans server ecosystem.
4
+
5
+ ## Overview
6
+
7
+ The `@owlmeans/server-oidc-provider` package delivers a full-featured OIDC identity provider with:
8
+
9
+ - **Complete OIDC Provider**: Full OpenID Connect 1.0 and OAuth 2.0 implementation
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
17
+
18
+ This package is part of the OwlMeans OIDC ecosystem:
19
+ - **@owlmeans/oidc**: Core OIDC types and utilities
20
+ - **@owlmeans/server-oidc-provider**: Server-side OIDC provider *(this package)*
21
+ - **@owlmeans/server-oidc-rp**: Server-side relying party
22
+ - **@owlmeans/web-oidc-provider**: Web UI for OIDC provider
23
+ - **@owlmeans/web-oidc-rp**: Web-based relying party
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ npm install @owlmeans/server-oidc-provider oidc-provider fastify
29
+ ```
30
+
31
+ ## Dependencies
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
53
+
54
+ Configurable storage adapter service for persisting OIDC sessions, authorization codes, access tokens, and other provider data.
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.
67
+
68
+ ```typescript
69
+ interface OidcProviderService extends InitializedService {
70
+ oidc: Provider // oidc-provider instance
71
+ update(api: ApiServer): Promise<void> // Update provider with API server
72
+ instance(): Provider // Get provider instance
73
+ getInteraction(id: string): Promise<Interaction | null> // Get interaction by ID
74
+ }
75
+ ```
76
+
77
+ #### `OidcConfig`
78
+
79
+ Configuration interface for OIDC provider.
80
+
81
+ ```typescript
82
+ interface OidcConfig extends OidcSharedConfig {
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
+ }
111
+ ```
112
+
113
+ #### `OidcAdapterService`
114
+
115
+ Interface for storage adapter management.
116
+
117
+ ```typescript
118
+ interface OidcAdapterService extends InitializedService {
119
+ instance(name: string): Adapter
120
+ }
121
+ ```
122
+
123
+ #### `Config`
124
+
125
+ Extended server configuration with OIDC settings.
126
+
127
+ ```typescript
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
+ ```
136
+
137
+ ### Factory Functions
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
+ ```
161
+
162
+ #### `createOidcProviderMiddleware(web?: string, oidc?: string): Middleware`
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
+ ```
234
+
235
+ ### Constants
236
+
237
+ ```typescript
238
+ const DEFAULT_ALIAS = 'oidc-provider' // Default service alias
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')
283
+
284
+ // Register services and middleware
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
835
+
836
+ - **Adapter Choice**: Use Redis or other fast storage for sessions
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
841
+
842
+ ## Related Packages
843
+
844
+ - [`@owlmeans/oidc`](../oidc) - Core OIDC types and utilities
845
+ - [`@owlmeans/server-api`](../server-api) - Server API framework
846
+ - [`@owlmeans/server-context`](../server-context) - Server context management
847
+ - [`@owlmeans/server-oidc-rp`](../server-oidc-rp) - Server-side relying party
848
+ - [`@owlmeans/web-oidc-provider`](../web-oidc-provider) - Web UI for OIDC provider