@maronn-openid-connect/cli 0.7.0 → 0.8.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 (67) hide show
  1. package/README.md +83 -7
  2. package/dist/frameworks/hono/index.d.ts.map +1 -1
  3. package/dist/frameworks/hono/index.js +18 -9
  4. package/dist/frameworks/hono/index.js.map +1 -1
  5. package/dist/frameworks/hono/pages.d.ts +7 -6
  6. package/dist/frameworks/hono/pages.d.ts.map +1 -1
  7. package/dist/frameworks/hono/pages.js +62 -44
  8. package/dist/frameworks/hono/pages.js.map +1 -1
  9. package/dist/frameworks/hono/templates.d.ts +19 -6
  10. package/dist/frameworks/hono/templates.d.ts.map +1 -1
  11. package/dist/frameworks/hono/templates.js +439 -304
  12. package/dist/frameworks/hono/templates.js.map +1 -1
  13. package/dist/frameworks/hono/views.d.ts +3 -0
  14. package/dist/frameworks/hono/views.d.ts.map +1 -0
  15. package/dist/frameworks/hono/views.js +458 -0
  16. package/dist/frameworks/hono/views.js.map +1 -0
  17. package/dist/frameworks/nextjs/authorize.d.ts +3 -0
  18. package/dist/frameworks/nextjs/authorize.d.ts.map +1 -0
  19. package/dist/frameworks/nextjs/authorize.js +757 -0
  20. package/dist/frameworks/nextjs/authorize.js.map +1 -0
  21. package/dist/frameworks/nextjs/ciba.d.ts +8 -0
  22. package/dist/frameworks/nextjs/ciba.d.ts.map +1 -0
  23. package/dist/frameworks/nextjs/ciba.js +591 -0
  24. package/dist/frameworks/nextjs/ciba.js.map +1 -0
  25. package/dist/frameworks/nextjs/conformance.d.ts +3 -0
  26. package/dist/frameworks/nextjs/conformance.d.ts.map +1 -0
  27. package/dist/frameworks/nextjs/conformance.js +2447 -0
  28. package/dist/frameworks/nextjs/conformance.js.map +1 -0
  29. package/dist/frameworks/nextjs/device.d.ts +8 -0
  30. package/dist/frameworks/nextjs/device.d.ts.map +1 -0
  31. package/dist/frameworks/nextjs/device.js +662 -0
  32. package/dist/frameworks/nextjs/device.js.map +1 -0
  33. package/dist/frameworks/nextjs/endpoints.d.ts +7 -0
  34. package/dist/frameworks/nextjs/endpoints.d.ts.map +1 -0
  35. package/dist/frameworks/nextjs/endpoints.js +673 -0
  36. package/dist/frameworks/nextjs/endpoints.js.map +1 -0
  37. package/dist/frameworks/nextjs/index.d.ts.map +1 -1
  38. package/dist/frameworks/nextjs/index.js +135 -2
  39. package/dist/frameworks/nextjs/index.js.map +1 -1
  40. package/dist/frameworks/nextjs/interaction.d.ts +13 -0
  41. package/dist/frameworks/nextjs/interaction.d.ts.map +1 -0
  42. package/dist/frameworks/nextjs/interaction.js +864 -0
  43. package/dist/frameworks/nextjs/interaction.js.map +1 -0
  44. package/dist/frameworks/nextjs/logout.d.ts +5 -0
  45. package/dist/frameworks/nextjs/logout.d.ts.map +1 -0
  46. package/dist/frameworks/nextjs/logout.js +271 -0
  47. package/dist/frameworks/nextjs/logout.js.map +1 -0
  48. package/dist/frameworks/nextjs/metadata.d.ts +6 -0
  49. package/dist/frameworks/nextjs/metadata.d.ts.map +1 -0
  50. package/dist/frameworks/nextjs/metadata.js +339 -0
  51. package/dist/frameworks/nextjs/metadata.js.map +1 -0
  52. package/dist/frameworks/nextjs/provider.d.ts +6 -0
  53. package/dist/frameworks/nextjs/provider.d.ts.map +1 -0
  54. package/dist/frameworks/nextjs/provider.js +797 -0
  55. package/dist/frameworks/nextjs/provider.js.map +1 -0
  56. package/dist/frameworks/nextjs/token.d.ts +7 -0
  57. package/dist/frameworks/nextjs/token.d.ts.map +1 -0
  58. package/dist/frameworks/nextjs/token.js +1337 -0
  59. package/dist/frameworks/nextjs/token.js.map +1 -0
  60. package/dist/frameworks/web-standard/templates.d.ts +1 -13
  61. package/dist/frameworks/web-standard/templates.d.ts.map +1 -1
  62. package/dist/frameworks/web-standard/templates.js +12 -1296
  63. package/dist/frameworks/web-standard/templates.js.map +1 -1
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +38 -8
  66. package/dist/index.js.map +1 -1
  67. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../../src/frameworks/nextjs/authorize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { OidcFeatureConfig } from '../../features.js';
