@kaapi/oauth2-auth-design 0.0.35 → 0.0.37

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.
@@ -0,0 +1,606 @@
1
+ "use strict";
2
+ var _OAuth2FlowGenerator_instances, _OAuth2FlowGenerator_values, _OAuth2FlowGenerator_getOidcAuthCodeContent, _OAuth2FlowGenerator_getOidcClientCredentialsContent, _OAuth2FlowGenerator_getOidcDeviceAuthContent;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.OAuth2FlowGenerator = void 0;
5
+ const tslib_1 = require("tslib");
6
+ const definitions_1 = require("@kaapi/cli/definitions");
7
+ const utils_1 = require("@kaapi/cli/utils");
8
+ var FLOW_ENUM;
9
+ (function (FLOW_ENUM) {
10
+ FLOW_ENUM["oidcAuthCode"] = "oidc-auth-code";
11
+ FLOW_ENUM["oidcClientCredentials"] = "oidc-client-credentials";
12
+ FLOW_ENUM["oidcDeviceAuth"] = "oidc-device-auth";
13
+ })(FLOW_ENUM || (FLOW_ENUM = {}));
14
+ const FLOW_OPTIONS = [
15
+ {
16
+ value: FLOW_ENUM.oidcAuthCode,
17
+ label: 'OIDC Authorization Code',
18
+ hint: ''
19
+ },
20
+ {
21
+ value: FLOW_ENUM.oidcClientCredentials,
22
+ label: 'OIDC Client Credentials',
23
+ hint: ''
24
+ },
25
+ {
26
+ value: FLOW_ENUM.oidcDeviceAuth,
27
+ label: 'OIDC Device Authorization',
28
+ hint: ''
29
+ }
30
+ ];
31
+ class OAuth2FlowGenerator {
32
+ constructor() {
33
+ _OAuth2FlowGenerator_instances.add(this);
34
+ _OAuth2FlowGenerator_values.set(this, {
35
+ name: '',
36
+ flow: ''
37
+ });
38
+ }
39
+ get type() {
40
+ return 'auth-design';
41
+ }
42
+ get name() {
43
+ return 'oauth2-flow';
44
+ }
45
+ get description() {
46
+ return 'Creates an auth design based on OAuth2 specifications.';
47
+ }
48
+ get notes() {
49
+ return [
50
+ 'Allowed values for --flow:',
51
+ ...FLOW_OPTIONS.map(o => ` - ${o.value}`)
52
+ ];
53
+ }
54
+ get options() {
55
+ return {
56
+ name: 'The name of the strategy',
57
+ flow: 'The grant type flow'
58
+ };
59
+ }
60
+ init(options) {
61
+ if (typeof options['name'] == 'string') {
62
+ tslib_1.__classPrivateFieldGet(this, _OAuth2FlowGenerator_values, "f").name = (0, utils_1.camelCase)(options['name']);
63
+ }
64
+ if (typeof options['flow'] == 'string') {
65
+ if (!FLOW_OPTIONS.map(v => v.value).includes(options['flow'])) {
66
+ throw new Error(`Invalid value for '--flow'. Allowed values are: ${FLOW_OPTIONS.map(v => v.value).join(', ')}.`);
67
+ }
68
+ tslib_1.__classPrivateFieldGet(this, _OAuth2FlowGenerator_values, "f").flow = options['flow'];
69
+ }
70
+ }
71
+ isValid() {
72
+ return !!tslib_1.__classPrivateFieldGet(this, _OAuth2FlowGenerator_values, "f").name;
73
+ }
74
+ getFileContent() {
75
+ if (tslib_1.__classPrivateFieldGet(this, _OAuth2FlowGenerator_values, "f").flow === FLOW_ENUM.oidcAuthCode)
76
+ return tslib_1.__classPrivateFieldGet(this, _OAuth2FlowGenerator_instances, "m", _OAuth2FlowGenerator_getOidcAuthCodeContent).call(this);
77
+ else if (tslib_1.__classPrivateFieldGet(this, _OAuth2FlowGenerator_values, "f").flow === FLOW_ENUM.oidcClientCredentials)
78
+ return tslib_1.__classPrivateFieldGet(this, _OAuth2FlowGenerator_instances, "m", _OAuth2FlowGenerator_getOidcClientCredentialsContent).call(this);
79
+ else if (tslib_1.__classPrivateFieldGet(this, _OAuth2FlowGenerator_values, "f").flow === FLOW_ENUM.oidcDeviceAuth)
80
+ return tslib_1.__classPrivateFieldGet(this, _OAuth2FlowGenerator_instances, "m", _OAuth2FlowGenerator_getOidcDeviceAuthContent).call(this);
81
+ else
82
+ return '';
83
+ }
84
+ getQuestions() {
85
+ const r = [];
86
+ if (!tslib_1.__classPrivateFieldGet(this, _OAuth2FlowGenerator_values, "f").name) {
87
+ r.push({
88
+ type: definitions_1.QuestionType.text,
89
+ options: {
90
+ message: 'The name of the strategy?',
91
+ defaultValue: 'oauth2-strategy',
92
+ placeholder: 'oauth2-strategy'
93
+ },
94
+ setValue: (value) => {
95
+ tslib_1.__classPrivateFieldGet(this, _OAuth2FlowGenerator_values, "f").name = (0, utils_1.camelCase)(value);
96
+ }
97
+ });
98
+ }
99
+ if (!(tslib_1.__classPrivateFieldGet(this, _OAuth2FlowGenerator_values, "f").flow && FLOW_OPTIONS.map(v => v.value).includes(tslib_1.__classPrivateFieldGet(this, _OAuth2FlowGenerator_values, "f").flow))) {
100
+ r.push({
101
+ type: definitions_1.QuestionType.select,
102
+ options: {
103
+ message: 'The authorization flow?',
104
+ options: FLOW_OPTIONS,
105
+ initialValue: 'oidc-auth-code'
106
+ },
107
+ setValue: (value) => {
108
+ tslib_1.__classPrivateFieldGet(this, _OAuth2FlowGenerator_values, "f").flow = `${value}`;
109
+ }
110
+ });
111
+ }
112
+ return r;
113
+ }
114
+ getFilename() {
115
+ return (0, utils_1.kebabCase)(`${tslib_1.__classPrivateFieldGet(this, _OAuth2FlowGenerator_values, "f").name}`) + '.ts';
116
+ }
117
+ }
118
+ exports.OAuth2FlowGenerator = OAuth2FlowGenerator;
119
+ _OAuth2FlowGenerator_values = new WeakMap(), _OAuth2FlowGenerator_instances = new WeakSet(), _OAuth2FlowGenerator_getOidcAuthCodeContent = function _OAuth2FlowGenerator_getOidcAuthCodeContent() {
120
+ return `// generated by @kaapi/oauth2-auth-design
121
+
122
+ import {
123
+ BearerToken,
124
+ OIDCAuthorizationCodeBuilder,
125
+ createInMemoryKeyStore,
126
+ createMatchAuthCodeResult,
127
+ OAuth2TokenResponse,
128
+ ClientSecretBasic,
129
+ ClientSecretPost,
130
+ NoneAuthMethod,
131
+ OAuth2ErrorCode,
132
+ } from '@kaapi/oauth2-auth-design';
133
+
134
+ const VALID_CLIENTS = [
135
+ {
136
+ client_id: 'service-api-client',
137
+ client_secret: 's3cr3tK3y123!',
138
+ allowed_scopes: ['openid', 'profile', 'email', 'read', 'write'],
139
+ },
140
+ ];
141
+
142
+ const REGISTERED_USERS = [{ id: 'user-1234', username: 'user', password: 'password', email: 'user@email.com' }];
143
+
144
+ const authCodesStore: Map<
145
+ string,
146
+ { clientId: string; scopes: string[]; userId: string; codeChallenge?: string | undefined }
147
+ > = new Map();
148
+
149
+ export const oidcAuthCodeBuilder = OIDCAuthorizationCodeBuilder.create()
150
+ // the name of the strategy
151
+ .strategyName('${(0, utils_1.kebabCase)(tslib_1.__classPrivateFieldGet(this, _OAuth2FlowGenerator_values, "f").name)}')
152
+ // access token TTL (used in generateToken controller)
153
+ .setTokenTtl(3600)
154
+ // activate auto parsing of access token (jwtAccessTokenPayload + createJwtAccessToken)
155
+ .useAccessTokenJwks(true)
156
+ // Client authentication methods
157
+ .addClientAuthenticationMethod(new ClientSecretBasic())
158
+ .addClientAuthenticationMethod(new ClientSecretPost())
159
+ .addClientAuthenticationMethod(new NoneAuthMethod())
160
+ .setTokenType(new BearerToken())
161
+ // Define scopes
162
+ .setScopes({
163
+ profile: 'Access to basic profile information such as name and picture.',
164
+ email: "Access to the user's email address and its verification status.",
165
+ read: 'Grants read-only access to protected resources',
166
+ write: 'Grants write access to protected resources',
167
+ })
168
+
169
+ // Authorization
170
+ .authorizationRoute<object, { Payload: { username?: string; password?: string } }>((route) =>
171
+ route
172
+ .setPath('/oauth2/v1.0/authorize')
173
+ .setUsernameField('username')
174
+ .setPasswordField('password')
175
+ .generateCode(async ({ clientId, codeChallenge, scope }, req, _h) => {
176
+ // client exists?
177
+ const client = VALID_CLIENTS.find((c) => c.client_id === clientId);
178
+ if (!client) return null;
179
+
180
+ // filter client's allowed scoped
181
+ const requestedScopes = (scope ?? '').split(/\\s+/).filter(Boolean);
182
+ const grantedScopes = requestedScopes.length
183
+ ? requestedScopes.filter((s) => client.allowed_scopes.includes(s))
184
+ : client.allowed_scopes;
185
+ if (grantedScopes.length === 0) return null;
186
+
187
+ // user exists?
188
+ const user = REGISTERED_USERS.find(
189
+ (u) => u.username === req.payload.username && u.password === req.payload.password
190
+ );
191
+ if (!user) return null;
192
+
193
+ // generate code
194
+ const code = \`auth-${Date.now()}\`;
195
+ authCodesStore.set(code, { clientId, scopes: grantedScopes, userId: user.id, codeChallenge });
196
+ return { type: 'code', value: code };
197
+ })
198
+ .finalizeAuthorization(async (ctx, _params, _req, h) => {
199
+ // redirect to callback url (could do something else depending on the code result)
200
+ const matcher = createMatchAuthCodeResult({
201
+ code: async () => h.redirect(ctx.fullRedirectUri),
202
+ continue: async () => h.redirect(ctx.fullRedirectUri),
203
+ deny: async () => h.redirect(ctx.fullRedirectUri),
204
+ });
205
+ return matcher(ctx.authorizationResult);
206
+ })
207
+ )
208
+
209
+ // Token exchange
210
+ .tokenRoute((route) =>
211
+ route.generateToken(
212
+ async ({
213
+ clientId,
214
+ ttl,
215
+ tokenType,
216
+ code,
217
+ clientSecret,
218
+ codeVerifier,
219
+ createJwtAccessToken,
220
+ createIdToken,
221
+ verifyCodeVerifier,
222
+ }) => {
223
+ const entry = authCodesStore.get(code);
224
+ if (!entry || entry.clientId !== clientId) return null;
225
+
226
+ const client = VALID_CLIENTS.find((c) => c.client_id === clientId);
227
+ if (!client) {
228
+ return {
229
+ error: OAuth2ErrorCode.INVALID_CLIENT,
230
+ error_description: 'Client authentication failed.',
231
+ };
232
+ }
233
+
234
+ if (entry.codeChallenge && codeVerifier) {
235
+ if (!verifyCodeVerifier(codeVerifier, entry.codeChallenge)) {
236
+ return {
237
+ error: OAuth2ErrorCode.INVALID_GRANT,
238
+ error_description: 'Invalid authorization grant.',
239
+ };
240
+ }
241
+ } else if (clientSecret) {
242
+ if (client.client_secret !== clientSecret) {
243
+ return {
244
+ error: OAuth2ErrorCode.INVALID_CLIENT,
245
+ error_description: 'Client authentication failed.',
246
+ };
247
+ }
248
+ } else {
249
+ return {
250
+ error: OAuth2ErrorCode.INVALID_REQUEST,
251
+ error_description: 'Missing or invalid request parameter.',
252
+ };
253
+ }
254
+
255
+ const user = REGISTERED_USERS.find((u) => u.id === entry.userId);
256
+ if (!user) {
257
+ return {
258
+ error: OAuth2ErrorCode.INVALID_GRANT,
259
+ error_description: 'Invalid authorization grant.',
260
+ };
261
+ }
262
+
263
+ // Generate a signed JWT access token
264
+ const { token: accessToken } = await createJwtAccessToken!({
265
+ sub: entry.userId,
266
+ client_id: clientId,
267
+ scope: entry.scopes,
268
+ });
269
+
270
+ // Generate a signed JWT id token
271
+ const idToken = entry.scopes.includes('openid')
272
+ ? (await createIdToken!({ sub: entry.userId, name: user.username, aud: clientId })).token
273
+ : undefined;
274
+
275
+ // Return token response
276
+ return new OAuth2TokenResponse({ access_token: accessToken })
277
+ .setExpiresIn(ttl)
278
+ .setTokenType(tokenType)
279
+ .setScope(entry.scopes)
280
+ .setIdToken(idToken);
281
+ }
282
+ )
283
+ )
284
+
285
+ // Access Token Validation
286
+ .validate(async (_req, { jwtAccessTokenPayload }) => {
287
+ if (!jwtAccessTokenPayload?.sub) return { isValid: false };
288
+ return {
289
+ isValid: true,
290
+ credentials: {
291
+ user: {
292
+ id: jwtAccessTokenPayload.sub,
293
+ clientId: jwtAccessTokenPayload.client_id,
294
+ },
295
+ scope: Array.isArray(jwtAccessTokenPayload.scope) ? jwtAccessTokenPayload.scope : [],
296
+ },
297
+ };
298
+ })
299
+
300
+ // JWKS
301
+ .jwksRoute((route) => route.setPath('/discovery/v1.0/keys')) // activates jwks uri
302
+ .setPublicKeyExpiry(86400) // 24h
303
+ .setJwksKeyStore(createInMemoryKeyStore()) // store for JWKS
304
+ .setJwksRotatorOptions({
305
+ intervalMs: 7.884e9, // 91 days
306
+ timestampStore: createInMemoryKeyStore(),
307
+ });
308
+ `;
309
+ }, _OAuth2FlowGenerator_getOidcClientCredentialsContent = function _OAuth2FlowGenerator_getOidcClientCredentialsContent() {
310
+ return `// generated by @kaapi/oauth2-auth-design
311
+
312
+ import {
313
+ OIDCClientCredentialsBuilder,
314
+ OAuth2TokenResponse,
315
+ OAuth2ErrorCode,
316
+ ClientSecretBasic,
317
+ ClientSecretPost,
318
+ BearerToken,
319
+ createInMemoryKeyStore,
320
+ } from '@kaapi/oauth2-auth-design';
321
+
322
+ const VALID_CLIENTS = [
323
+ {
324
+ client_id: 'internal-service',
325
+ client_secret: 'Int3rnalK3y!',
326
+ allowed_scopes: ['read', 'write', 'admin'],
327
+ },
328
+ ];
329
+
330
+ export const oidcClientCredentialsBuilder = OIDCClientCredentialsBuilder.create()
331
+ // the name of the strategy
332
+ .strategyName('${(0, utils_1.kebabCase)(tslib_1.__classPrivateFieldGet(this, _OAuth2FlowGenerator_values, "f").name)}')
333
+ // access token ttl (used in generateToken controller)
334
+ .setTokenTtl(600)
335
+ // activate auto parsing of access token (jwtAccessTokenPayload + createJwtAccessToken)
336
+ .useAccessTokenJwks(true)
337
+ // Client authentication methods
338
+ .addClientAuthenticationMethod(new ClientSecretBasic())
339
+ .addClientAuthenticationMethod(new ClientSecretPost())
340
+ .setTokenType(new BearerToken())
341
+ // Define available scopes
342
+ .setScopes({
343
+ read: 'Grants read-only access to protected resources',
344
+ write: 'Grants write access to protected resources',
345
+ admin: 'Administrative access',
346
+ })
347
+
348
+ // Token exchange
349
+ .tokenRoute((route) =>
350
+ route.generateToken(async ({ clientId, clientSecret, ttl, tokenType, scope, createJwtAccessToken }) => {
351
+ // Validate client credentials
352
+ const client = VALID_CLIENTS.find((c) => c.client_id === clientId && c.client_secret === clientSecret);
353
+
354
+ if (!client) {
355
+ return {
356
+ error: OAuth2ErrorCode.INVALID_CLIENT,
357
+ error_description: 'Invalid client_id or client_secret',
358
+ };
359
+ }
360
+
361
+ // Determine requested scopes
362
+ const requestedScopes = (scope ?? '').split(/\\s+/).filter(Boolean);
363
+
364
+ // Compute granted scopes
365
+ let grantedScopes = client.allowed_scopes;
366
+ if (requestedScopes.length > 0) {
367
+ grantedScopes = requestedScopes.filter((s) => client.allowed_scopes.includes(s));
368
+ }
369
+
370
+ if (grantedScopes.length === 0) {
371
+ return {
372
+ error: OAuth2ErrorCode.INVALID_SCOPE,
373
+ error_description: 'No valid scopes granted for this client',
374
+ };
375
+ }
376
+
377
+ // Generate a signed JWT access token
378
+ const { token: accessToken } = await createJwtAccessToken!({
379
+ sub: clientId,
380
+ app: true,
381
+ scope: grantedScopes,
382
+ });
383
+
384
+ // Return token response
385
+ return new OAuth2TokenResponse({ access_token: accessToken })
386
+ .setExpiresIn(ttl)
387
+ .setTokenType(tokenType)
388
+ .setScope(grantedScopes.join(' '));
389
+ })
390
+ )
391
+
392
+ // Access Token Validation
393
+ .validate(async (_req, { jwtAccessTokenPayload }) => {
394
+ if (!jwtAccessTokenPayload?.sub || !jwtAccessTokenPayload.app) return { isValid: false };
395
+ return {
396
+ isValid: true,
397
+ credentials: {
398
+ app: {
399
+ id: jwtAccessTokenPayload.sub,
400
+ },
401
+ scope: Array.isArray(jwtAccessTokenPayload.scope) ? jwtAccessTokenPayload.scope : [],
402
+ },
403
+ };
404
+ })
405
+
406
+ // JWKS
407
+ .jwksRoute((route) => route.setPath('/discovery/v1.0/keys')) // activates jwks uri
408
+ .setPublicKeyExpiry(86400) // 24h
409
+ .setJwksKeyStore(createInMemoryKeyStore()) // store for JWKS
410
+ .setJwksRotatorOptions({
411
+ intervalMs: 7.884e9, // 91 days
412
+ timestampStore: createInMemoryKeyStore(),
413
+ });
414
+ `;
415
+ }, _OAuth2FlowGenerator_getOidcDeviceAuthContent = function _OAuth2FlowGenerator_getOidcDeviceAuthContent() {
416
+ return `// generated by @kaapi/oauth2-auth-design
417
+
418
+ import {
419
+ BearerToken,
420
+ OIDCAuthorizationCodeBuilder,
421
+ createInMemoryKeyStore,
422
+ createMatchAuthCodeResult,
423
+ OAuth2TokenResponse,
424
+ ClientSecretBasic,
425
+ ClientSecretPost,
426
+ NoneAuthMethod,
427
+ OAuth2ErrorCode,
428
+ } from '@kaapi/oauth2-auth-design';
429
+
430
+ const VALID_CLIENTS = [
431
+ {
432
+ client_id: 'service-api-client',
433
+ client_secret: 's3cr3tK3y123!',
434
+ allowed_scopes: ['openid', 'profile', 'email', 'read', 'write'],
435
+ },
436
+ ];
437
+
438
+ const REGISTERED_USERS = [{ id: 'user-1234', username: 'user', password: 'password', email: 'user@email.com' }];
439
+
440
+ const authCodesStore: Map<
441
+ string,
442
+ { clientId: string; scopes: string[]; userId: string; codeChallenge?: string | undefined }
443
+ > = new Map();
444
+
445
+ export const oidcAuthCodeBuilder = OIDCAuthorizationCodeBuilder.create()
446
+ // the name of the strategy
447
+ .strategyName('${(0, utils_1.kebabCase)(tslib_1.__classPrivateFieldGet(this, _OAuth2FlowGenerator_values, "f").name)}')
448
+ // access token TTL (used in generateToken controller)
449
+ .setTokenTtl(3600)
450
+ // activate auto parsing of access token (jwtAccessTokenPayload + createJwtAccessToken)
451
+ .useAccessTokenJwks(true)
452
+ // Client authentication methods
453
+ .addClientAuthenticationMethod(new ClientSecretBasic())
454
+ .addClientAuthenticationMethod(new ClientSecretPost())
455
+ .addClientAuthenticationMethod(new NoneAuthMethod())
456
+ .setTokenType(new BearerToken())
457
+ // Define scopes
458
+ .setScopes({
459
+ profile: 'Access to basic profile information such as name and picture.',
460
+ email: "Access to the user's email address and its verification status.",
461
+ read: 'Grants read-only access to protected resources',
462
+ write: 'Grants write access to protected resources',
463
+ })
464
+
465
+ // Authorization
466
+ .authorizationRoute<object, { Payload: { username?: string; password?: string } }>((route) =>
467
+ route
468
+ .setPath('/oauth2/v1.0/authorize')
469
+ .setUsernameField('username')
470
+ .setPasswordField('password')
471
+ .generateCode(async ({ clientId, codeChallenge, scope }, req, _h) => {
472
+ // client exists?
473
+ const client = VALID_CLIENTS.find((c) => c.client_id === clientId);
474
+ if (!client) return null;
475
+
476
+ // filter client's allowed scoped
477
+ const requestedScopes = (scope ?? '').split(/\\s+/).filter(Boolean);
478
+ const grantedScopes = requestedScopes.length
479
+ ? requestedScopes.filter((s) => client.allowed_scopes.includes(s))
480
+ : client.allowed_scopes;
481
+ if (grantedScopes.length === 0) return null;
482
+
483
+ // user exists?
484
+ const user = REGISTERED_USERS.find(
485
+ (u) => u.username === req.payload.username && u.password === req.payload.password
486
+ );
487
+ if (!user) return null;
488
+
489
+ // generate code
490
+ const code = \`auth-${Date.now()}\`;
491
+ authCodesStore.set(code, { clientId, scopes: grantedScopes, userId: user.id, codeChallenge });
492
+ return { type: 'code', value: code };
493
+ })
494
+ .finalizeAuthorization(async (ctx, _params, _req, h) => {
495
+ // redirect to callback url (could do something else depending on the code result)
496
+ const matcher = createMatchAuthCodeResult({
497
+ code: async () => h.redirect(ctx.fullRedirectUri),
498
+ continue: async () => h.redirect(ctx.fullRedirectUri),
499
+ deny: async () => h.redirect(ctx.fullRedirectUri),
500
+ });
501
+ return matcher(ctx.authorizationResult);
502
+ })
503
+ )
504
+
505
+ // Token exchange
506
+ .tokenRoute((route) =>
507
+ route.generateToken(
508
+ async ({
509
+ clientId,
510
+ ttl,
511
+ tokenType,
512
+ code,
513
+ clientSecret,
514
+ codeVerifier,
515
+ createJwtAccessToken,
516
+ createIdToken,
517
+ verifyCodeVerifier,
518
+ }) => {
519
+ const entry = authCodesStore.get(code);
520
+ if (!entry || entry.clientId !== clientId) return null;
521
+
522
+ const client = VALID_CLIENTS.find((c) => c.client_id === clientId);
523
+ if (!client) {
524
+ return {
525
+ error: OAuth2ErrorCode.INVALID_CLIENT,
526
+ error_description: 'Client authentication failed.',
527
+ };
528
+ }
529
+
530
+ if (entry.codeChallenge && codeVerifier) {
531
+ if (!verifyCodeVerifier(codeVerifier, entry.codeChallenge)) {
532
+ return {
533
+ error: OAuth2ErrorCode.INVALID_GRANT,
534
+ error_description: 'Invalid authorization grant.',
535
+ };
536
+ }
537
+ } else if (clientSecret) {
538
+ if (client.client_secret !== clientSecret) {
539
+ return {
540
+ error: OAuth2ErrorCode.INVALID_CLIENT,
541
+ error_description: 'Client authentication failed.',
542
+ };
543
+ }
544
+ } else {
545
+ return {
546
+ error: OAuth2ErrorCode.INVALID_REQUEST,
547
+ error_description: 'Missing or invalid request parameter.',
548
+ };
549
+ }
550
+
551
+ const user = REGISTERED_USERS.find((u) => u.id === entry.userId);
552
+ if (!user) {
553
+ return {
554
+ error: OAuth2ErrorCode.INVALID_GRANT,
555
+ error_description: 'Invalid authorization grant.',
556
+ };
557
+ }
558
+
559
+ // Generate a signed JWT access token
560
+ const { token: accessToken } = await createJwtAccessToken!({
561
+ sub: entry.userId,
562
+ client_id: clientId,
563
+ scope: entry.scopes,
564
+ });
565
+
566
+ // Generate a signed JWT id token
567
+ const idToken = entry.scopes.includes('openid')
568
+ ? (await createIdToken!({ sub: entry.userId, name: user.username, aud: clientId })).token
569
+ : undefined;
570
+
571
+ // Return token response
572
+ return new OAuth2TokenResponse({ access_token: accessToken })
573
+ .setExpiresIn(ttl)
574
+ .setTokenType(tokenType)
575
+ .setScope(entry.scopes)
576
+ .setIdToken(idToken);
577
+ }
578
+ )
579
+ )
580
+
581
+ // Access Token Validation
582
+ .validate(async (_req, { jwtAccessTokenPayload }) => {
583
+ if (!jwtAccessTokenPayload?.sub) return { isValid: false };
584
+ return {
585
+ isValid: true,
586
+ credentials: {
587
+ user: {
588
+ id: jwtAccessTokenPayload.sub,
589
+ clientId: jwtAccessTokenPayload.client_id,
590
+ },
591
+ scope: Array.isArray(jwtAccessTokenPayload.scope) ? jwtAccessTokenPayload.scope : [],
592
+ },
593
+ };
594
+ })
595
+
596
+ // JWKS
597
+ .jwksRoute((route) => route.setPath('/discovery/v1.0/keys')) // activates jwks uri
598
+ .setPublicKeyExpiry(86400) // 24h
599
+ .setJwksKeyStore(createInMemoryKeyStore()) // store for JWKS
600
+ .setJwksRotatorOptions({
601
+ intervalMs: 7.884e9, // 91 days
602
+ timestampStore: createInMemoryKeyStore(),
603
+ });
604
+ `;
605
+ };
606
+ //# sourceMappingURL=oauth2-flow-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth2-flow-generator.js","sourceRoot":"","sources":["../../src/generators/oauth2-flow-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { FileGenerator, FileGeneratorType, Question } from '@kaapi/cli/definitions';
2
+ export declare class OAuth2UtilGenerator implements FileGenerator {
3
+ #private;
4
+ get type(): FileGeneratorType;
5
+ get name(): 'oauth2-util';
6
+ get description(): string;
7
+ get notes(): string[];
8
+ get options(): Record<string, string>;
9
+ init(options: Record<string, unknown>): void;
10
+ isValid(): boolean;
11
+ getFileContent(): string;
12
+ getQuestions(): Question[];
13
+ getFilename(): string;
14
+ }