2
+ export declare function nextJsCibaConfigTemplate(): string;
3
+ export declare function nextJsBackchannelAuthenticationRouteTemplate(corePkg: string, features?: OidcFeatureConfig, scopes?: string[]): string;
4
+ export declare function nextJsCibaScreensTemplate(scopes?: string[]): string;
5
+ export declare function nextJsCibaRouteTemplate(): string;
6
+ export declare function nextJsCibaLoginRouteTemplate(corePkg: string): string;
7
+ export declare function nextJsCibaApproveRouteTemplate(corePkg: string, scopes?: string[]): string;
8
+ //# sourceMappingURL=ciba.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ciba.d.ts","sourceRoot":"","sources":["../../../src/frameworks/nextjs/ciba.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAM3D,wBAAgB,wBAAwB,IAAI,MAAM,CAqCjD;AAGD,wBAAgB,4CAA4C,CAC1D,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,iBAAoC,EAC9C,MAAM,GAAE,MAAM,EAAO,GACpB,MAAM,CAyJR;AAGD,wBAAgB,yBAAyB,CAAC,MAAM,GAAE,MAAM,EAAO,GAAG,MAAM,CAoKvE;AAGD,wBAAgB,uBAAuB,IAAI,MAAM,CA0ChD;AAGD,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAuFpE;AAGD,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,EAAO,GAAG,MAAM,CAkG7F"}
@@ -0,0 +1,591 @@
1
+ import { DEFAULT_FEATURES } from '../../features.js';
2
+ import { EXPERIMENTAL_PACKAGE } from '../hono/templates.js';
3
+ const CIBA_PACKAGE = `${EXPERIMENTAL_PACKAGE}/ciba`;
4
+ export function nextJsCibaConfigTemplate() {
5
+ return `/**
6
+ * EXPERIMENTAL — CIBA settings (CIBA Core 1.0).
7
+ *
8
+ * Read by the backchannel authentication endpoint and the authentication device
9
+ * UI (/ciba).
10
+ *
11
+ * - authReqIdExpiresIn: §7.3 expires_in, in seconds (range 30–600). Keep it
12
+ * short: it is the window the user has to approve, and the window in which a
13
+ * pending request can pile up on the approval screen.
14
+ * - pollingInterval: §7.3 interval, in seconds (range 1–60). The token endpoint
15
+ * raises a record's own interval by 5 every time it answers slow_down (§11).
16
+ * - maxPendingPerSubject: pending backchannel requests allowed per user (range
17
+ * 1–100) before new ones are refused — the flood defense for the approval
18
+ * screen (the role §7.1.2's unsupported user_code would otherwise play).
19
+ * - maxLoginAttempts: failed /ciba logins allowed per login transaction before
20
+ * it is discarded. Per-transaction only — see ciba/login/route.ts.
21
+ */
22
+ export const cibaConfig = {
23
+ authReqIdExpiresIn: 120,
24
+ pollingInterval: 5,
25
+ maxPendingPerSubject: 10,
26
+ maxLoginAttempts: 5,
27
+ };
28
+
29
+ // Fail fast on a config edit that leaves the documented ranges: a typo here
30
+ // weakens either the approval-screen flood cap or the polling contract.
31
+ if (cibaConfig.authReqIdExpiresIn < 30 || cibaConfig.authReqIdExpiresIn > 600) {
32
+ throw new Error('cibaConfig.authReqIdExpiresIn must be between 30 and 600 seconds');
33
+ }
34
+ if (cibaConfig.pollingInterval < 1 || cibaConfig.pollingInterval > 60) {
35
+ throw new Error('cibaConfig.pollingInterval must be between 1 and 60 seconds');
36
+ }
37
+ if (cibaConfig.maxPendingPerSubject < 1 || cibaConfig.maxPendingPerSubject > 100) {
38
+ throw new Error('cibaConfig.maxPendingPerSubject must be between 1 and 100');
39
+ }
40
+ `;
41
+ }
42
+ export function nextJsBackchannelAuthenticationRouteTemplate(corePkg, features = DEFAULT_FEATURES, scopes = []) {
43
+ const customScopesDeclared = scopes.length > 0;
44
+ const customScopeImport = customScopesDeclared
45
+ ? `
46
+ import { findUnsupportedScopes } from '../_oidc-provider/scopes';`
47
+ : '';
48
+ const customScopeStep = customScopesDeclared
49
+ ? ` // RFC 6749 §3.3: reject a scope this OP never declared, the same allow list
50
+ // /authorize uses (scopes.ts). Checked before the pipeline so a request for
51
+ // an unknown scope never reaches the store. offline_access is left out: the
52
+ // pipeline applies its own policy and ignores it when it cannot be granted
53
+ // (OIDC Core 1.0 §11), which must not become an error.
54
+ const unsupportedScopes = findUnsupportedScopes(
55
+ (params['scope'] ?? '')
56
+ .split(' ')
57
+ .filter((scope) => scope.length > 0 && scope !== 'offline_access'),
58
+ );
59
+ if (unsupportedScopes.length > 0) {
60
+ throw new BackchannelAuthenticationError(
61
+ 'invalid_scope',
62
+ 'Unsupported scope: ' + unsupportedScopes.join(' '),
63
+ );
64
+ }
65
+
66
+ `
67
+ : '';
68
+ return `/**
69
+ * EXPERIMENTAL — Backchannel Authentication Endpoint (CIBA Core 1.0, poll mode).
70
+ *
71
+ * Generated because the OP was created with \`--enable ciba\`. Backed by
72
+ * ${EXPERIMENTAL_PACKAGE}, whose API is NOT stable: it may change in a breaking
73
+ * way between releases. Do not build production code on it without pinning the
74
+ * version.
75
+ *
76
+ * The consumption device (a call-center console, a kiosk, a smart speaker
77
+ * backend) POSTs here — back channel, client-authenticated — with a login_hint
78
+ * naming the user, and receives an auth_req_id it polls the token endpoint
79
+ * with. The user approves or denies on their own browser at /ciba.
80
+ *
81
+ * NOTE (CIBA §15): the login_hint is a user identifier and therefore PII. Never
82
+ * log it, and never echo it in an error_description. Rate limiting is left to
83
+ * the deployment layer (reverse proxy / platform); the in-band defenses are
84
+ * mandatory client authentication, the fixed unknown_user_id wording, and the
85
+ * per-subject pending-request cap (config.ts).
86
+ */
87
+ import {
88
+ BackchannelAuthenticationError,
89
+ processBackchannelAuthenticationRequest,
90
+ type CibaClientInfo,
91
+ } from '${CIBA_PACKAGE}';
92
+ import {
93
+ TokenError,
94
+ extractClientCredentials,
95
+ resolveAuthenticatedTokenClient,
96
+ sanitizeErrorDescription,
97
+ validateClientAuthMethod,
98
+ verifyClientSecret,
99
+ } from '${corePkg}';
100
+ import {
101
+ cibaAuthenticationRequestStore,
102
+ clientResolver,
103
+ resolveCibaUser,
104
+ } from '../_oidc-provider/provider';
105
+ import {
106
+ clientCors,
107
+ corsPreflight,
108
+ isFormUrlEncoded,
109
+ noStoreJson,
110
+ oauthError,
111
+ uniqueParams,
112
+ withCors,
113
+ } from '../_oidc-provider/http';${customScopeImport}
114
+ import { cibaConfig } from './config';
115
+
116
+ export const dynamic = 'force-dynamic';
117
+ export const runtime = 'nodejs';
118
+
119
+ export async function POST(request: Request): Promise<Response> {
120
+ return withCors(request, clientCors, await backchannelAuthentication(request));
121
+ }
122
+
123
+ export function OPTIONS(request: Request): Response {
124
+ return corsPreflight(request, clientCors);
125
+ }
126
+
127
+ /**
128
+ * auth_req_id is a credential, so every answer follows the token response rules
129
+ * of RFC 6749 §5.1 / §5.2 (no-store).
130
+ */
131
+ async function backchannelAuthentication(request: Request): Promise<Response> {
132
+ // CIBA §7.1: the body MUST be application/x-www-form-urlencoded.
133
+ if (!isFormUrlEncoded(request)) {
134
+ return oauthError('invalid_request', 'Backchannel authentication requests must use application/x-www-form-urlencoded');
135
+ }
136
+
137
+ // RFC 6749 §3.1: request parameters MUST NOT be repeated.
138
+ const { params, duplicateKey } = uniqueParams(new URLSearchParams(await request.text()));
139
+ if (duplicateKey !== undefined) {
140
+ return oauthError('invalid_request', \`Parameter "\${sanitizeErrorDescription(duplicateKey)}" must not be repeated\`);
141
+ }
142
+
143
+ try {
144
+ // --- Client authentication pipeline -------------------------------------
145
+ // CIBA §7.1: "The Client MUST authenticate ... using the authentication
146
+ // method registered for its client_id" — the same pipeline the token
147
+ // endpoint runs.
148
+ const presentedCredentials = extractClientCredentials({
149
+ params,
150
+ authorizationHeader: request.headers.get('Authorization') ?? '',
151
+ });
152
+ const client = await resolveAuthenticatedTokenClient(
153
+ presentedCredentials.clientId,
154
+ clientResolver,
155
+ );
156
+ validateClientAuthMethod(client, presentedCredentials);
157
+ await verifyClientSecret(client, presentedCredentials.clientSecret);
158
+
159
+ ${customScopeStep} // --- Backchannel authentication pipeline --------------------------------
160
+ // Validation runs in CIBA §7.1 order inside the experimental package:
161
+ // client checks (public client / grant registration / delivery mode) →
162
+ // request parameter rejection → the one-and-only-one hint rule → scope →
163
+ // binding_message → requested_expiry → login_hint resolution (resolveCibaUser
164
+ // in provider.ts) → the per-subject pending cap → record creation.
165
+ const response = await processBackchannelAuthenticationRequest({
166
+ params,
167
+ client: client as CibaClientInfo,
168
+ store: cibaAuthenticationRequestStore,
169
+ config: cibaConfig,
170
+ refreshTokenFeatureEnabled: ${features.refreshToken ? 'true' : 'false'},
171
+ resolveUser: resolveCibaUser,
172
+ });
173
+
174
+ // Never log auth_req_id (a live credential) or login_hint (PII, CIBA §15).
175
+
176
+ return noStoreJson(response);
177
+ } catch (error) {
178
+ if (error instanceof BackchannelAuthenticationError) {
179
+ // CIBA §13 / RFC 6749 §5.2 shape. Authentication failures are core
180
+ // TokenErrors (401).
181
+ return oauthError(error.code, error.errorDescription, error.statusCode);
182
+ }
183
+ if (error instanceof TokenError) {
184
+ return oauthError(
185
+ error.error,
186
+ error.errorDescription,
187
+ error.statusCode,
188
+ error.wwwAuthenticate ? { 'WWW-Authenticate': error.wwwAuthenticate } : undefined,
189
+ );
190
+ }
191
+ return oauthError('server_error', undefined, 500);
192
+ }
193
+ }
194
+ `;
195
+ }
196
+ export function nextJsCibaScreensTemplate(scopes = []) {
197
+ const customScopesDeclared = scopes.length > 0;
198
+ const customScopeImport = customScopesDeclared
199
+ ? `
200
+ import { resolveGrantableScopes } from '../_oidc-provider/scopes';`
201
+ : '';
202
+ const requestRows = customScopesDeclared
203
+ ? ` const requests = await Promise.all(
204
+ pending.map(async (record) => ({
205
+ ...record,
206
+ // Show only what this End-User can actually grant (scopes.ts).
207
+ scope: await resolveGrantableScopes(record.scope, subject),
208
+ })),
209
+ );`
210
+ : ` const requests = pending;`;
211
+ return `/**
212
+ * EXPERIMENTAL — the screens of the CIBA authentication device UI
213
+ * (CIBA Core 1.0 §7.1).
214
+ *
215
+ * Restyle the UI here. Keep the form actions, field names and decision values:
216
+ * the Route Handlers next to this file read exactly those.
217
+ */
218
+ import { CibaVerificationError, listPendingCibaRequests } from '${CIBA_PACKAGE}';
219
+ import { cibaAuthenticationRequestStore } from '../_oidc-provider/provider';
220
+ import { errorPage, escapeHtml, htmlResponse } from '../_oidc-provider/html';${customScopeImport}
221
+
222
+ /** The sign-in form; cookies carry the login transaction's browser binding. */
223
+ export function loginScreen(
224
+ params: { loginTransactionId: string; csrfToken: string; error?: string; remainingAttempts?: number },
225
+ cookies: readonly string[] = [],
226
+ ): Response {
227
+ const errorHtml = params.error
228
+ ? \`<p style="color: red;">\${escapeHtml(params.error)}\${
229
+ params.remainingAttempts !== undefined
230
+ ? \`. Attempts remaining: \${params.remainingAttempts}\`
231
+ : ''
232
+ }</p>\`
233
+ : '';
234
+ return htmlResponse(
235
+ \`<!DOCTYPE html>
236
+ <html>
237
+ <head><title>Sign in</title></head>
238
+ <body>
239
+ <h1>Sign in</h1>
240
+ <p>Sign in to review sign-in requests sent to you.</p>
241
+ \${errorHtml}
242
+ <form method="POST" action="/ciba/login">
243
+ <input type="hidden" name="login_transaction_id" value="\${escapeHtml(params.loginTransactionId)}" />
244
+ <input type="hidden" name="csrf_token" value="\${escapeHtml(params.csrfToken)}" />
245
+ <div>
246
+ <label for="username">Username:</label>
247
+ <input type="text" id="username" name="username" required />
248
+ </div>
249
+ <div>
250
+ <label for="password">Password:</label>
251
+ <input type="password" id="password" name="password" required />
252
+ </div>
253
+ <button type="submit">Login</button>
254
+ </form>
255
+ </body>
256
+ </html>\`,
257
+ { cookies },
258
+ );
259
+ }
260
+
261
+ /**
262
+ * The pending requests addressed to subject, each with a freshly rotated CSRF
263
+ * token — the only place those tokens are ever shown, and it is session-gated.
264
+ *
265
+ * CIBA Core 1.0 §7.1: the binding_message is repeated so the user can check it
266
+ * against the device that started the request before approving. It is
267
+ * client-supplied text, so it is escaped like everything else. Deny is shown
268
+ * with the same prominence as Approve.
269
+ */
270
+ export async function pendingRequestsScreen(
271
+ subject: string,
272
+ cookies: readonly string[] = [],
273
+ ): Promise<Response> {
274
+ const pending = await listPendingCibaRequests({ subject, store: cibaAuthenticationRequestStore });
275
+ ${requestRows}
276
+ if (requests.length === 0) {
277
+ return htmlResponse(
278
+ \`<!DOCTYPE html>
279
+ <html>
280
+ <head><title>Sign-in Requests</title></head>
281
+ <body>
282
+ <h1>Sign-in Requests</h1>
283
+ <p>No pending sign-in requests.</p>
284
+ </body>
285
+ </html>\`,
286
+ { cookies },
287
+ );
288
+ }
289
+
290
+ const requestListHtml = requests
291
+ .map((request) => {
292
+ const scopeListHtml = request.scope
293
+ .map((scope) => \` <li>\${escapeHtml(scope)}</li>\`)
294
+ .join('\\n');
295
+ const bindingMessageHtml = request.bindingMessage
296
+ ? \` <p>Confirm that your device is showing this message: <strong>\${escapeHtml(request.bindingMessage)}</strong></p>\\n\`
297
+ : '';
298
+ return \` <section>
299
+ <p>Client <strong>\${escapeHtml(request.clientId)}</strong> is requesting access to the following scopes:</p>
300
+ <ul>
301
+ \${scopeListHtml}
302
+ </ul>
303
+ \${bindingMessageHtml} <p>This request expires in \${remainingSeconds(request.expiresAt)} seconds.</p>
304
+ <form method="POST" action="/ciba/approve">
305
+ <input type="hidden" name="auth_req_id" value="\${escapeHtml(request.authReqId)}" />
306
+ <input type="hidden" name="csrf_token" value="\${escapeHtml(request.csrfToken ?? '')}" />
307
+ <button type="submit" name="decision" value="approve">Approve</button>
308
+ <button type="submit" name="decision" value="deny">Deny</button>
309
+ </form>
310
+ </section>\`;
311
+ })
312
+ .join('\\n');
313
+
314
+ return htmlResponse(
315
+ \`<!DOCTYPE html>
316
+ <html>
317
+ <head><title>Sign-in Requests</title></head>
318
+ <body>
319
+ <h1>Sign-in Requests</h1>
320
+ <p>Only approve a request you started yourself on another device.</p>
321
+ \${requestListHtml}
322
+ </body>
323
+ </html>\`,
324
+ { cookies },
325
+ );
326
+ }
327
+
328
+ /** The decision-recorded screen. */
329
+ export function completedScreen(params: { approved: boolean; clientId: string }): Response {
330
+ const outcome = params.approved
331
+ ? \`<p>You approved <strong>\${escapeHtml(params.clientId)}</strong>.</p>\`
332
+ : \`<p>You denied <strong>\${escapeHtml(params.clientId)}</strong>.</p>\`;
333
+ return htmlResponse(\`<!DOCTYPE html>
334
+ <html>
335
+ <head><title>Sign-in Requests</title></head>
336
+ <body>
337
+ <h1>Sign-in Requests</h1>
338
+ \${outcome}
339
+ <p>You can close this page and go back to your device.</p>
340
+ </body>
341
+ </html>\`);
342
+ }
343
+
344
+ /**
345
+ * A failed binding, CSRF or ownership check, shown on the OP's error page with
346
+ * its status. Anything that is not a verification failure is rethrown.
347
+ */
348
+ export function verificationFailureScreen(error: unknown): Response {
349
+ if (error instanceof CibaVerificationError) {
350
+ return errorPage(error.message, error.statusCode);
351
+ }
352
+ throw error;
353
+ }
354
+
355
+ /** Remaining lifetime in whole seconds, never negative. */
356
+ export function remainingSeconds(expiresAt: Date): number {
357
+ return Math.max(0, Math.ceil((expiresAt.getTime() - Date.now()) / 1000));
358
+ }
359
+ `;
360
+ }
361
+ export function nextJsCibaRouteTemplate() {
362
+ return `/**
363
+ * EXPERIMENTAL — CIBA authentication device UI, entry (CIBA Core 1.0 §7.1).
364
+ *
365
+ * CIBA leaves the authentication device — how the user is reached and how they
366
+ * authenticate — outside the specification. This UI is an OP-hosted page the
367
+ * user visits themselves: sign in at /ciba, review the pending requests
368
+ * addressed to you (client, scopes, binding_message), approve or deny. The
369
+ * consumption device learns the outcome only by polling the token endpoint.
370
+ *
371
+ * Backed by ${EXPERIMENTAL_PACKAGE}, whose API is NOT stable.
372
+ */
373
+ import { createCibaLoginTransaction } from '${CIBA_PACKAGE}';
374
+ import { cibaLoginTransactionStore, stores } from '../_oidc-provider/provider';
375
+ import { buildCibaLoginBindingCookie, parseSessionId } from '../_oidc-provider/store';
376
+ import { loginScreen, pendingRequestsScreen, remainingSeconds } from './screens';
377
+
378
+ export const dynamic = 'force-dynamic';
379
+ export const runtime = 'nodejs';
380
+
381
+ /**
382
+ * With an OP session: the pending requests addressed to the signed-in user.
383
+ * Without one: a fresh login transaction and its sign-in form, bound to this
384
+ * browser by a cookie. A hidden csrf_token alone cannot stop login CSRF — the
385
+ * attacker can fetch a valid pair from their own form — so the submission must
386
+ * also present this browser's binding (see buildCibaLoginBindingCookie() in
387
+ * store.ts).
388
+ */
389
+ export async function GET(request: Request): Promise<Response> {
390
+ const sessionId = parseSessionId(request.headers.get('Cookie'));
391
+ const session = sessionId ? await stores.browserSessionStore.get(sessionId) : undefined;
392
+ if (session) {
393
+ return pendingRequestsScreen(session.subject);
394
+ }
395
+
396
+ const { record, bindingSecret } = await createCibaLoginTransaction(cibaLoginTransactionStore);
397
+ return loginScreen(
398
+ { loginTransactionId: record.id, csrfToken: record.csrfToken },
399
+ [buildCibaLoginBindingCookie(record.id, bindingSecret, remainingSeconds(record.expiresAt))],
400
+ );
401
+ }
402
+ `;
403
+ }
404
+ export function nextJsCibaLoginRouteTemplate(corePkg) {
405
+ return `/**
406
+ * EXPERIMENTAL — CIBA authentication device UI, sign in.
407
+ *
408
+ * Binding first, then CSRF, then credentials: the binding proves this is the
409
+ * browser the login form was issued to, and it must gate the step that would
410
+ * otherwise let a forged POST plant an OP session in the victim's browser.
411
+ *
412
+ * Backed by ${EXPERIMENTAL_PACKAGE}, whose API is NOT stable.
413
+ */
414
+ import { recordCibaLoginFailure, validateCibaLoginSubmission } from '${CIBA_PACKAGE}';
415
+ import { generateRandomString } from '${corePkg}';
416
+ import { errorPage } from '../../_oidc-provider/html';
417
+ import { readFormFields } from '../../_oidc-provider/http';
418
+ import { cibaLoginTransactionStore, stores } from '../../_oidc-provider/provider';
419
+ import {
420
+ buildClearedCibaLoginBindingCookie,
421
+ buildSessionCookie,
422
+ parseCibaLoginBindingSecret,
423
+ } from '../../_oidc-provider/store';
424
+ import { cibaConfig } from '../../backchannel_authentication/config';
425
+ import { loginScreen, pendingRequestsScreen, verificationFailureScreen } from '../screens';
426
+
427
+ export const dynamic = 'force-dynamic';
428
+ export const runtime = 'nodejs';
429
+
430
+ export async function POST(request: Request): Promise<Response> {
431
+ const form = await readFormFields(request);
432
+ const transactionId = String(form.get('login_transaction_id') ?? '');
433
+
434
+ let transaction;
435
+ try {
436
+ transaction = await validateCibaLoginSubmission({
437
+ transactionId,
438
+ csrfToken: String(form.get('csrf_token') ?? ''),
439
+ bindingSecret: parseCibaLoginBindingSecret(request.headers.get('Cookie'), transactionId),
440
+ store: cibaLoginTransactionStore,
441
+ });
442
+ } catch (error) {
443
+ return verificationFailureScreen(error);
444
+ }
445
+
446
+ // Swap point: replace this with your own credential check (LDAP, WebAuthn, an
447
+ // upstream IdP) without touching anything above or below it.
448
+ const user = await stores.userStore.authenticate(
449
+ String(form.get('username') ?? ''),
450
+ String(form.get('password') ?? ''),
451
+ );
452
+ if (!user) {
453
+ // Per-transaction throttling only. Anyone can mint fresh login transactions
454
+ // by reloading /ciba, so the aggregate password-guess budget is the same as
455
+ // the one on /login. Subject-scoped throttling is a separate concern.
456
+ const failure = await recordCibaLoginFailure(
457
+ transaction,
458
+ cibaLoginTransactionStore,
459
+ cibaConfig.maxLoginAttempts,
460
+ );
461
+ if (!failure.canRetry) {
462
+ // The transaction is gone: this form cannot be retried at all.
463
+ return errorPage('Too many login attempts', 429);
464
+ }
465
+ return loginScreen({
466
+ loginTransactionId: transaction.id,
467
+ csrfToken: transaction.csrfToken,
468
+ error: 'Invalid credentials',
469
+ remainingAttempts: failure.remainingAttempts,
470
+ });
471
+ }
472
+
473
+ // The transaction is single-use: a successful login consumes it, and the
474
+ // session is established under a NEWLY minted id (never one the request
475
+ // brought along — session fixation).
476
+ await cibaLoginTransactionStore.delete(transaction.id);
477
+ const sessionId = generateRandomString(32);
478
+ await stores.browserSessionStore.set(sessionId, {
479
+ subject: user.sub,
480
+ authTime: Math.floor(Date.now() / 1000),
481
+ });
482
+
483
+ // Two cookies travel with the listing: the new OP session, and the cleared
484
+ // login binding (it is single-use and would otherwise linger until Max-Age).
485
+ return pendingRequestsScreen(user.sub, [
486
+ buildSessionCookie(sessionId),
487
+ buildClearedCibaLoginBindingCookie(transaction.id),
488
+ ]);
489
+ }
490
+ `;
491
+ }
492
+ export function nextJsCibaApproveRouteTemplate(corePkg, scopes = []) {
493
+ const customScopesDeclared = scopes.length > 0;
494
+ const customScopeImport = customScopesDeclared
495
+ ? `
496
+ import { resolveGrantableScopes } from '../../_oidc-provider/scopes';`
497
+ : '';
498
+ const approveNarrowStep = customScopesDeclared
499
+ ? `
500
+ // Apply the scope policy to what was approved. approveCibaRequest() copies
501
+ // the requested scope into approvedScope, so the policy is applied
502
+ // afterwards and persisted; the token endpoint reads approvedScope, and
503
+ // RFC 6749 §3.3 allows a granted scope narrower than the request. Write the
504
+ // policy in resolveGrantableScopes() (scopes.ts).
505
+ approved.approvedScope = await resolveGrantableScopes(
506
+ approved.approvedScope ?? approved.scope,
507
+ session.subject,
508
+ );
509
+ await cibaAuthenticationRequestStore.update(approved);
510
+ `
511
+ : '';
512
+ return `/**
513
+ * EXPERIMENTAL — CIBA authentication device UI, approve or deny.
514
+ *
515
+ * The only state-changing step of the UI. It demands an OP session whose
516
+ * subject owns the record, plus the per-record csrf_token from the
517
+ * session-gated listing. No binding cookie is needed here: the approval is
518
+ * already bound to the authenticated session, so knowing an auth_req_id gives
519
+ * an attacker no step to forge.
520
+ *
521
+ * Backed by ${EXPERIMENTAL_PACKAGE}, whose API is NOT stable.
522
+ */
523
+ import { approveCibaRequest, denyCibaRequest } from '${CIBA_PACKAGE}';
524
+ import { generateRandomString } from '${corePkg}';
525
+ import { errorPage } from '../../_oidc-provider/html';
526
+ import { readFormFields } from '../../_oidc-provider/http';
527
+ import { cibaAuthenticationRequestStore, resolvers, stores } from '../../_oidc-provider/provider';
528
+ import { parseSessionId } from '../../_oidc-provider/store';${customScopeImport}
529
+ import { completedScreen, verificationFailureScreen } from '../screens';
530
+
531
+ export const dynamic = 'force-dynamic';
532
+ export const runtime = 'nodejs';
533
+
534
+ export async function POST(request: Request): Promise<Response> {
535
+ const form = await readFormFields(request);
536
+ const authReqId = String(form.get('auth_req_id') ?? '');
537
+ const csrfToken = String(form.get('csrf_token') ?? '');
538
+ const decision = String(form.get('decision') ?? '');
539
+
540
+ const sessionId = parseSessionId(request.headers.get('Cookie'));
541
+ const session = sessionId ? await stores.browserSessionStore.get(sessionId) : undefined;
542
+ if (!session) {
543
+ return errorPage('Sign in again to review this request', 401);
544
+ }
545
+
546
+ if (decision !== 'approve' && decision !== 'deny') {
547
+ return errorPage('invalid_request', 400, 'decision must be approve or deny');
548
+ }
549
+
550
+ try {
551
+ if (decision === 'approve') {
552
+ // subject and csrf_token are validated inside; the record moves to
553
+ // approved with auth_time, scope and a fresh grantId the token endpoint
554
+ // reads.
555
+ const grantId = generateRandomString(32);
556
+ const approved = await approveCibaRequest({
557
+ authReqId,
558
+ subject: session.subject,
559
+ csrfToken,
560
+ authTime: session.authTime,
561
+ grantId,
562
+ store: cibaAuthenticationRequestStore,
563
+ });
564
+ ${approveNarrowStep}
565
+ // Record the consent the way /consent does, so a later Authorization Code
566
+ // Flow for this client skips the consent screen (OIDC Core 1.0 §3.1.2.4),
567
+ // and the grant, so withdrawing that consent revokes these tokens too.
568
+ await resolvers.consentResolver.recordConsent?.(
569
+ session.subject,
570
+ approved.clientId,
571
+ approved.approvedScope ?? approved.scope,
572
+ );
573
+ await resolvers.consentResolver.recordGrant(session.subject, approved.clientId, grantId);
574
+ return completedScreen({ approved: true, clientId: approved.clientId });
575
+ }
576
+
577
+ const record = await cibaAuthenticationRequestStore.findByAuthReqId(authReqId);
578
+ await denyCibaRequest({
579
+ authReqId,
580
+ subject: session.subject,
581
+ csrfToken,
582
+ store: cibaAuthenticationRequestStore,
583
+ });
584
+ return completedScreen({ approved: false, clientId: record?.clientId ?? '' });
585
+ } catch (error) {
586
+ return verificationFailureScreen(error);
587
+ }
588
+ }
589
+ `;
590
+ }
591
+ //# sourceMappingURL=ciba.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ciba.js","sourceRoot":"","sources":["../../../src/frameworks/nextjs/ciba.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D,MAAM,YAAY,GAAG,GAAG,oBAAoB,OAAO,CAAC;AAGpD,MAAM,UAAU,wBAAwB;IACtC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmCR,CAAC;AACF,CAAC;AAGD,MAAM,UAAU,4CAA4C,CAC1D,OAAe,EACf,QAAQ,GAAsB,gBAAgB,EAC9C,MAAM,GAAa,EAAE;IAErB,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IAC/C,MAAM,iBAAiB,GAAG,oBAAoB;QAC5C,CAAC,CAAC;kEAC4D;QAC9D,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,eAAe,GAAG,oBAAoB;QAC1C,CAAC,CAAC;;;;;;;;;;;;;;;;;CAiBL;QACG,CAAC,CAAC,EAAE,CAAC;IACP,OAAO;;;;KAIJ,oBAAoB;;;;;;;;;;;;;;;;;;;UAmBf,YAAY;;;;;;;;UAQZ,OAAO;;;;;;;;;;;;;;kCAciB,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8CjD,eAAe;;;;;;;;;;;oCAWmB,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;CAwB3E,CAAC;AACF,CAAC;AAGD,MAAM,UAAU,yBAAyB,CAAC,MAAM,GAAa,EAAE;IAC7D,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IAC/C,MAAM,iBAAiB,GAAG,oBAAoB;QAC5C,CAAC,CAAC;mEAC6D;QAC/D,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,WAAW,GAAG,oBAAoB;QACtC,CAAC,CAAC;;;;;;KAMD;QACD,CAAC,CAAC,6BAA6B,CAAC;IAClC,OAAO;;;;;;;kEAOyD,YAAY;;+EAEC,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuD9F,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoFZ,CAAC;AACF,CAAC;AAGD,MAAM,UAAU,uBAAuB;IACrC,OAAO;;;;;;;;;eASM,oBAAoB;;8CAEW,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BzD,CAAC;AACF,CAAC;AAGD,MAAM,UAAU,4BAA4B,CAAC,OAAe;IAC1D,OAAO;;;;;;;eAOM,oBAAoB;;uEAEoC,YAAY;wCAC3C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2E9C,CAAC;AACF,CAAC;AAGD,MAAM,UAAU,8BAA8B,CAAC,OAAe,EAAE,MAAM,GAAa,EAAE;IACnF,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IAC/C,MAAM,iBAAiB,GAAG,oBAAoB;QAC5C,CAAC,CAAC;sEACgE;QAClE,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,iBAAiB,GAAG,oBAAoB;QAC5C,CAAC,CAAC;;;;;;;;;;;CAWL;QACG,CAAC,CAAC,EAAE,CAAC;IACP,OAAO;;;;;;;;;eASM,oBAAoB;;uDAEoB,YAAY;wCAC3B,OAAO;;;;8DAIe,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoC7E,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;CAyBlB,CAAC;AACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { OidcFeatureConfig } from '../../features.js';
2
+ export declare function nextJsConformanceTestTemplate(features?: OidcFeatureConfig, scopes?: string[]): string;
3
+ //# sourceMappingURL=conformance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conformance.d.ts","sourceRoot":"","sources":["../../../src/frameworks/nextjs/conformance.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,wBAAgB,6BAA6B,CAC3C,QAAQ,GAAE,iBAAoC,EAC9C,MAAM,GAAE,MAAM,EAAO,GACpB,MAAM,CA6BR"}