@absolutejs/auth 0.54.9 → 0.55.1

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/dist/index.js CHANGED
@@ -3112,11 +3112,684 @@ var resolveAgentPrincipal = async (request, config) => {
3112
3112
  };
3113
3113
  };
3114
3114
 
3115
+ // src/agents/registration.ts
3116
+ init_constants();
3117
+ init_crypto();
3118
+
3119
+ // src/oidc/keys.ts
3120
+ var ENCODER = new TextEncoder;
3121
+ var ES256 = { hash: "SHA-256", name: "ECDSA" };
3122
+ var KEY_PARAMS = { name: "ECDSA", namedCurve: "P-256" };
3123
+ var toBase64Url = (bytes) => Buffer.from(bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)).toString("base64url");
3124
+ var fromBase64Url = (value) => new Uint8Array(Buffer.from(value, "base64url"));
3125
+ var encodeSegment = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
3126
+ var decodeSegment = (segment) => {
3127
+ try {
3128
+ const value = JSON.parse(Buffer.from(segment, "base64url").toString("utf8"));
3129
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
3130
+ return;
3131
+ }
3132
+ return Object.fromEntries(Object.entries(value));
3133
+ } catch {
3134
+ return;
3135
+ }
3136
+ };
3137
+ var generateSigningKey = async () => {
3138
+ const pair = await crypto.subtle.generateKey(KEY_PARAMS, true, [
3139
+ "sign",
3140
+ "verify"
3141
+ ]);
3142
+ const privateJwk = await crypto.subtle.exportKey("jwk", pair.privateKey);
3143
+ const publicJwk = await crypto.subtle.exportKey("jwk", pair.publicKey);
3144
+ return { kid: await jwkThumbprint(publicJwk), privateJwk, publicJwk };
3145
+ };
3146
+ var jwkThumbprint = async (jwk) => {
3147
+ const canonical = JSON.stringify({
3148
+ crv: jwk.crv,
3149
+ kty: jwk.kty,
3150
+ x: jwk.x,
3151
+ y: jwk.y
3152
+ });
3153
+ return toBase64Url(await crypto.subtle.digest("SHA-256", ENCODER.encode(canonical)));
3154
+ };
3155
+ var signJwt = async (payload, signing, typ = "JWT") => {
3156
+ const key = await crypto.subtle.importKey("jwk", signing.privateJwk, KEY_PARAMS, false, ["sign"]);
3157
+ const input = `${encodeSegment({ alg: "ES256", kid: signing.kid, typ })}.${encodeSegment(payload)}`;
3158
+ const signature = await crypto.subtle.sign(ES256, key, ENCODER.encode(input));
3159
+ return `${input}.${toBase64Url(signature)}`;
3160
+ };
3161
+ var toPublicJwk = (key) => ({
3162
+ alg: "ES256",
3163
+ crv: key.publicJwk.crv,
3164
+ kid: key.kid,
3165
+ kty: key.publicJwk.kty,
3166
+ use: "sig",
3167
+ x: key.publicJwk.x,
3168
+ y: key.publicJwk.y
3169
+ });
3170
+ var verifyJwt = async (token, publicJwk) => {
3171
+ const [headerSegment, payloadSegment, signatureSegment] = token.split(".");
3172
+ if (headerSegment === undefined || payloadSegment === undefined || signatureSegment === undefined) {
3173
+ return;
3174
+ }
3175
+ const key = await crypto.subtle.importKey("jwk", publicJwk, KEY_PARAMS, false, ["verify"]);
3176
+ const valid = await crypto.subtle.verify(ES256, key, fromBase64Url(signatureSegment), ENCODER.encode(`${headerSegment}.${payloadSegment}`));
3177
+ if (!valid)
3178
+ return;
3179
+ const header = decodeSegment(headerSegment);
3180
+ const payload = decodeSegment(payloadSegment);
3181
+ if (header === undefined || payload === undefined)
3182
+ return;
3183
+ return {
3184
+ header,
3185
+ payload
3186
+ };
3187
+ };
3188
+
3189
+ // src/agents/registration.ts
3190
+ var AGENT_CLAIM_GRANT_TYPE = "urn:workos:agent-auth:grant-type:claim";
3191
+ var AGENT_IDENTITY_ASSERTION_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer";
3192
+ var AGENT_IDENTITY_ASSERTION_TYPE = "urn:ietf:params:oauth:token-type:id-jag";
3193
+ var DEFAULT_IDENTITY_ROUTE = "/agent/identity";
3194
+ var DEFAULT_CLAIM_ROUTE = "/agent/identity/claim";
3195
+ var DEFAULT_COMPLETE_ROUTE = "/agent/identity/claim/complete";
3196
+ var DEFAULT_GUIDE_ROUTE = "/auth.md";
3197
+ var DEFAULT_CLAIM_TTL_MS = 24 * 60 * MILLISECONDS_IN_A_MINUTE;
3198
+ var DEFAULT_ATTEMPT_TTL_MS = 10 * MILLISECONDS_IN_A_MINUTE;
3199
+ var DEFAULT_ASSERTION_TTL_MS = 60 * MILLISECONDS_IN_A_MINUTE;
3200
+ var DEFAULT_ACCESS_TOKEN_TTL_MS2 = 15 * MILLISECONDS_IN_A_MINUTE;
3201
+ var DEFAULT_MAX_AUTH_AGE_MS = 60 * MILLISECONDS_IN_A_MINUTE;
3202
+ var DEFAULT_POLL_INTERVAL_SECONDS = 5;
3203
+ var DEFAULT_MAX_CODE_ATTEMPTS = 5;
3204
+ var MAX_CONCURRENT_UPDATE_RETRIES = 5;
3205
+ var TOKEN_BYTES2 = 32;
3206
+ var agentRegistrationDiscoveryMetadata = (config) => {
3207
+ const registration = requiredRegistration(config);
3208
+ const endpoints = agentRegistrationEndpoints(config);
3209
+ const identityTypes = ["identity_assertion"];
3210
+ if (registration.allowAnonymous === true)
3211
+ identityTypes.unshift("anonymous");
3212
+ if (registration.allowServiceAuth === true)
3213
+ identityTypes.push("service_auth");
3214
+ return {
3215
+ claim_endpoint: endpoints.claimEndpoint,
3216
+ identity_assertion: {
3217
+ assertion_types_supported: [AGENT_IDENTITY_ASSERTION_TYPE]
3218
+ },
3219
+ identity_endpoint: endpoints.identityEndpoint,
3220
+ identity_types_supported: identityTypes,
3221
+ skill: endpoints.guide
3222
+ };
3223
+ };
3224
+ var agentRegistrationEndpoints = (config) => {
3225
+ const registration = requiredRegistration(config);
3226
+ const base = config.authorizationServer;
3227
+ const oidcRoute = config.oidcRoute ?? "/oauth2";
3228
+ return {
3229
+ claimEndpoint: new URL(registration.claimRoute ?? DEFAULT_CLAIM_ROUTE, base).toString(),
3230
+ completeEndpoint: new URL(registration.completeRoute ?? DEFAULT_COMPLETE_ROUTE, base).toString(),
3231
+ guide: new URL(registration.guideRoute ?? DEFAULT_GUIDE_ROUTE, base).toString(),
3232
+ identityEndpoint: new URL(registration.identityRoute ?? DEFAULT_IDENTITY_ROUTE, base).toString(),
3233
+ tokenEndpoint: new URL(`${oidcRoute}/token`, base).toString()
3234
+ };
3235
+ };
3236
+ var markdownJson = (value) => `\`\`\`json
3237
+ ${JSON.stringify(value, null, 2)}
3238
+ \`\`\``;
3239
+ var generateAgentRegistrationGuide = (config) => {
3240
+ const registration = requiredRegistration(config);
3241
+ const endpoints = agentRegistrationEndpoints(config);
3242
+ const metadataUrl = new URL(config.metadataRoute ?? "/.well-known/oauth-protected-resource", config.resource).toString();
3243
+ const methods = [
3244
+ "- `identity_assertion`: present an audience-bound ID-JAG from a trusted provider.",
3245
+ ...registration.allowServiceAuth === true ? [
3246
+ "- `service_auth`: provide a user login hint and complete the service-owned claim ceremony."
3247
+ ] : [],
3248
+ ...registration.allowAnonymous === true ? [
3249
+ "- `anonymous`: receive pre-claim scopes, then optionally let a signed-in user claim the registration."
3250
+ ] : []
3251
+ ].join(`
3252
+ `);
3253
+ return `# Agent registration for ${config.resourceName ?? config.resource}
3254
+
3255
+ This service supports the open auth.md agent-registration profile. Structured
3256
+ OAuth metadata is authoritative; this document is its agent-readable companion.
3257
+
3258
+ ## 1. Discover
3259
+
3260
+ Fetch ${metadataUrl}, follow \`authorization_servers\`, then fetch
3261
+ \`/.well-known/oauth-authorization-server\`. Read its \`agent_auth\` object and
3262
+ top-level \`token_endpoint\` and \`grant_types_supported\` fields.
3263
+
3264
+ ## 2. Choose a method
3265
+
3266
+ ${methods}
3267
+
3268
+ Before asserting a user identity, show the service name and requested scopes to
3269
+ the user and obtain consent. Never ask the user to send a password or OTP to the
3270
+ agent.
3271
+
3272
+ ## 3. Register
3273
+
3274
+ POST one of these bodies to ${endpoints.identityEndpoint}:
3275
+
3276
+ ${markdownJson({
3277
+ assertion: "<ID-JAG>",
3278
+ assertion_type: AGENT_IDENTITY_ASSERTION_TYPE,
3279
+ type: "identity_assertion"
3280
+ })}
3281
+
3282
+ ${registration.allowServiceAuth === true ? markdownJson({ login_hint: "user@example.com", type: "service_auth" }) : ""}
3283
+
3284
+ ${registration.allowAnonymous === true ? markdownJson({ type: "anonymous" }) : ""}
3285
+
3286
+ ## 4. Claim
3287
+
3288
+ Surface \`verification_uri\` and \`user_code\` together. The user opens the
3289
+ service-owned page, signs in using the service's normal MFA/SSO policy, and types
3290
+ the code there. Poll ${endpoints.tokenEndpoint} using
3291
+ \`grant_type=${AGENT_CLAIM_GRANT_TYPE}\` and the one-time \`claim_token\`.
3292
+ Treat \`authorization_pending\` as retryable, honor \`interval\`, and restart
3293
+ when the server returns \`expired_token\`.
3294
+
3295
+ ## 5. Exchange and use credentials
3296
+
3297
+ Exchange \`identity_assertion\` at ${endpoints.tokenEndpoint} with
3298
+ \`grant_type=${AGENT_IDENTITY_ASSERTION_GRANT_TYPE}\`. Present the resulting
3299
+ access token as \`Authorization: Bearer <access_token>\`. Credentials are scoped,
3300
+ short-lived, revocable, and bound to the registered agent identity.
3301
+
3302
+ ## Errors and safety
3303
+
3304
+ - Stop on \`invalid_issuer\`, \`invalid_signature\`, \`invalid_audience\`, or \`replay_detected\`.
3305
+ - Reauthenticate the user on \`login_required\`.
3306
+ - Open the service-owned confirmation URL on \`interaction_required\`.
3307
+ - Never persist claim tokens after completion and never expose credentials in model context.
3308
+ `;
3309
+ };
3310
+ var requiredRegistration = (config) => {
3311
+ if (config.agentRegistration === undefined) {
3312
+ throw new Error("agentAuth.agentRegistration is not configured");
3313
+ }
3314
+ return config.agentRegistration;
3315
+ };
3316
+ var randomCode = () => {
3317
+ const bytes = crypto.getRandomValues(new Uint8Array(4));
3318
+ const value = new DataView(bytes.buffer).getUint32(0) % 1e6;
3319
+ return value.toString().padStart(6, "0");
3320
+ };
3321
+ var makeSecret = (prefix) => `${prefix}_${generateSecureToken(TOKEN_BYTES2)}`;
3322
+ var makeAttempt = async ({
3323
+ email,
3324
+ now,
3325
+ registration
3326
+ }) => {
3327
+ const attemptToken = makeSecret("cat");
3328
+ const userCode = randomCode();
3329
+ const expiresAt = now + (registration.attemptTtlMs ?? DEFAULT_ATTEMPT_TTL_MS);
3330
+ return {
3331
+ attempt: {
3332
+ attempts: 0,
3333
+ email: email.toLowerCase(),
3334
+ expiresAt,
3335
+ tokenHash: await hashToken(attemptToken),
3336
+ userCodeHash: await hashToken(userCode)
3337
+ },
3338
+ attemptToken,
3339
+ expiresAt,
3340
+ userCode
3341
+ };
3342
+ };
3343
+ var createFlow = async ({
3344
+ agentId,
3345
+ kind,
3346
+ loginHint,
3347
+ now,
3348
+ registration,
3349
+ status,
3350
+ upstream,
3351
+ userId,
3352
+ withAttempt
3353
+ }) => {
3354
+ const claimToken = makeSecret("clm");
3355
+ const registrationId = `air_${crypto.randomUUID()}`;
3356
+ const claimExpiresAt = now + (registration.claimTtlMs ?? DEFAULT_CLAIM_TTL_MS);
3357
+ const attempt = withAttempt === undefined ? undefined : await makeAttempt({ email: withAttempt, now, registration });
3358
+ const flow = {
3359
+ agentId,
3360
+ claimAttempt: attempt?.attempt,
3361
+ claimExpiresAt,
3362
+ claimTokenHash: await hashToken(claimToken),
3363
+ createdAt: now,
3364
+ expiresAt: claimExpiresAt,
3365
+ kind,
3366
+ loginHint,
3367
+ registrationId,
3368
+ status,
3369
+ updatedAt: now,
3370
+ upstream,
3371
+ userId,
3372
+ version: 1
3373
+ };
3374
+ if (!await registration.identityStore.create(flow)) {
3375
+ throw new Error("Agent identity registration id collision");
3376
+ }
3377
+ return { attempt, claimToken, flow };
3378
+ };
3379
+ var assertionClaims = (config, flow, now) => ({
3380
+ aud: config.authorizationServer,
3381
+ exp: Math.floor((now + (config.agentRegistration?.assertionTtlMs ?? DEFAULT_ASSERTION_TTL_MS)) / MILLISECONDS_IN_A_SECOND),
3382
+ iat: Math.floor(now / MILLISECONDS_IN_A_SECOND),
3383
+ iss: config.authorizationServer,
3384
+ jti: crypto.randomUUID(),
3385
+ registration_version: flow.version,
3386
+ sub: flow.registrationId
3387
+ });
3388
+ var issueAgentServiceAssertion = async (config, flow, now = Date.now()) => ({
3389
+ assertion: await signJwt(assertionClaims(config, flow, now), requiredRegistration(config).signingKey, "oauth-id-jag+jwt"),
3390
+ expiresAt: now + (requiredRegistration(config).assertionTtlMs ?? DEFAULT_ASSERTION_TTL_MS)
3391
+ });
3392
+ var activateAgent = async (config, flow, scopes) => {
3393
+ const now = Date.now();
3394
+ await config.registrationStore.saveRegistration({
3395
+ agentId: flow.agentId,
3396
+ allowedScopes: scopes.filter((scope) => config.scopes.includes(scope)),
3397
+ createdAt: now,
3398
+ metadata: {
3399
+ identityRegistrationId: flow.registrationId,
3400
+ registrationKind: flow.kind
3401
+ },
3402
+ name: `Agent registration ${flow.registrationId}`,
3403
+ status: "active",
3404
+ updatedAt: now
3405
+ });
3406
+ if (flow.userId !== undefined) {
3407
+ await config.delegationStore.saveDelegation({
3408
+ agentId: flow.agentId,
3409
+ createdAt: now,
3410
+ delegationId: `agd_${crypto.randomUUID()}`,
3411
+ scopes: scopes.filter((scope) => config.scopes.includes(scope)),
3412
+ status: "active",
3413
+ updatedAt: now,
3414
+ userId: flow.userId
3415
+ });
3416
+ }
3417
+ };
3418
+ var ceremony = (config, flow, attempt) => ({
3419
+ expires_in: Math.max(0, Math.floor((attempt.expiresAt - Date.now()) / MILLISECONDS_IN_A_SECOND)),
3420
+ interval: requiredRegistration(config).pollIntervalSeconds ?? DEFAULT_POLL_INTERVAL_SECONDS,
3421
+ user_code: attempt.userCode,
3422
+ verification_uri: `${agentRegistrationEndpoints(config).completeEndpoint}?claim_attempt_token=${encodeURIComponent(attempt.attemptToken)}`
3423
+ });
3424
+ var startAgentRegistration = async (config, input, now = Date.now()) => {
3425
+ const registration = requiredRegistration(config);
3426
+ const agentId = `agent_${crypto.randomUUID()}`;
3427
+ if (input.type === "anonymous") {
3428
+ if (registration.allowAnonymous !== true) {
3429
+ return { error: "anonymous_not_enabled", status: 403 };
3430
+ }
3431
+ if (registration.revokeAccessTokens === undefined) {
3432
+ throw new Error("Anonymous agent registration requires revokeAccessTokens");
3433
+ }
3434
+ const created2 = await createFlow({
3435
+ agentId,
3436
+ kind: "anonymous",
3437
+ now,
3438
+ registration,
3439
+ status: "pending"
3440
+ });
3441
+ await activateAgent(config, created2.flow, registration.preClaimScopes ?? []);
3442
+ const assertion2 = await issueAgentServiceAssertion(config, created2.flow, now);
3443
+ return {
3444
+ assertionExpires: assertion2.expiresAt,
3445
+ claimToken: created2.claimToken,
3446
+ claimTokenExpires: created2.flow.claimExpiresAt,
3447
+ identityAssertion: assertion2.assertion,
3448
+ postClaimScopes: registration.postClaimScopes,
3449
+ preClaimScopes: registration.preClaimScopes ?? [],
3450
+ registrationId: created2.flow.registrationId,
3451
+ registrationType: "anonymous",
3452
+ status: 200
3453
+ };
3454
+ }
3455
+ if (input.type === "service_auth") {
3456
+ if (registration.allowServiceAuth !== true) {
3457
+ return { error: "service_auth_not_enabled", status: 403 };
3458
+ }
3459
+ const email = input.loginHint.trim().toLowerCase();
3460
+ if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/u.test(email)) {
3461
+ return { error: "invalid_login_hint", status: 400 };
3462
+ }
3463
+ const created2 = await createFlow({
3464
+ agentId,
3465
+ kind: "service_auth",
3466
+ loginHint: email,
3467
+ now,
3468
+ registration,
3469
+ status: "pending",
3470
+ withAttempt: email
3471
+ });
3472
+ if (created2.attempt === undefined)
3473
+ throw new Error("Claim attempt missing");
3474
+ return {
3475
+ assertionExpires: 0,
3476
+ claim: ceremony(config, created2.flow, created2.attempt),
3477
+ claimToken: created2.claimToken,
3478
+ claimTokenExpires: created2.flow.claimExpiresAt,
3479
+ postClaimScopes: registration.postClaimScopes,
3480
+ registrationId: created2.flow.registrationId,
3481
+ registrationType: "service_auth",
3482
+ status: 200
3483
+ };
3484
+ }
3485
+ if (input.assertionType !== AGENT_IDENTITY_ASSERTION_TYPE || registration.verifyIdentityAssertion === undefined) {
3486
+ return { error: "invalid_request", status: 400 };
3487
+ }
3488
+ const identity = await registration.verifyIdentityAssertion(input.assertion);
3489
+ if (identity === undefined) {
3490
+ return { error: "invalid_identity_assertion", status: 401 };
3491
+ }
3492
+ const authAge = now - identity.authenticatedAt;
3493
+ if (authAge < -MILLISECONDS_IN_A_MINUTE || authAge > (registration.maxAuthenticationAgeMs ?? DEFAULT_MAX_AUTH_AGE_MS)) {
3494
+ return { error: "login_required", status: 401 };
3495
+ }
3496
+ if (identity.emailVerified !== true && identity.phoneNumberVerified !== true) {
3497
+ return { error: "missing_verified_identity", status: 403 };
3498
+ }
3499
+ const existing = await registration.identityStore.findByUpstreamIdentity({
3500
+ clientId: identity.clientId,
3501
+ issuer: identity.issuer,
3502
+ subject: identity.subject
3503
+ });
3504
+ if (existing?.status === "claimed") {
3505
+ const assertion2 = await issueAgentServiceAssertion(config, existing, now);
3506
+ return {
3507
+ assertionExpires: assertion2.expiresAt,
3508
+ identityAssertion: assertion2.assertion,
3509
+ postClaimScopes: registration.postClaimScopes,
3510
+ registrationId: existing.registrationId,
3511
+ registrationType: "identity_assertion",
3512
+ status: 200
3513
+ };
3514
+ }
3515
+ const match = await registration.resolveVerifiedIdentity?.(identity);
3516
+ const verifiedEmail = identity.emailVerified === true ? identity.email : undefined;
3517
+ if (match?.userId === undefined && verifiedEmail === undefined) {
3518
+ return { error: "interaction_required", status: 401 };
3519
+ }
3520
+ const created = await createFlow({
3521
+ agentId,
3522
+ kind: "identity_assertion",
3523
+ now,
3524
+ registration,
3525
+ status: match?.userId === undefined ? "pending" : "claimed",
3526
+ upstream: {
3527
+ clientId: identity.clientId,
3528
+ issuer: identity.issuer,
3529
+ subject: identity.subject
3530
+ },
3531
+ userId: match?.userId,
3532
+ withAttempt: match?.userId === undefined ? verifiedEmail : undefined
3533
+ });
3534
+ if (match?.userId === undefined) {
3535
+ if (created.attempt === undefined)
3536
+ throw new Error("Claim attempt missing");
3537
+ return {
3538
+ assertionExpires: 0,
3539
+ claim: ceremony(config, created.flow, created.attempt),
3540
+ claimToken: created.claimToken,
3541
+ claimTokenExpires: created.flow.claimExpiresAt,
3542
+ postClaimScopes: registration.postClaimScopes,
3543
+ registrationId: created.flow.registrationId,
3544
+ registrationType: "identity_assertion",
3545
+ status: 200
3546
+ };
3547
+ }
3548
+ await activateAgent(config, created.flow, registration.postClaimScopes);
3549
+ const assertion = await issueAgentServiceAssertion(config, created.flow, now);
3550
+ return {
3551
+ assertionExpires: assertion.expiresAt,
3552
+ identityAssertion: assertion.assertion,
3553
+ postClaimScopes: registration.postClaimScopes,
3554
+ registrationId: created.flow.registrationId,
3555
+ registrationType: "identity_assertion",
3556
+ status: 200
3557
+ };
3558
+ };
3559
+ var beginAgentClaim = async (config, input, now = Date.now()) => {
3560
+ const registration = requiredRegistration(config);
3561
+ const email = input.email.trim().toLowerCase();
3562
+ const claimTokenHash = await hashToken(input.claimToken);
3563
+ for (let retry = 0;retry < MAX_CONCURRENT_UPDATE_RETRIES; retry += 1) {
3564
+ const flow = await registration.identityStore.findByClaimTokenHash(claimTokenHash);
3565
+ if (flow === undefined) {
3566
+ return { error: "invalid_claim_token", status: 400 };
3567
+ }
3568
+ if (flow.claimExpiresAt <= now || flow.status === "revoked") {
3569
+ return { error: "claim_expired", status: 400 };
3570
+ }
3571
+ if (flow.loginHint !== undefined && flow.loginHint !== email) {
3572
+ return { error: "invalid_claim_token", status: 400 };
3573
+ }
3574
+ const attempt = await makeAttempt({ email, now, registration });
3575
+ const replacement = {
3576
+ ...flow,
3577
+ claimAttempt: attempt.attempt,
3578
+ loginHint: email,
3579
+ updatedAt: now
3580
+ };
3581
+ if (await registration.identityStore.replace(replacement, flow.version)) {
3582
+ return {
3583
+ claimAttempt: ceremony(config, replacement, attempt),
3584
+ status: 200
3585
+ };
3586
+ }
3587
+ }
3588
+ return { error: "concurrent_update", status: 409 };
3589
+ };
3590
+ var completeAgentClaim = async (config, input, now = Date.now()) => {
3591
+ const registration = requiredRegistration(config);
3592
+ const user = await registration.resolveAuthenticatedUser(input.request);
3593
+ if (user === undefined)
3594
+ return { error: "wrong_user", status: 403 };
3595
+ const attemptTokenHash = await hashToken(input.attemptToken);
3596
+ const userCodeHash = await hashToken(input.userCode);
3597
+ for (let retry = 0;retry < MAX_CONCURRENT_UPDATE_RETRIES; retry += 1) {
3598
+ const flow = await registration.identityStore.findByAttemptTokenHash(attemptTokenHash);
3599
+ const attempt = flow?.claimAttempt;
3600
+ if (flow === undefined || attempt === undefined) {
3601
+ return { error: "invalid_claim_attempt", status: 400 };
3602
+ }
3603
+ if (flow.claimExpiresAt <= now || attempt.expiresAt <= now) {
3604
+ return { error: "claim_expired", status: 400 };
3605
+ }
3606
+ if (user.email?.toLowerCase() !== attempt.email) {
3607
+ return { error: "wrong_user", status: 403 };
3608
+ }
3609
+ const matches = await constantTimeEqual(userCodeHash, attempt.userCodeHash);
3610
+ if (!matches) {
3611
+ const attempts = attempt.attempts + 1;
3612
+ const locked = attempts >= (registration.maxCodeAttempts ?? DEFAULT_MAX_CODE_ATTEMPTS);
3613
+ const replaced = await registration.identityStore.replace({
3614
+ ...flow,
3615
+ claimAttempt: locked ? undefined : { ...attempt, attempts },
3616
+ updatedAt: now
3617
+ }, flow.version);
3618
+ if (replaced) {
3619
+ return {
3620
+ error: "user_code_invalid",
3621
+ status: locked ? 429 : 400
3622
+ };
3623
+ }
3624
+ continue;
3625
+ }
3626
+ if (flow.kind === "anonymous") {
3627
+ await registration.revokeAccessTokens?.(flow.agentId);
3628
+ }
3629
+ const replacement = {
3630
+ ...flow,
3631
+ claimAttempt: undefined,
3632
+ status: "claimed",
3633
+ updatedAt: now,
3634
+ userId: user.userId
3635
+ };
3636
+ if (!await registration.identityStore.replace(replacement, flow.version)) {
3637
+ continue;
3638
+ }
3639
+ const persisted = await registration.identityStore.findByRegistrationId(flow.registrationId);
3640
+ if (persisted === undefined) {
3641
+ throw new Error("Completed agent registration disappeared");
3642
+ }
3643
+ await activateAgent(config, persisted, registration.postClaimScopes);
3644
+ return { status: 204 };
3645
+ }
3646
+ return { error: "concurrent_update", status: 409 };
3647
+ };
3648
+ var issueAgentAccessToken = async (config, flow, now) => {
3649
+ const registration = requiredRegistration(config);
3650
+ const accessToken = `at_${generateSecureToken(TOKEN_BYTES2)}`;
3651
+ const scopes = (flow.status === "claimed" ? registration.postClaimScopes : registration.preClaimScopes ?? []).filter((scope) => config.scopes.includes(scope));
3652
+ const expiresAt = now + (registration.tokenTtlMs ?? DEFAULT_ACCESS_TOKEN_TTL_MS2);
3653
+ await registration.accessTokenStore.saveToken({
3654
+ clientId: flow.agentId,
3655
+ createdAt: now,
3656
+ expiresAt,
3657
+ hashedToken: await hashToken(accessToken),
3658
+ ownerId: flow.userId,
3659
+ scopes,
3660
+ tokenId: crypto.randomUUID()
3661
+ });
3662
+ return {
3663
+ accessToken,
3664
+ expiresIn: Math.floor((expiresAt - now) / MILLISECONDS_IN_A_SECOND),
3665
+ scopes
3666
+ };
3667
+ };
3668
+ var createAgentRegistrationCredentialVerifier = (accessTokenStore, identityStore) => async (request) => {
3669
+ const authorization = request.headers.get("authorization");
3670
+ if (authorization?.startsWith("Bearer at_") !== true)
3671
+ return;
3672
+ const token = authorization.slice("Bearer ".length).trim();
3673
+ const record = await accessTokenStore.findByHashedToken(await hashToken(token));
3674
+ if (record === undefined || record.expiresAt <= Date.now())
3675
+ return;
3676
+ if (identityStore !== undefined) {
3677
+ const identity = await identityStore.findByAgentId(record.clientId);
3678
+ if (identity === undefined || identity.status === "revoked") {
3679
+ return;
3680
+ }
3681
+ }
3682
+ return {
3683
+ agentId: record.clientId,
3684
+ expiresAt: record.expiresAt,
3685
+ scopes: record.scopes,
3686
+ userId: record.ownerId
3687
+ };
3688
+ };
3689
+ var handleAgentTokenGrant = async (config, body, now = Date.now()) => {
3690
+ const registration = requiredRegistration(config);
3691
+ if (body.grant_type === AGENT_CLAIM_GRANT_TYPE) {
3692
+ if (body.claim_token === undefined) {
3693
+ return { body: { error: "invalid_request" }, status: 400 };
3694
+ }
3695
+ const claimTokenHash = await hashToken(body.claim_token);
3696
+ let flow2;
3697
+ for (let retry = 0;retry < MAX_CONCURRENT_UPDATE_RETRIES; retry += 1) {
3698
+ flow2 = await registration.identityStore.findByClaimTokenHash(claimTokenHash);
3699
+ if (flow2 === undefined || flow2.claimExpiresAt <= now) {
3700
+ return { body: { error: "expired_token" }, status: 400 };
3701
+ }
3702
+ if (flow2.status === "claimed")
3703
+ break;
3704
+ if (flow2.claimAttempt !== undefined && flow2.claimAttempt.expiresAt <= now) {
3705
+ return { body: { error: "expired_token" }, status: 400 };
3706
+ }
3707
+ const intervalMs = (registration.pollIntervalSeconds ?? DEFAULT_POLL_INTERVAL_SECONDS) * MILLISECONDS_IN_A_SECOND;
3708
+ if (flow2.lastPolledAt !== undefined && now - flow2.lastPolledAt < intervalMs) {
3709
+ return { body: { error: "slow_down" }, status: 400 };
3710
+ }
3711
+ if (await registration.identityStore.replace({ ...flow2, lastPolledAt: now, updatedAt: now }, flow2.version)) {
3712
+ return {
3713
+ body: { error: "authorization_pending" },
3714
+ status: 400
3715
+ };
3716
+ }
3717
+ }
3718
+ if (flow2?.status !== "claimed") {
3719
+ return { body: { error: "temporarily_unavailable" }, status: 400 };
3720
+ }
3721
+ const assertion = await issueAgentServiceAssertion(config, flow2, now);
3722
+ const token2 = await issueAgentAccessToken(config, flow2, now);
3723
+ return {
3724
+ body: {
3725
+ access_token: token2.accessToken,
3726
+ assertion_expires: new Date(assertion.expiresAt).toISOString(),
3727
+ expires_in: token2.expiresIn,
3728
+ identity_assertion: assertion.assertion,
3729
+ scope: token2.scopes.join(" "),
3730
+ token_type: "Bearer"
3731
+ },
3732
+ status: 200
3733
+ };
3734
+ }
3735
+ if (body.grant_type !== AGENT_IDENTITY_ASSERTION_GRANT_TYPE) {
3736
+ return;
3737
+ }
3738
+ if (body.assertion === undefined) {
3739
+ return { body: { error: "invalid_request" }, status: 400 };
3740
+ }
3741
+ const verified = await verifyJwt(body.assertion, registration.signingKey.publicJwk);
3742
+ const payload = verified?.payload;
3743
+ if (verified?.header?.typ !== "oauth-id-jag+jwt" || payload?.iss !== config.authorizationServer || payload.aud !== config.authorizationServer || typeof payload.sub !== "string" || typeof payload.exp !== "number" || payload.exp <= Math.floor(now / MILLISECONDS_IN_A_SECOND)) {
3744
+ return { body: { error: "invalid_grant" }, status: 400 };
3745
+ }
3746
+ const flow = await registration.identityStore.findByRegistrationId(payload.sub);
3747
+ if (flow === undefined || flow.status === "revoked" || payload.registration_version !== flow.version) {
3748
+ return { body: { error: "invalid_grant" }, status: 400 };
3749
+ }
3750
+ const token = await issueAgentAccessToken(config, flow, now);
3751
+ return {
3752
+ body: {
3753
+ access_token: token.accessToken,
3754
+ expires_in: token.expiresIn,
3755
+ scope: token.scopes.join(" "),
3756
+ token_type: "Bearer"
3757
+ },
3758
+ status: 200
3759
+ };
3760
+ };
3761
+ var revokeAgentIdentityRegistration = async (config, registrationId, now = Date.now()) => {
3762
+ const registration = requiredRegistration(config);
3763
+ for (let retry = 0;retry < MAX_CONCURRENT_UPDATE_RETRIES; retry += 1) {
3764
+ const flow = await registration.identityStore.findByRegistrationId(registrationId);
3765
+ if (flow === undefined)
3766
+ return false;
3767
+ if (flow.status === "revoked")
3768
+ return true;
3769
+ await registration.revokeAccessTokens?.(flow.agentId);
3770
+ if (await registration.identityStore.replace({
3771
+ ...flow,
3772
+ claimAttempt: undefined,
3773
+ status: "revoked",
3774
+ updatedAt: now
3775
+ }, flow.version)) {
3776
+ return true;
3777
+ }
3778
+ }
3779
+ throw new Error("Could not revoke agent identity after concurrent updates");
3780
+ };
3781
+
3115
3782
  // src/agents/routes.ts
3783
+ var DELETE_CODE_POINT = 127;
3784
+ var HTTP_BAD_REQUEST2 = 400;
3785
+ var HTTP_FORBIDDEN = 403;
3786
+ var HTTP_OK2 = 200;
3787
+ var HTTP_UNAUTHORIZED2 = 401;
3788
+ var MINIMUM_PRINTABLE_CODE_POINT = 32;
3116
3789
  var quoteHeaderValue = (value) => {
3117
3790
  const printable = [...value].filter((character) => {
3118
3791
  const codePoint = character.codePointAt(0) ?? 0;
3119
- return codePoint >= 32 && codePoint !== 127;
3792
+ return codePoint >= MINIMUM_PRINTABLE_CODE_POINT && codePoint !== DELETE_CODE_POINT;
3120
3793
  }).join("");
3121
3794
  return `"${printable.replace(/[\\"]/g, "\\$&")}"`;
3122
3795
  };
@@ -3149,39 +3822,170 @@ var failureResponse = (config, failure, requiredScopes) => new Response(JSON.str
3149
3822
  requiredScopes
3150
3823
  })
3151
3824
  },
3152
- status: failure.code === "Forbidden" ? 403 : 401
3825
+ status: failure.code === "Forbidden" ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED2
3153
3826
  });
3154
- var agentAuthPlugin = (config) => {
3155
- const plugin = new Elysia2().derive(({ request }) => ({
3156
- protectAgent: async (requiredScopes, handleAuth, handleAuthFail) => {
3157
- if (config === undefined) {
3158
- const failure = {
3159
- code: "Unauthorized",
3160
- message: "Agent is not authenticated"
3161
- };
3162
- return await handleAuthFail?.(failure) ?? new Response(failure.message, { status: 401 });
3163
- }
3164
- const principal = await resolveAgentPrincipal(request, config);
3165
- if (principal === undefined) {
3166
- const failure = {
3167
- code: "Unauthorized",
3168
- message: "Agent is not authenticated"
3169
- };
3170
- return await handleAuthFail?.(failure) ?? failureResponse(config, failure, requiredScopes);
3171
- }
3172
- if (!agentHasScopes(principal, requiredScopes)) {
3173
- const failure = {
3174
- code: "Forbidden",
3175
- message: "Insufficient agent scopes"
3176
- };
3177
- return await handleAuthFail?.(failure) ?? failureResponse(config, failure, requiredScopes);
3178
- }
3179
- return handleAuth(principal);
3827
+ var json = (value, status = HTTP_OK2) => new Response(JSON.stringify(value), {
3828
+ headers: {
3829
+ "cache-control": "no-store",
3830
+ "content-type": "application/json"
3831
+ },
3832
+ status
3833
+ });
3834
+ var recordBody = (body) => {
3835
+ if (typeof body !== "object" || body === null || Array.isArray(body)) {
3836
+ return;
3837
+ }
3838
+ return Object.fromEntries(Object.entries(body));
3839
+ };
3840
+ var registrationResponse = (result) => {
3841
+ const body = {
3842
+ ...result.assertionExpires > 0 ? {
3843
+ assertion_expires: new Date(result.assertionExpires).toISOString()
3844
+ } : {},
3845
+ ...result.claim === undefined ? {} : { claim: result.claim },
3846
+ ...result.claimToken === undefined ? {} : { claim_token: result.claimToken },
3847
+ ...result.claimTokenExpires === undefined ? {} : {
3848
+ claim_token_expires: new Date(result.claimTokenExpires).toISOString()
3849
+ },
3850
+ ...result.identityAssertion === undefined ? {} : { identity_assertion: result.identityAssertion },
3851
+ ...result.preClaimScopes === undefined ? {} : { pre_claim_scopes: result.preClaimScopes },
3852
+ post_claim_scopes: result.postClaimScopes,
3853
+ registration_id: result.registrationId,
3854
+ registration_type: result.registrationType
3855
+ };
3856
+ if (result.registrationType === "identity_assertion" && result.identityAssertion === undefined) {
3857
+ return json({
3858
+ ...body,
3859
+ error: "interaction_required",
3860
+ error_description: "Authenticate at the service and confirm the account link."
3861
+ }, HTTP_UNAUTHORIZED2);
3862
+ }
3863
+ return json(body);
3864
+ };
3865
+ var parseRegistrationInput = (value) => {
3866
+ if (value.type === "anonymous") {
3867
+ const input2 = { type: "anonymous" };
3868
+ return input2;
3869
+ }
3870
+ if (value.type === "service_auth" && typeof value.login_hint === "string") {
3871
+ const input2 = {
3872
+ loginHint: value.login_hint,
3873
+ type: "service_auth"
3874
+ };
3875
+ return input2;
3876
+ }
3877
+ if (value.type !== "identity_assertion" || value.assertion_type !== AGENT_IDENTITY_ASSERTION_TYPE || typeof value.assertion !== "string") {
3878
+ return;
3879
+ }
3880
+ const input = {
3881
+ assertion: value.assertion,
3882
+ assertionType: AGENT_IDENTITY_ASSERTION_TYPE,
3883
+ type: "identity_assertion"
3884
+ };
3885
+ return input;
3886
+ };
3887
+ var escapeHtml = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
3888
+ var agentAuthContextPlugin = (config) => new Elysia2().derive(({ request }) => ({
3889
+ protectAgent: async (requiredScopes, handleAuth, handleAuthFail) => {
3890
+ if (config === undefined) {
3891
+ const failure = {
3892
+ code: "Unauthorized",
3893
+ message: "Agent is not authenticated"
3894
+ };
3895
+ return await handleAuthFail?.(failure) ?? new Response(failure.message, { status: 401 });
3180
3896
  }
3181
- }));
3897
+ const principal = await resolveAgentPrincipal(request, config);
3898
+ if (principal === undefined) {
3899
+ const failure = {
3900
+ code: "Unauthorized",
3901
+ message: "Agent is not authenticated"
3902
+ };
3903
+ return await handleAuthFail?.(failure) ?? failureResponse(config, failure, requiredScopes);
3904
+ }
3905
+ if (!agentHasScopes(principal, requiredScopes)) {
3906
+ const failure = {
3907
+ code: "Forbidden",
3908
+ message: "Insufficient agent scopes"
3909
+ };
3910
+ return await handleAuthFail?.(failure) ?? failureResponse(config, failure, requiredScopes);
3911
+ }
3912
+ return handleAuth(principal);
3913
+ }
3914
+ }));
3915
+ var agentAuthPlugin = (config) => {
3916
+ const plugin = agentAuthContextPlugin(config);
3182
3917
  if (config === undefined)
3183
3918
  return plugin.as("global");
3184
- return plugin.get(config.metadataRoute ?? DEFAULT_AGENT_RESOURCE_METADATA_ROUTE, () => agentProtectedResourceMetadata(config)).as("global");
3919
+ if (config.agentRegistration === undefined) {
3920
+ return plugin.get(config.metadataRoute ?? DEFAULT_AGENT_RESOURCE_METADATA_ROUTE, () => agentProtectedResourceMetadata(config)).as("global");
3921
+ }
3922
+ const registration = config.agentRegistration;
3923
+ const identityRoute = registration.identityRoute ?? "/agent/identity";
3924
+ const claimRoute = registration.claimRoute ?? "/agent/identity/claim";
3925
+ const completeRoute = registration.completeRoute ?? "/agent/identity/claim/complete";
3926
+ const guideRoute = registration.guideRoute ?? "/auth.md";
3927
+ return plugin.get(config.metadataRoute ?? DEFAULT_AGENT_RESOURCE_METADATA_ROUTE, () => agentProtectedResourceMetadata(config)).get(guideRoute, () => new Response(generateAgentRegistrationGuide(config), {
3928
+ headers: {
3929
+ "cache-control": "public, max-age=300",
3930
+ "content-type": "text/markdown; charset=utf-8"
3931
+ }
3932
+ })).get(completeRoute, ({ query }) => {
3933
+ const token = typeof query.claim_attempt_token === "string" ? query.claim_attempt_token : "";
3934
+ if (token.length === 0)
3935
+ return new Response("Invalid claim link", { status: 400 });
3936
+ return new Response(`<!doctype html><html><head><meta charset="utf-8"><meta name="robots" content="noindex"><title>Confirm agent registration</title></head><body><main><h1>Confirm agent registration</h1><p>Sign in to this service, verify the agent and scopes shown by your agent, then enter the six-digit code.</p><form method="post"><input type="hidden" name="claim_attempt_token" value="${escapeHtml(token)}"><label>Code <input name="user_code" inputmode="numeric" autocomplete="one-time-code" pattern="[0-9]{6}" required></label><button type="submit">Confirm</button></form></main></body></html>`, {
3937
+ headers: {
3938
+ "cache-control": "no-store",
3939
+ "content-security-policy": "default-src 'none'; form-action 'self'; style-src 'none'; base-uri 'none'; frame-ancestors 'none'",
3940
+ "content-type": "text/html; charset=utf-8",
3941
+ "x-content-type-options": "nosniff"
3942
+ }
3943
+ });
3944
+ }).post(identityRoute, async ({ body }) => {
3945
+ const value = recordBody(body);
3946
+ if (value === undefined || typeof value.type !== "string") {
3947
+ return json({ error: "invalid_request" }, HTTP_BAD_REQUEST2);
3948
+ }
3949
+ const input = parseRegistrationInput(value);
3950
+ if (input === undefined)
3951
+ return json({ error: "invalid_request" }, HTTP_BAD_REQUEST2);
3952
+ const result = await startAgentRegistration(config, input);
3953
+ if ("error" in result) {
3954
+ return json({
3955
+ error: result.error,
3956
+ ...result.message === undefined ? {} : { error_description: result.message }
3957
+ }, result.status);
3958
+ }
3959
+ return registrationResponse(result);
3960
+ }).post(claimRoute, async ({ body }) => {
3961
+ const value = recordBody(body);
3962
+ if (value === undefined || typeof value.claim_token !== "string" || typeof value.email !== "string") {
3963
+ return json({ error: "invalid_request" }, HTTP_BAD_REQUEST2);
3964
+ }
3965
+ const result = await beginAgentClaim(config, {
3966
+ claimToken: value.claim_token,
3967
+ email: value.email
3968
+ });
3969
+ if ("error" in result)
3970
+ return json({ error: result.error }, result.status);
3971
+ return json({ claim_attempt: result.claimAttempt });
3972
+ }).post(completeRoute, async ({ body, request }) => {
3973
+ let value = recordBody(body);
3974
+ if (value === undefined && typeof body === "string") {
3975
+ value = Object.fromEntries(new URLSearchParams(body));
3976
+ }
3977
+ if (value === undefined || typeof value.claim_attempt_token !== "string" || typeof value.user_code !== "string") {
3978
+ return json({ error: "invalid_request" }, HTTP_BAD_REQUEST2);
3979
+ }
3980
+ const result = await completeAgentClaim(config, {
3981
+ attemptToken: value.claim_attempt_token,
3982
+ request,
3983
+ userCode: value.user_code
3984
+ });
3985
+ if ("error" in result)
3986
+ return json({ error: result.error }, result.status);
3987
+ return new Response(null, { status: 204 });
3988
+ }).as("global");
3185
3989
  };
3186
3990
 
3187
3991
  // src/audit/config.ts
@@ -3484,6 +4288,11 @@ import { Elysia as Elysia5, t as t4 } from "elysia";
3484
4288
 
3485
4289
  // src/utils.ts
3486
4290
  init_constants();
4291
+ var MILLISECONDS_IN_A_SECOND2 = 1000;
4292
+ var readOptionalString = (value, property) => {
4293
+ const candidate = Reflect.get(value, property);
4294
+ return typeof candidate === "string" ? candidate : undefined;
4295
+ };
3487
4296
  var defineAuthConfig = (configuration) => configuration;
3488
4297
  var defineAuthHtmxConfig = (htmxConfig) => htmxConfig;
3489
4298
  var defineAuthSettings = (settings) => settings;
@@ -3579,14 +4388,10 @@ var resolveOAuthAuthorization = async ({
3579
4388
  let userIdentity;
3580
4389
  let accessToken = tokenResponse.access_token;
3581
4390
  let refreshToken = tokenResponse.refresh_token;
3582
- if (authProvider === "withings" && !accessToken) {
3583
- const body = Reflect.get(tokenResponse, "body");
3584
- if (body && typeof body === "object") {
3585
- const nestedAccessToken = Reflect.get(body, "access_token");
3586
- if (typeof nestedAccessToken === "string") {
3587
- accessToken = nestedAccessToken;
3588
- }
3589
- }
4391
+ const withingsBody = Reflect.get(tokenResponse, "body");
4392
+ const withingsAccessToken = authProvider === "withings" && withingsBody !== null && typeof withingsBody === "object" ? readOptionalString(withingsBody, "access_token") : undefined;
4393
+ if (!accessToken && withingsAccessToken !== undefined) {
4394
+ accessToken = withingsAccessToken;
3590
4395
  }
3591
4396
  if (typeof accessToken !== "string" || accessToken.length === 0) {
3592
4397
  throw new Error("OAuth authorization response contains no access_token");
@@ -3604,9 +4409,12 @@ var resolveOAuthAuthorization = async ({
3604
4409
  source: "tokenResponse"
3605
4410
  });
3606
4411
  } else if (authProvider === "withings") {
3607
- userIdentity = { userid: tokenResponse.body.userid };
3608
- accessToken = tokenResponse.body.access_token;
3609
- refreshToken = tokenResponse.body.refresh_token;
4412
+ if (withingsBody === null || typeof withingsBody !== "object") {
4413
+ throw new Error("Withings OAuth response contains no body");
4414
+ }
4415
+ userIdentity = { userid: Reflect.get(withingsBody, "userid") };
4416
+ accessToken = readOptionalString(withingsBody, "access_token") ?? accessToken;
4417
+ refreshToken = readOptionalString(withingsBody, "refresh_token") ?? refreshToken;
3610
4418
  } else {
3611
4419
  userIdentity = normalizeProviderIdentity({
3612
4420
  identity: await providerInstance.fetchUserProfile(accessToken),
@@ -3638,7 +4446,7 @@ var resolveOAuthTokenExpiresAt = (tokenResponse, now = Date.now()) => {
3638
4446
  if (!Number.isFinite(expiresInSeconds) || expiresInSeconds <= 0) {
3639
4447
  return;
3640
4448
  }
3641
- return now + expiresInSeconds * 1000;
4449
+ return now + expiresInSeconds * MILLISECONDS_IN_A_SECOND2;
3642
4450
  };
3643
4451
  var validateSession = ({
3644
4452
  user_session_id,
@@ -4381,7 +5189,7 @@ var protectRoutePlugin = ({
4381
5189
  })).as("global");
4382
5190
 
4383
5191
  // src/htmx/renderers.ts
4384
- var escapeHtml = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
5192
+ var escapeHtml2 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
4385
5193
  var defaultAuthorizationHref = (provider, client) => client ? `/oauth2/${provider}/authorization?client=${client}` : `/oauth2/${provider}/authorization`;
4386
5194
  var resolveAuthHtmxRenderers = (config) => {
4387
5195
  const { connectorTargets, featuredLoginProviders, providerData } = config;
@@ -4394,10 +5202,10 @@ var resolveAuthHtmxRenderers = (config) => {
4394
5202
  return `<div class="grid-2">
4395
5203
  <div class="card"><h2 class="card__title">Canonical account</h2><p class="muted">Absolute Auth keeps one canonical user and links every OAuth identity to it. Conflicting identities raise a merge request.</p></div>
4396
5204
  <div class="card text-left"><h2 class="card__title">Profile fields</h2>
4397
- <div class="spread"><span class="muted">Subject</span><span>${escapeHtml(user.sub)}</span></div>
4398
- <div class="spread"><span class="muted">Name</span><span>${escapeHtml(fullName || "\u2014")}</span></div>
4399
- <div class="spread"><span class="muted">Email</span><span>${escapeHtml(user.email ?? "\u2014")}</span></div>
4400
- <div class="spread"><span class="muted">Primary identity</span><span>${escapeHtml(user.primary_auth_identity_id ?? "\u2014")}</span></div>
5205
+ <div class="spread"><span class="muted">Subject</span><span>${escapeHtml2(user.sub)}</span></div>
5206
+ <div class="spread"><span class="muted">Name</span><span>${escapeHtml2(fullName || "\u2014")}</span></div>
5207
+ <div class="spread"><span class="muted">Email</span><span>${escapeHtml2(user.email ?? "\u2014")}</span></div>
5208
+ <div class="spread"><span class="muted">Primary identity</span><span>${escapeHtml2(user.primary_auth_identity_id ?? "\u2014")}</span></div>
4401
5209
  </div>
4402
5210
  </div>`;
4403
5211
  };
@@ -4406,18 +5214,18 @@ var resolveAuthHtmxRenderers = (config) => {
4406
5214
  return `<a class="btn btn--primary btn--sm" href="/htmx">Sign in</a>`;
4407
5215
  }
4408
5216
  const label = user.email ?? user.first_name ?? "Account";
4409
- return `<span class="muted">${escapeHtml(label)}</span><a class="btn btn--ghost btn--sm" href="/htmx/signout">Sign out</a>`;
5217
+ return `<span class="muted">${escapeHtml2(label)}</span><a class="btn btn--ghost btn--sm" href="/htmx/signout">Sign out</a>`;
4410
5218
  };
4411
- const connectorLinks = () => connectorTargets.map((target) => `<div class="card text-left"><h2 class="card__title row"><img class="entity__logo" alt="" src="${providerLogo(target.provider)}" />${escapeHtml(target.label)}</h2><p class="muted">${escapeHtml(target.description)}</p><a class="btn btn--primary" href="${authorizationHref(target.provider, "connector")}">Link ${escapeHtml(target.label)}</a></div>`).join("");
5219
+ const connectorLinks = () => connectorTargets.map((target) => `<div class="card text-left"><h2 class="card__title row"><img class="entity__logo" alt="" src="${providerLogo(target.provider)}" />${escapeHtml2(target.label)}</h2><p class="muted">${escapeHtml2(target.description)}</p><a class="btn btn--primary" href="${authorizationHref(target.provider, "connector")}">Link ${escapeHtml2(target.label)}</a></div>`).join("");
4412
5220
  const connectors = (payload) => {
4413
- const scopeList = (scopes) => scopes.map((scope) => `<span class="scope">${escapeHtml(scope)}</span>`).join("");
4414
- const bindings = payload.bindings.length === 0 ? `<div class="empty-state">No external accounts linked.</div>` : `<div class="entity-list">${payload.bindings.map((binding) => `<div class="entity"><div class="entity__meta"><span class="entity__title">${escapeHtml(binding.label ?? binding.externalAccountId)}<span class="pill">${escapeHtml(binding.connectorProvider)}</span></span><span class="entity__sub">${escapeHtml(binding.externalAccountType)} \xB7 ${escapeHtml(binding.status)}</span><div class="scope-list">${scopeList(binding.availableScopes)}</div></div><div class="entity__actions"><form hx-delete="/htmx/connectors/bindings/${binding.id}" hx-target="#connector-list" hx-swap="innerHTML"><button class="btn btn--danger btn--sm" type="submit">Remove</button></form></div></div>`).join("")}</div>`;
4415
- const grants = payload.grants.length === 0 ? `<div class="empty-state">No connector grants yet.</div>` : `<div class="entity-list">${payload.grants.map((grant) => `<div class="entity"><div class="entity__meta"><span class="entity__title">${escapeHtml(grant.authProviderKey)}<span class="pill pill--indigo">${escapeHtml(grant.status)}</span></span><span class="entity__sub">Subject ${escapeHtml(grant.providerSubject)}</span><div class="scope-list">${scopeList(grant.grantedScopes)}</div></div><div class="entity__actions"><form hx-delete="/htmx/connectors/grants/${grant.id}" hx-target="#connector-list" hx-swap="innerHTML"><button class="btn btn--danger btn--sm" type="submit">Remove</button></form></div></div>`).join("")}</div>`;
5221
+ const scopeList = (scopes) => scopes.map((scope) => `<span class="scope">${escapeHtml2(scope)}</span>`).join("");
5222
+ const bindings = payload.bindings.length === 0 ? `<div class="empty-state">No external accounts linked.</div>` : `<div class="entity-list">${payload.bindings.map((binding) => `<div class="entity"><div class="entity__meta"><span class="entity__title">${escapeHtml2(binding.label ?? binding.externalAccountId)}<span class="pill">${escapeHtml2(binding.connectorProvider)}</span></span><span class="entity__sub">${escapeHtml2(binding.externalAccountType)} \xB7 ${escapeHtml2(binding.status)}</span><div class="scope-list">${scopeList(binding.availableScopes)}</div></div><div class="entity__actions"><form hx-delete="/htmx/connectors/bindings/${binding.id}" hx-target="#connector-list" hx-swap="innerHTML"><button class="btn btn--danger btn--sm" type="submit">Remove</button></form></div></div>`).join("")}</div>`;
5223
+ const grants = payload.grants.length === 0 ? `<div class="empty-state">No connector grants yet.</div>` : `<div class="entity-list">${payload.grants.map((grant) => `<div class="entity"><div class="entity__meta"><span class="entity__title">${escapeHtml2(grant.authProviderKey)}<span class="pill pill--indigo">${escapeHtml2(grant.status)}</span></span><span class="entity__sub">Subject ${escapeHtml2(grant.providerSubject)}</span><div class="scope-list">${scopeList(grant.grantedScopes)}</div></div><div class="entity__actions"><form hx-delete="/htmx/connectors/grants/${grant.id}" hx-target="#connector-list" hx-swap="innerHTML"><button class="btn btn--danger btn--sm" type="submit">Remove</button></form></div></div>`).join("")}</div>`;
4416
5224
  return `<h3 class="provider-heading">External accounts</h3>${bindings}<h3 class="provider-heading">Grants</h3>${grants}`;
4417
5225
  };
4418
5226
  const identities = (payload, query) => {
4419
5227
  const pending = payload.mergeRequests.filter((req) => req.status === "pending");
4420
- const mergesHtml = pending.length === 0 ? "" : `<div class="stack"><h3 class="provider-heading">Merge requests</h3><div class="entity-list">${pending.map((req) => `<div class="entity card--danger"><div class="entity__meta"><span class="entity__title">${escapeHtml(providerLabel(req.conflicting_auth_provider))} conflict</span><span class="entity__sub">Subject ${escapeHtml(req.conflicting_provider_subject)}</span></div><div class="entity__actions">
5228
+ const mergesHtml = pending.length === 0 ? "" : `<div class="stack"><h3 class="provider-heading">Merge requests</h3><div class="entity-list">${pending.map((req) => `<div class="entity card--danger"><div class="entity__meta"><span class="entity__title">${escapeHtml2(providerLabel(req.conflicting_auth_provider))} conflict</span><span class="entity__sub">Subject ${escapeHtml2(req.conflicting_provider_subject)}</span></div><div class="entity__actions">
4421
5229
  <form hx-post="/htmx/merge/${req.id}" hx-target="#identities-list" hx-swap="innerHTML" hx-include="#identity-query"><button class="btn btn--primary btn--sm" type="submit">Merge</button></form>
4422
5230
  <form hx-delete="/htmx/merge/${req.id}" hx-target="#identities-list" hx-swap="innerHTML" hx-include="#identity-query"><button class="btn btn--ghost btn--sm" type="submit">Dismiss</button></form>
4423
5231
  </div></div>`).join("")}</div></div>`;
@@ -4426,19 +5234,19 @@ var resolveAuthHtmxRenderers = (config) => {
4426
5234
  identities: list.filter((identity) => term === "" || providerLabel(provider).toLowerCase().includes(term) || identity.id.toLowerCase().includes(term) || identity.provider_subject.toLowerCase().includes(term)),
4427
5235
  provider
4428
5236
  })).filter((group) => group.identities.length > 0);
4429
- const groupsHtml = groups.length === 0 ? `<div class="empty-state">No identities match your search.</div>` : groups.map((group) => `<div class="provider-group"><h3 class="provider-heading">${providerLogo(group.provider) ? `<img class="entity__logo" alt="" src="${providerLogo(group.provider)}" />` : ""}${escapeHtml(providerLabel(group.provider))}</h3><div class="entity-list">${group.identities.map((identity) => `<div class="entity"><div class="entity__main"><div class="entity__meta"><span class="entity__title">${escapeHtml(identity.provider_subject)}${identity.isPrimary ? `<span class="pill pill--primary">Primary</span>` : ""}</span><span class="entity__sub">${escapeHtml(identity.id)}</span></div></div><div class="entity__actions">
5237
+ const groupsHtml = groups.length === 0 ? `<div class="empty-state">No identities match your search.</div>` : groups.map((group) => `<div class="provider-group"><h3 class="provider-heading">${providerLogo(group.provider) ? `<img class="entity__logo" alt="" src="${providerLogo(group.provider)}" />` : ""}${escapeHtml2(providerLabel(group.provider))}</h3><div class="entity-list">${group.identities.map((identity) => `<div class="entity"><div class="entity__main"><div class="entity__meta"><span class="entity__title">${escapeHtml2(identity.provider_subject)}${identity.isPrimary ? `<span class="pill pill--primary">Primary</span>` : ""}</span><span class="entity__sub">${escapeHtml2(identity.id)}</span></div></div><div class="entity__actions">
4430
5238
  ${identity.isPrimary ? "" : `<form hx-post="/htmx/identities/${identity.id}/primary" hx-target="#identities-list" hx-swap="innerHTML" hx-include="#identity-query"><button class="btn btn--neutral btn--sm" type="submit">Set primary</button></form>`}
4431
5239
  <form hx-delete="/htmx/identities/${identity.id}" hx-target="#identities-list" hx-swap="innerHTML" hx-include="#identity-query"><button class="btn btn--danger btn--sm" type="submit">Remove</button></form>
4432
5240
  </div></div>`).join("")}</div></div>`).join("");
4433
5241
  return `${mergesHtml}${groupsHtml}`;
4434
5242
  };
4435
- const protectedView = (user) => `<section class="auth-section stack"><div><h1 class="page-heading">Protected page</h1><p class="muted">Your authenticated session resolves to this user record.</p></div><pre class="json">${escapeHtml(JSON.stringify(user, null, 2))}</pre></section>`;
5243
+ const protectedView = (user) => `<section class="auth-section stack"><div><h1 class="page-heading">Protected page</h1><p class="muted">Your authenticated session resolves to this user record.</p></div><pre class="json">${escapeHtml2(JSON.stringify(user, null, 2))}</pre></section>`;
4436
5244
  const providerLogin = (verb, includeDropdown) => {
4437
- const featured = featuredLoginProviders.map((provider) => `<a class="oauth-button" href="${authorizationHref(provider)}"><img class="oauth-button__icon" alt="" src="${providerLogo(provider)}" /><span class="oauth-button__text">${escapeHtml(verb)} ${escapeHtml(providerLabel(provider))}</span></a>`).join("");
5245
+ const featured = featuredLoginProviders.map((provider) => `<a class="oauth-button" href="${authorizationHref(provider)}"><img class="oauth-button__icon" alt="" src="${providerLogo(provider)}" /><span class="oauth-button__text">${escapeHtml2(verb)} ${escapeHtml2(providerLabel(provider))}</span></a>`).join("");
4438
5246
  if (!includeDropdown) {
4439
5247
  return `<div class="oauth-grid">${featured}</div>`;
4440
5248
  }
4441
- const options = providerOptions.map((provider) => `<option value="${provider}">${escapeHtml(providerLabel(provider))}</option>`).join("");
5249
+ const options = providerOptions.map((provider) => `<option value="${provider}">${escapeHtml2(providerLabel(provider))}</option>`).join("");
4442
5250
  return `<div class="oauth-grid">${featured}
4443
5251
  <div class="separator"><span class="separator__line"></span><span class="separator__text">or any provider</span><span class="separator__line"></span></div>
4444
5252
  <form class="oauth-grid" action="/htmx/login-redirect" method="get">
@@ -4454,7 +5262,7 @@ var resolveAuthHtmxRenderers = (config) => {
4454
5262
  authMenu: overrides.authMenu ?? authMenu,
4455
5263
  connectorLinks: overrides.connectorLinks ?? connectorLinks,
4456
5264
  connectors: overrides.connectors ?? connectors,
4457
- escapeHtml,
5265
+ escapeHtml: escapeHtml2,
4458
5266
  identities: overrides.identities ?? identities,
4459
5267
  protected: overrides.protected ?? protectedView,
4460
5268
  providerLogin: overrides.providerLogin ?? providerLogin
@@ -5071,75 +5879,17 @@ import { Elysia as Elysia18, t as t14 } from "elysia";
5071
5879
  // src/oidc/config.ts
5072
5880
  init_constants();
5073
5881
  init_crypto();
5074
-
5075
- // src/oidc/keys.ts
5076
- var ENCODER = new TextEncoder;
5077
- var ES256 = { hash: "SHA-256", name: "ECDSA" };
5078
- var KEY_PARAMS = { name: "ECDSA", namedCurve: "P-256" };
5079
- var toBase64Url = (bytes) => Buffer.from(bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)).toString("base64url");
5080
- var fromBase64Url = (value) => new Uint8Array(Buffer.from(value, "base64url"));
5081
- var encodeSegment = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
5082
- var decodeSegment = (segment) => JSON.parse(Buffer.from(segment, "base64url").toString("utf8"));
5083
- var generateSigningKey = async () => {
5084
- const pair = await crypto.subtle.generateKey(KEY_PARAMS, true, [
5085
- "sign",
5086
- "verify"
5087
- ]);
5088
- const privateJwk = await crypto.subtle.exportKey("jwk", pair.privateKey);
5089
- const publicJwk = await crypto.subtle.exportKey("jwk", pair.publicKey);
5090
- return { kid: await jwkThumbprint(publicJwk), privateJwk, publicJwk };
5091
- };
5092
- var jwkThumbprint = async (jwk) => {
5093
- const canonical = JSON.stringify({
5094
- crv: jwk.crv,
5095
- kty: jwk.kty,
5096
- x: jwk.x,
5097
- y: jwk.y
5098
- });
5099
- return toBase64Url(await crypto.subtle.digest("SHA-256", ENCODER.encode(canonical)));
5100
- };
5101
- var signJwt = async (payload, signing) => {
5102
- const key = await crypto.subtle.importKey("jwk", signing.privateJwk, KEY_PARAMS, false, ["sign"]);
5103
- const input = `${encodeSegment({ alg: "ES256", kid: signing.kid, typ: "JWT" })}.${encodeSegment(payload)}`;
5104
- const signature = await crypto.subtle.sign(ES256, key, ENCODER.encode(input));
5105
- return `${input}.${toBase64Url(signature)}`;
5106
- };
5107
- var toPublicJwk = (key) => ({
5108
- alg: "ES256",
5109
- crv: key.publicJwk.crv,
5110
- kid: key.kid,
5111
- kty: key.publicJwk.kty,
5112
- use: "sig",
5113
- x: key.publicJwk.x,
5114
- y: key.publicJwk.y
5115
- });
5116
- var verifyJwt = async (token, publicJwk) => {
5117
- const [headerSegment, payloadSegment, signatureSegment] = token.split(".");
5118
- if (headerSegment === undefined || payloadSegment === undefined || signatureSegment === undefined) {
5119
- return;
5120
- }
5121
- const key = await crypto.subtle.importKey("jwk", publicJwk, KEY_PARAMS, false, ["verify"]);
5122
- const valid = await crypto.subtle.verify(ES256, key, fromBase64Url(signatureSegment), ENCODER.encode(`${headerSegment}.${payloadSegment}`));
5123
- if (!valid)
5124
- return;
5125
- return {
5126
- header: decodeSegment(headerSegment),
5127
- payload: decodeSegment(payloadSegment)
5128
- };
5129
- };
5130
-
5131
- // src/oidc/config.ts
5132
5882
  var DEFAULT_OIDC_ROUTE = "/oauth2";
5133
5883
  var MS_PER_SECOND = 1000;
5134
- var TOKEN_BYTES2 = 32;
5884
+ var TOKEN_BYTES3 = 32;
5135
5885
  var REFRESH_TTL_DAYS = 30;
5136
- var DEFAULT_ACCESS_TOKEN_TTL_MS2 = MILLISECONDS_IN_AN_HOUR;
5886
+ var DEFAULT_ACCESS_TOKEN_TTL_MS3 = MILLISECONDS_IN_AN_HOUR;
5137
5887
  var DEFAULT_ID_TOKEN_TTL_MS = MILLISECONDS_IN_AN_HOUR;
5138
5888
  var DEFAULT_REFRESH_TOKEN_TTL_MS = MILLISECONDS_IN_A_DAY * REFRESH_TTL_DAYS;
5139
5889
  var resolveAccessTtl = (ttl, scopes) => {
5140
5890
  if (typeof ttl === "function")
5141
5891
  return ttl({ scopes });
5142
- return ttl ?? DEFAULT_ACCESS_TOKEN_TTL_MS2;
5892
+ return ttl ?? DEFAULT_ACCESS_TOKEN_TTL_MS3;
5143
5893
  };
5144
5894
  var nowSeconds = (milliseconds) => Math.floor(milliseconds / MS_PER_SECOND);
5145
5895
  var narrowScopes = (available, requested) => requested === undefined || requested.length === 0 ? available : requested.filter((scope) => available.includes(scope));
@@ -5280,7 +6030,7 @@ var issueTokenSet = async ({
5280
6030
  idPayload.nonce = nonce;
5281
6031
  if (acr !== undefined)
5282
6032
  idPayload.acr = acr;
5283
- const refreshToken = generateSecureToken(TOKEN_BYTES2);
6033
+ const refreshToken = generateSecureToken(TOKEN_BYTES3);
5284
6034
  await config.refreshTokenStore.saveToken({
5285
6035
  acr,
5286
6036
  claims,
@@ -6706,15 +7456,15 @@ var updateRegisteredClient = async ({
6706
7456
  };
6707
7457
 
6708
7458
  // src/oidc/routes.ts
6709
- var HTTP_OK2 = 200;
7459
+ var HTTP_OK3 = 200;
6710
7460
  var HTTP_NO_CONTENT = 204;
6711
7461
  var HTTP_FOUND = 302;
6712
- var HTTP_BAD_REQUEST2 = 400;
6713
- var HTTP_UNAUTHORIZED2 = 401;
7462
+ var HTTP_BAD_REQUEST3 = 400;
7463
+ var HTTP_UNAUTHORIZED3 = 401;
6714
7464
  var HTTP_NOT_IMPLEMENTED = 501;
6715
7465
  var CODE_TTL_MINUTES = 10;
6716
7466
  var CODE_TTL_MS = MILLISECONDS_IN_A_MINUTE * CODE_TTL_MINUTES;
6717
- var TOKEN_BYTES3 = 32;
7467
+ var TOKEN_BYTES4 = 32;
6718
7468
  var BASIC_PREFIX2 = "Basic ";
6719
7469
  var jsonResponse = (value, status) => new Response(JSON.stringify(value), {
6720
7470
  headers: {
@@ -6724,7 +7474,7 @@ var jsonResponse = (value, status) => new Response(JSON.stringify(value), {
6724
7474
  status
6725
7475
  });
6726
7476
  var oauthError2 = (status, error) => jsonResponse({ error }, status);
6727
- var tokenResponse = (tokens) => jsonResponse(tokens, HTTP_OK2);
7477
+ var tokenResponse = (tokens) => jsonResponse(tokens, HTTP_OK3);
6728
7478
  var canonicalizeRequestUrl = (requestUrl, issuer) => {
6729
7479
  try {
6730
7480
  const url = new URL(requestUrl);
@@ -6746,7 +7496,7 @@ var formPostResponse = (redirectUri, params) => {
6746
7496
  "cache-control": "no-store",
6747
7497
  "content-type": "text/html;charset=UTF-8"
6748
7498
  },
6749
- status: HTTP_OK2
7499
+ status: HTTP_OK3
6750
7500
  });
6751
7501
  };
6752
7502
  var respondToClient = (redirectUri, responseMode, params) => {
@@ -6797,7 +7547,7 @@ var oidcProviderRoutes = (config) => {
6797
7547
  const userinfoRoute = `${oidcRoute}/userinfo`;
6798
7548
  const registrationBaseUrl = `${issuer}${registrationRoute}`;
6799
7549
  const tokenUrl = `${issuer}${oidcRoute}/token`;
6800
- const resolveClient = async (clientId) => await clientStore.findClient(clientId) ?? await config.resolveClientIdMetadata?.(clientId);
7550
+ const resolveClient = async (clientId) => await clientStore.findClient(clientId) ?? config.resolveClientIdMetadata?.(clientId);
6801
7551
  const authenticateClient = async (clientId, clientSecret) => {
6802
7552
  const client = await resolveClient(clientId);
6803
7553
  if (client === undefined)
@@ -6884,7 +7634,7 @@ var oidcProviderRoutes = (config) => {
6884
7634
  "dpop-nonce": fresh,
6885
7635
  "www-authenticate": 'DPoP error="use_dpop_nonce"'
6886
7636
  },
6887
- status: HTTP_UNAUTHORIZED2
7637
+ status: HTTP_UNAUTHORIZED3
6888
7638
  });
6889
7639
  };
6890
7640
  const grantAuthorizationCode = async (client, body, dpop, clientCertThumbprint) => {
@@ -6894,11 +7644,11 @@ var oidcProviderRoutes = (config) => {
6894
7644
  redirect_uri: redirectUri
6895
7645
  } = body;
6896
7646
  if (code === undefined || codeVerifier === undefined || redirectUri === undefined) {
6897
- return oauthError2(HTTP_BAD_REQUEST2, "invalid_request");
7647
+ return oauthError2(HTTP_BAD_REQUEST3, "invalid_request");
6898
7648
  }
6899
7649
  const record = await authorizationCodeStore.consumeCode(await hashToken(code));
6900
7650
  if (record === undefined || record.expiresAt < Date.now() || record.clientId !== client.clientId || record.redirectUri !== redirectUri || !await verifyPkce(codeVerifier, record.codeChallenge)) {
6901
- return oauthError2(HTTP_BAD_REQUEST2, "invalid_grant");
7651
+ return oauthError2(HTTP_BAD_REQUEST3, "invalid_grant");
6902
7652
  }
6903
7653
  const dpopResult = dpop === undefined ? undefined : await verifyDpopProof({
6904
7654
  htm: "POST",
@@ -6906,7 +7656,7 @@ var oidcProviderRoutes = (config) => {
6906
7656
  proof: dpop
6907
7657
  });
6908
7658
  if (dpop !== undefined && dpopResult === undefined) {
6909
- return oauthError2(HTTP_BAD_REQUEST2, "invalid_dpop_proof");
7659
+ return oauthError2(HTTP_BAD_REQUEST3, "invalid_dpop_proof");
6910
7660
  }
6911
7661
  return tokenResponse(await issueTokenSet({
6912
7662
  acr: record.acr,
@@ -6923,11 +7673,11 @@ var oidcProviderRoutes = (config) => {
6923
7673
  const grantRefreshToken = async (client, body, dpop, clientCertThumbprint) => {
6924
7674
  const presented = body.refresh_token;
6925
7675
  if (presented === undefined) {
6926
- return oauthError2(HTTP_BAD_REQUEST2, "invalid_request");
7676
+ return oauthError2(HTTP_BAD_REQUEST3, "invalid_request");
6927
7677
  }
6928
7678
  const record = await refreshTokenStore.consumeToken(await hashToken(presented));
6929
7679
  if (record === undefined || record.expiresAt < Date.now() || record.clientId !== client.clientId) {
6930
- return oauthError2(HTTP_BAD_REQUEST2, "invalid_grant");
7680
+ return oauthError2(HTTP_BAD_REQUEST3, "invalid_grant");
6931
7681
  }
6932
7682
  if (record.dpopJkt !== undefined) {
6933
7683
  const proof = await verifyDpopProof({
@@ -6936,7 +7686,7 @@ var oidcProviderRoutes = (config) => {
6936
7686
  proof: dpop
6937
7687
  });
6938
7688
  if (proof === undefined || proof.jkt !== record.dpopJkt) {
6939
- return oauthError2(HTTP_BAD_REQUEST2, "invalid_dpop_proof");
7689
+ return oauthError2(HTTP_BAD_REQUEST3, "invalid_dpop_proof");
6940
7690
  }
6941
7691
  }
6942
7692
  return tokenResponse(await issueTokenSet({
@@ -6952,7 +7702,7 @@ var oidcProviderRoutes = (config) => {
6952
7702
  };
6953
7703
  const grantTokenExchange = async (client, body, dpop) => {
6954
7704
  if (body.subject_token === undefined) {
6955
- return oauthError2(HTTP_BAD_REQUEST2, "invalid_request");
7705
+ return oauthError2(HTTP_BAD_REQUEST3, "invalid_request");
6956
7706
  }
6957
7707
  const dpopResult = dpop === undefined ? undefined : await verifyDpopProof({
6958
7708
  htm: "POST",
@@ -6960,7 +7710,7 @@ var oidcProviderRoutes = (config) => {
6960
7710
  proof: dpop
6961
7711
  });
6962
7712
  if (dpop !== undefined && dpopResult === undefined) {
6963
- return oauthError2(HTTP_BAD_REQUEST2, "invalid_dpop_proof");
7713
+ return oauthError2(HTTP_BAD_REQUEST3, "invalid_dpop_proof");
6964
7714
  }
6965
7715
  const result = await exchangeToken({
6966
7716
  actorClientId: client.clientId,
@@ -6971,21 +7721,21 @@ var oidcProviderRoutes = (config) => {
6971
7721
  subjectToken: body.subject_token
6972
7722
  });
6973
7723
  if (!result.ok)
6974
- return oauthError2(HTTP_BAD_REQUEST2, result.error);
7724
+ return oauthError2(HTTP_BAD_REQUEST3, result.error);
6975
7725
  return jsonResponse({
6976
7726
  access_token: result.accessToken,
6977
7727
  expires_in: result.expiresIn,
6978
7728
  issued_token_type: "urn:ietf:params:oauth:token-type:access_token",
6979
7729
  scope: result.scope,
6980
7730
  token_type: dpopResult === undefined ? "Bearer" : "DPoP"
6981
- }, HTTP_OK2);
7731
+ }, HTTP_OK3);
6982
7732
  };
6983
7733
  const grantBackchannel = async (client, body, dpop, clientCertThumbprint) => {
6984
7734
  if (config.backchannelAuthStore === undefined) {
6985
- return oauthError2(HTTP_BAD_REQUEST2, "unsupported_grant_type");
7735
+ return oauthError2(HTTP_BAD_REQUEST3, "unsupported_grant_type");
6986
7736
  }
6987
7737
  if (body.auth_req_id === undefined) {
6988
- return oauthError2(HTTP_BAD_REQUEST2, "invalid_request");
7738
+ return oauthError2(HTTP_BAD_REQUEST3, "invalid_request");
6989
7739
  }
6990
7740
  const dpopResult = dpop === undefined ? undefined : await verifyDpopProof({
6991
7741
  htm: "POST",
@@ -6993,7 +7743,7 @@ var oidcProviderRoutes = (config) => {
6993
7743
  proof: dpop
6994
7744
  });
6995
7745
  if (dpop !== undefined && dpopResult === undefined) {
6996
- return oauthError2(HTTP_BAD_REQUEST2, "invalid_dpop_proof");
7746
+ return oauthError2(HTTP_BAD_REQUEST3, "invalid_dpop_proof");
6997
7747
  }
6998
7748
  const result = await exchangeBackchannelAuth({
6999
7749
  authReqId: body.auth_req_id,
@@ -7003,7 +7753,7 @@ var oidcProviderRoutes = (config) => {
7003
7753
  dpopJkt: dpopResult?.jkt
7004
7754
  });
7005
7755
  if (!result.ok)
7006
- return oauthError2(HTTP_BAD_REQUEST2, result.error);
7756
+ return oauthError2(HTTP_BAD_REQUEST3, result.error);
7007
7757
  return jsonResponse({
7008
7758
  access_token: result.access_token,
7009
7759
  expires_in: result.expires_in,
@@ -7011,14 +7761,14 @@ var oidcProviderRoutes = (config) => {
7011
7761
  refresh_token: result.refresh_token,
7012
7762
  scope: result.scope,
7013
7763
  token_type: dpopResult === undefined ? "Bearer" : "DPoP"
7014
- }, HTTP_OK2);
7764
+ }, HTTP_OK3);
7015
7765
  };
7016
7766
  const grantDeviceCode = async (client, body, dpop) => {
7017
7767
  if (config.deviceAuthorizationStore === undefined) {
7018
- return oauthError2(HTTP_BAD_REQUEST2, "unsupported_grant_type");
7768
+ return oauthError2(HTTP_BAD_REQUEST3, "unsupported_grant_type");
7019
7769
  }
7020
7770
  if (body.device_code === undefined) {
7021
- return oauthError2(HTTP_BAD_REQUEST2, "invalid_request");
7771
+ return oauthError2(HTTP_BAD_REQUEST3, "invalid_request");
7022
7772
  }
7023
7773
  const dpopResult = dpop === undefined ? undefined : await verifyDpopProof({
7024
7774
  htm: "POST",
@@ -7026,7 +7776,7 @@ var oidcProviderRoutes = (config) => {
7026
7776
  proof: dpop
7027
7777
  });
7028
7778
  if (dpop !== undefined && dpopResult === undefined) {
7029
- return oauthError2(HTTP_BAD_REQUEST2, "invalid_dpop_proof");
7779
+ return oauthError2(HTTP_BAD_REQUEST3, "invalid_dpop_proof");
7030
7780
  }
7031
7781
  const result = await exchangeDeviceCode({
7032
7782
  clientId: client.clientId,
@@ -7035,7 +7785,7 @@ var oidcProviderRoutes = (config) => {
7035
7785
  dpopJkt: dpopResult?.jkt
7036
7786
  });
7037
7787
  if (!result.ok)
7038
- return oauthError2(HTTP_BAD_REQUEST2, result.error);
7788
+ return oauthError2(HTTP_BAD_REQUEST3, result.error);
7039
7789
  return jsonResponse({
7040
7790
  access_token: result.access_token,
7041
7791
  expires_in: result.expires_in,
@@ -7043,7 +7793,7 @@ var oidcProviderRoutes = (config) => {
7043
7793
  refresh_token: result.refresh_token,
7044
7794
  scope: result.scope,
7045
7795
  token_type: dpopResult === undefined ? "Bearer" : "DPoP"
7046
- }, HTTP_OK2);
7796
+ }, HTTP_OK3);
7047
7797
  };
7048
7798
  const grantTypes = [
7049
7799
  "authorization_code",
@@ -7059,13 +7809,17 @@ var oidcProviderRoutes = (config) => {
7059
7809
  if (config.vciConfig !== undefined) {
7060
7810
  grantTypes.push(PRE_AUTHORIZED_CODE_GRANT);
7061
7811
  }
7812
+ for (const grantType of config.publicTokenGrantTypes ?? []) {
7813
+ if (!grantTypes.includes(grantType))
7814
+ grantTypes.push(grantType);
7815
+ }
7062
7816
  const discovery = {
7063
7817
  authorization_endpoint: `${issuer}${authorizeRoute}`,
7064
7818
  authorization_response_iss_parameter_supported: true,
7065
7819
  backchannel_logout_session_supported: false,
7066
7820
  backchannel_logout_supported: true,
7067
- code_challenge_methods_supported: ["S256"],
7068
7821
  client_id_metadata_document_supported: config.resolveClientIdMetadata !== undefined,
7822
+ code_challenge_methods_supported: ["S256"],
7069
7823
  dpop_signing_alg_values_supported: ["ES256"],
7070
7824
  end_session_endpoint: `${issuer}${endSessionRoute}`,
7071
7825
  grant_types_supported: grantTypes,
@@ -7113,6 +7867,11 @@ var oidcProviderRoutes = (config) => {
7113
7867
  if (config.acrValuesSupported !== undefined && config.acrValuesSupported.length > 0) {
7114
7868
  discovery.acr_values_supported = config.acrValuesSupported;
7115
7869
  }
7870
+ const reservedDiscoveryKeys = new Set(Object.keys(discovery));
7871
+ for (const [key, value] of Object.entries(config.additionalDiscoveryMetadata ?? {})) {
7872
+ if (!reservedDiscoveryKeys.has(key))
7873
+ discovery[key] = value;
7874
+ }
7116
7875
  const handleEndSession = async ({
7117
7876
  cookie,
7118
7877
  inMemorySession,
@@ -7147,7 +7906,7 @@ var oidcProviderRoutes = (config) => {
7147
7906
  requestedUri: query.post_logout_redirect_uri
7148
7907
  });
7149
7908
  if (redirectUri === undefined) {
7150
- return jsonResponse({ ok: true }, HTTP_OK2);
7909
+ return jsonResponse({ ok: true }, HTTP_OK3);
7151
7910
  }
7152
7911
  const url = new URL(redirectUri);
7153
7912
  if (query.state !== undefined)
@@ -7168,11 +7927,11 @@ var oidcProviderRoutes = (config) => {
7168
7927
  store: config.pushedAuthorizationRequestStore
7169
7928
  });
7170
7929
  if (pushed === undefined) {
7171
- return jsonResponse({ error: "invalid_request_uri" }, HTTP_BAD_REQUEST2);
7930
+ return jsonResponse({ error: "invalid_request_uri" }, HTTP_BAD_REQUEST3);
7172
7931
  }
7173
7932
  effectiveQuery = pushed;
7174
7933
  } else if (query.request_uri !== undefined && query.request_uri.startsWith(REQUEST_URI_PREFIX)) {
7175
- return jsonResponse({ error: "invalid_request_uri" }, HTTP_BAD_REQUEST2);
7934
+ return jsonResponse({ error: "invalid_request_uri" }, HTTP_BAD_REQUEST3);
7176
7935
  }
7177
7936
  const initialClientId = effectiveQuery.client_id;
7178
7937
  const initialClient = initialClientId === undefined ? undefined : await resolveClient(initialClientId);
@@ -7183,7 +7942,7 @@ var oidcProviderRoutes = (config) => {
7183
7942
  jwt: effectiveQuery.request
7184
7943
  });
7185
7944
  if (!parsed.ok) {
7186
- return jsonResponse({ error: parsed.error }, HTTP_BAD_REQUEST2);
7945
+ return jsonResponse({ error: parsed.error }, HTTP_BAD_REQUEST3);
7187
7946
  }
7188
7947
  effectiveQuery = {
7189
7948
  ...parsed.params,
@@ -7203,11 +7962,11 @@ var oidcProviderRoutes = (config) => {
7203
7962
  } = effectiveQuery;
7204
7963
  const client = initialClient;
7205
7964
  if (client === undefined || clientId !== client.clientId || redirectUri === undefined || !client.redirectUris.includes(redirectUri)) {
7206
- return jsonResponse({ error: "invalid_client" }, HTTP_BAD_REQUEST2);
7965
+ return jsonResponse({ error: "invalid_client" }, HTTP_BAD_REQUEST3);
7207
7966
  }
7208
7967
  const responseMode = requestedResponseMode === "form_post" ? "form_post" : "query";
7209
7968
  if (requestedResponseMode !== undefined && requestedResponseMode !== "query" && requestedResponseMode !== "form_post") {
7210
- return jsonResponse({ error: "unsupported_response_mode" }, HTTP_BAD_REQUEST2);
7969
+ return jsonResponse({ error: "unsupported_response_mode" }, HTTP_BAD_REQUEST3);
7211
7970
  }
7212
7971
  const errorRedirect = (error) => {
7213
7972
  const params2 = {
@@ -7251,7 +8010,7 @@ var oidcProviderRoutes = (config) => {
7251
8010
  if (wantsSilent) {
7252
8011
  return errorRedirect(userSession === undefined ? "login_required" : "interaction_required");
7253
8012
  }
7254
- return loginUrl === undefined ? jsonResponse({ error: "login_required" }, HTTP_UNAUTHORIZED2) : redirectTo(`${loginUrl}?return_to=${encodeURIComponent(canonicalizeRequestUrl(request.url, issuer))}`);
8013
+ return loginUrl === undefined ? jsonResponse({ error: "login_required" }, HTTP_UNAUTHORIZED3) : redirectTo(`${loginUrl}?return_to=${encodeURIComponent(canonicalizeRequestUrl(request.url, issuer))}`);
7255
8014
  }
7256
8015
  const requested = scope === undefined || scope.length === 0 ? client.scopes : scope.split(" ").filter((entry) => client.scopes.includes(entry));
7257
8016
  if (config.consentUrl !== undefined && await config.needsConsent?.({
@@ -7287,7 +8046,7 @@ var oidcProviderRoutes = (config) => {
7287
8046
  if (requestedAcr !== undefined && (userAcr === undefined || !requestedAcr.includes(userAcr))) {
7288
8047
  return errorRedirect("insufficient_user_authentication");
7289
8048
  }
7290
- const code = generateSecureToken(TOKEN_BYTES3);
8049
+ const code = generateSecureToken(TOKEN_BYTES4);
7291
8050
  await authorizationCodeStore.saveCode({
7292
8051
  acr: userAcr,
7293
8052
  claims: getClaims?.(userSession.user),
@@ -7334,7 +8093,7 @@ var oidcProviderRoutes = (config) => {
7334
8093
  if (body.grant_type === PRE_AUTHORIZED_CODE_GRANT && config.vciConfig !== undefined) {
7335
8094
  const preAuthorizedCode = body["pre-authorized_code"];
7336
8095
  if (typeof preAuthorizedCode !== "string") {
7337
- return oauthError2(HTTP_BAD_REQUEST2, "invalid_request");
8096
+ return oauthError2(HTTP_BAD_REQUEST3, "invalid_request");
7338
8097
  }
7339
8098
  const result = await exchangePreAuthorizedCode({
7340
8099
  config: config.vciConfig,
@@ -7343,14 +8102,23 @@ var oidcProviderRoutes = (config) => {
7343
8102
  signingKey: config.vciConfig.signingKey ?? config.signingKey
7344
8103
  });
7345
8104
  if (!result.ok)
7346
- return oauthError2(HTTP_BAD_REQUEST2, result.error);
8105
+ return oauthError2(HTTP_BAD_REQUEST3, result.error);
7347
8106
  return jsonResponse({
7348
8107
  access_token: result.access_token,
7349
8108
  c_nonce: result.c_nonce,
7350
8109
  c_nonce_expires_in: result.c_nonce_expires_in,
7351
8110
  expires_in: result.expires_in,
7352
8111
  token_type: result.token_type
7353
- }, HTTP_OK2);
8112
+ }, HTTP_OK3);
8113
+ }
8114
+ if (typeof body.grant_type === "string" && config.publicTokenGrantTypes?.includes(body.grant_type) === true && config.handlePublicTokenGrant !== undefined) {
8115
+ const result = await config.handlePublicTokenGrant({
8116
+ body,
8117
+ request
8118
+ });
8119
+ if (result !== undefined) {
8120
+ return jsonResponse(result.body, result.status);
8121
+ }
7354
8122
  }
7355
8123
  const basic = readBasicAuth2(headers.authorization);
7356
8124
  const auth = await authenticateTokenClient({
@@ -7363,7 +8131,7 @@ var oidcProviderRoutes = (config) => {
7363
8131
  requestHeaders: request.headers
7364
8132
  });
7365
8133
  if (auth === undefined) {
7366
- return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
8134
+ return oauthError2(HTTP_UNAUTHORIZED3, "invalid_client");
7367
8135
  }
7368
8136
  const { client, clientCertThumbprint } = auth;
7369
8137
  const nonceChallenge = await dpopNonceChallenge(headers.dpop);
@@ -7384,11 +8152,13 @@ var oidcProviderRoutes = (config) => {
7384
8152
  if (body.grant_type === CIBA_GRANT_TYPE) {
7385
8153
  return grantBackchannel(client, body, headers.dpop, clientCertThumbprint);
7386
8154
  }
7387
- return oauthError2(HTTP_BAD_REQUEST2, "unsupported_grant_type");
8155
+ return oauthError2(HTTP_BAD_REQUEST3, "unsupported_grant_type");
7388
8156
  }, {
7389
8157
  body: t14.Object({
8158
+ assertion: t14.Optional(t14.String()),
7390
8159
  audience: t14.Optional(t14.String()),
7391
8160
  auth_req_id: t14.Optional(t14.String()),
8161
+ claim_token: t14.Optional(t14.String()),
7392
8162
  client_assertion: t14.Optional(t14.String()),
7393
8163
  client_assertion_type: t14.Optional(t14.String()),
7394
8164
  client_id: t14.Optional(t14.String()),
@@ -7420,7 +8190,7 @@ var oidcProviderRoutes = (config) => {
7420
8190
  requestHeaders: request.headers
7421
8191
  });
7422
8192
  if (auth === undefined) {
7423
- return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
8193
+ return oauthError2(HTTP_UNAUTHORIZED3, "invalid_client");
7424
8194
  }
7425
8195
  const { client } = auth;
7426
8196
  const isAuthField = (key) => key === "client_assertion" || key === "client_assertion_type" || key === "client_secret";
@@ -7431,7 +8201,7 @@ var oidcProviderRoutes = (config) => {
7431
8201
  store: config.pushedAuthorizationRequestStore,
7432
8202
  ttlMs: config.pushedAuthorizationRequestTtlMs
7433
8203
  });
7434
- return jsonResponse(result.body, result.ok ? HTTP_OK2 : result.status);
8204
+ return jsonResponse(result.body, result.ok ? HTTP_OK3 : result.status);
7435
8205
  }, {
7436
8206
  body: t14.Object({
7437
8207
  acr_values: t14.Optional(t14.String()),
@@ -7458,11 +8228,11 @@ var oidcProviderRoutes = (config) => {
7458
8228
  const clientId = body.client_id ?? basic.clientId;
7459
8229
  const clientSecret = body.client_secret ?? basic.clientSecret;
7460
8230
  if (clientId === undefined) {
7461
- return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
8231
+ return oauthError2(HTTP_UNAUTHORIZED3, "invalid_client");
7462
8232
  }
7463
8233
  const client = await authenticateClient(clientId, clientSecret);
7464
8234
  if (client === undefined) {
7465
- return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
8235
+ return oauthError2(HTTP_UNAUTHORIZED3, "invalid_client");
7466
8236
  }
7467
8237
  const result = await introspectToken({
7468
8238
  config,
@@ -7470,7 +8240,7 @@ var oidcProviderRoutes = (config) => {
7470
8240
  now: Date.now(),
7471
8241
  token: body.token
7472
8242
  });
7473
- return jsonResponse(result, HTTP_OK2);
8243
+ return jsonResponse(result, HTTP_OK3);
7474
8244
  }, {
7475
8245
  body: t14.Object({
7476
8246
  client_id: t14.Optional(t14.String()),
@@ -7486,16 +8256,16 @@ var oidcProviderRoutes = (config) => {
7486
8256
  const clientId = body.client_id ?? basic.clientId;
7487
8257
  const clientSecret = body.client_secret ?? basic.clientSecret;
7488
8258
  if (clientId === undefined) {
7489
- return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
8259
+ return oauthError2(HTTP_UNAUTHORIZED3, "invalid_client");
7490
8260
  }
7491
8261
  const client = await authenticateClient(clientId, clientSecret);
7492
8262
  if (client === undefined) {
7493
- return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
8263
+ return oauthError2(HTTP_UNAUTHORIZED3, "invalid_client");
7494
8264
  }
7495
8265
  if (body.token_type_hint !== "access_token") {
7496
8266
  await revokeRefreshToken(config, body.token);
7497
8267
  }
7498
- return new Response(null, { status: HTTP_OK2 });
8268
+ return new Response(null, { status: HTTP_OK3 });
7499
8269
  }, {
7500
8270
  body: t14.Object({
7501
8271
  client_id: t14.Optional(t14.String()),
@@ -7508,20 +8278,20 @@ var oidcProviderRoutes = (config) => {
7508
8278
  })
7509
8279
  }).post(backchannelAuthorizationRoute, async ({ body, headers }) => {
7510
8280
  if (config.backchannelAuthStore === undefined) {
7511
- return oauthError2(HTTP_BAD_REQUEST2, "unsupported_grant_type");
8281
+ return oauthError2(HTTP_BAD_REQUEST3, "unsupported_grant_type");
7512
8282
  }
7513
8283
  const basic = readBasicAuth2(headers.authorization);
7514
8284
  const clientId = body.client_id ?? basic.clientId;
7515
8285
  const clientSecret = body.client_secret ?? basic.clientSecret;
7516
8286
  if (clientId === undefined) {
7517
- return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
8287
+ return oauthError2(HTTP_UNAUTHORIZED3, "invalid_client");
7518
8288
  }
7519
8289
  const client = await authenticateClient(clientId, clientSecret);
7520
8290
  if (client === undefined) {
7521
- return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
8291
+ return oauthError2(HTTP_UNAUTHORIZED3, "invalid_client");
7522
8292
  }
7523
8293
  if (body.login_hint === undefined) {
7524
- return oauthError2(HTTP_BAD_REQUEST2, "invalid_request");
8294
+ return oauthError2(HTTP_BAD_REQUEST3, "invalid_request");
7525
8295
  }
7526
8296
  const requested = body.scope === undefined || body.scope.length === 0 ? client.scopes : body.scope.split(" ").filter((entry) => client.scopes.includes(entry));
7527
8297
  const result = await issueBackchannelAuth({
@@ -7533,12 +8303,12 @@ var oidcProviderRoutes = (config) => {
7533
8303
  requestedScopes: requested
7534
8304
  });
7535
8305
  if (!result.ok)
7536
- return oauthError2(HTTP_BAD_REQUEST2, result.error);
8306
+ return oauthError2(HTTP_BAD_REQUEST3, result.error);
7537
8307
  return jsonResponse({
7538
8308
  auth_req_id: result.auth_req_id,
7539
8309
  expires_in: result.expires_in,
7540
8310
  interval: result.interval
7541
- }, HTTP_OK2);
8311
+ }, HTTP_OK3);
7542
8312
  }, {
7543
8313
  body: t14.Object({
7544
8314
  binding_message: t14.Optional(t14.String()),
@@ -7552,17 +8322,17 @@ var oidcProviderRoutes = (config) => {
7552
8322
  })
7553
8323
  }).post(deviceAuthorizationRoute, async ({ body, headers }) => {
7554
8324
  if (config.deviceAuthorizationStore === undefined) {
7555
- return oauthError2(HTTP_BAD_REQUEST2, "unsupported_grant_type");
8325
+ return oauthError2(HTTP_BAD_REQUEST3, "unsupported_grant_type");
7556
8326
  }
7557
8327
  const basic = readBasicAuth2(headers.authorization);
7558
8328
  const clientId = body.client_id ?? basic.clientId;
7559
8329
  const clientSecret = body.client_secret ?? basic.clientSecret;
7560
8330
  if (clientId === undefined) {
7561
- return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
8331
+ return oauthError2(HTTP_UNAUTHORIZED3, "invalid_client");
7562
8332
  }
7563
8333
  const client = await authenticateClient(clientId, clientSecret);
7564
8334
  if (client === undefined) {
7565
- return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
8335
+ return oauthError2(HTTP_UNAUTHORIZED3, "invalid_client");
7566
8336
  }
7567
8337
  const requested = body.scope === undefined || body.scope.length === 0 ? client.scopes : body.scope.split(" ").filter((entry) => client.scopes.includes(entry));
7568
8338
  const response = await issueDeviceAuthorization({
@@ -7571,7 +8341,7 @@ var oidcProviderRoutes = (config) => {
7571
8341
  now: Date.now(),
7572
8342
  requestedScopes: requested
7573
8343
  });
7574
- return jsonResponse(response, HTTP_OK2);
8344
+ return jsonResponse(response, HTTP_OK3);
7575
8345
  }, {
7576
8346
  body: t14.Object({
7577
8347
  client_id: t14.Optional(t14.String()),
@@ -7583,7 +8353,7 @@ var oidcProviderRoutes = (config) => {
7583
8353
  })
7584
8354
  }).post(deviceApproveRoute, async ({ body, cookie: { user_session_id }, store }) => {
7585
8355
  if (config.deviceAuthorizationStore === undefined) {
7586
- return oauthError2(HTTP_BAD_REQUEST2, "unsupported_grant_type");
8356
+ return oauthError2(HTTP_BAD_REQUEST3, "unsupported_grant_type");
7587
8357
  }
7588
8358
  const userSession = await loadSessionFromSource({
7589
8359
  authSessionStore,
@@ -7591,7 +8361,7 @@ var oidcProviderRoutes = (config) => {
7591
8361
  userSessionId: user_session_id.value
7592
8362
  });
7593
8363
  if (userSession === undefined) {
7594
- return oauthError2(HTTP_UNAUTHORIZED2, "login_required");
8364
+ return oauthError2(HTTP_UNAUTHORIZED3, "login_required");
7595
8365
  }
7596
8366
  const result = body.action === "deny" ? await denyDeviceAuthorization({
7597
8367
  config,
@@ -7602,8 +8372,8 @@ var oidcProviderRoutes = (config) => {
7602
8372
  userSub: getUserId(userSession.user)
7603
8373
  });
7604
8374
  if (!result.ok)
7605
- return oauthError2(HTTP_BAD_REQUEST2, result.error);
7606
- return jsonResponse({ ok: true }, HTTP_OK2);
8375
+ return oauthError2(HTTP_BAD_REQUEST3, result.error);
8376
+ return jsonResponse({ ok: true }, HTTP_OK3);
7607
8377
  }, {
7608
8378
  body: t14.Object({
7609
8379
  action: t14.Optional(t14.Union([t14.Literal("approve"), t14.Literal("deny")])),
@@ -7655,7 +8425,7 @@ var oidcProviderRoutes = (config) => {
7655
8425
  registrationBaseUrl,
7656
8426
  registrationTokenStore: config.clientRegistrationTokenStore
7657
8427
  });
7658
- return jsonResponse(result.body, result.ok ? HTTP_OK2 : result.status);
8428
+ return jsonResponse(result.body, result.ok ? HTTP_OK3 : result.status);
7659
8429
  }, {
7660
8430
  body: t14.Object({
7661
8431
  backchannel_logout_uri: t14.Optional(t14.String()),
@@ -7742,10 +8512,10 @@ var oidcProviderRoutes = (config) => {
7742
8512
  "content-type": "application/json",
7743
8513
  "www-authenticate": userInfoChallengeHeader(result.error)
7744
8514
  },
7745
- status: HTTP_UNAUTHORIZED2
8515
+ status: HTTP_UNAUTHORIZED3
7746
8516
  });
7747
8517
  }
7748
- return jsonResponse(result.body, HTTP_OK2);
8518
+ return jsonResponse(result.body, HTTP_OK3);
7749
8519
  }, {
7750
8520
  headers: t14.Object({
7751
8521
  authorization: t14.Optional(t14.String())
@@ -7759,10 +8529,10 @@ var oidcProviderRoutes = (config) => {
7759
8529
  "content-type": "application/json",
7760
8530
  "www-authenticate": userInfoChallengeHeader(result.error)
7761
8531
  },
7762
- status: HTTP_UNAUTHORIZED2
8532
+ status: HTTP_UNAUTHORIZED3
7763
8533
  });
7764
8534
  }
7765
- return jsonResponse(result.body, HTTP_OK2);
8535
+ return jsonResponse(result.body, HTTP_OK3);
7766
8536
  }, {
7767
8537
  body: t14.Object({
7768
8538
  access_token: t14.Optional(t14.String())
@@ -13742,7 +14512,7 @@ var PgJson = class extends PgColumn {
13742
14512
  return "json";
13743
14513
  }
13744
14514
  };
13745
- function json(name2) {
14515
+ function json2(name2) {
13746
14516
  return new PgJsonBuilder(name2 ?? "");
13747
14517
  }
13748
14518
 
@@ -14279,7 +15049,7 @@ function getPgColumnBuilders() {
14279
15049
  inet,
14280
15050
  integer,
14281
15051
  interval,
14282
- json,
15052
+ json: json2,
14283
15053
  jsonb,
14284
15054
  line,
14285
15055
  macaddr,
@@ -14496,6 +15266,9 @@ var Index = class {
14496
15266
  function index(name2) {
14497
15267
  return new IndexBuilderOn(false, name2);
14498
15268
  }
15269
+ function uniqueIndex(name2) {
15270
+ return new IndexBuilderOn(true, name2);
15271
+ }
14499
15272
  // node_modules/drizzle-orm/pg-core/checks.js
14500
15273
  var CheckBuilder = class {
14501
15274
  static [entityKind] = "PgCheckBuilder";
@@ -24277,9 +25050,9 @@ var createInMemoryCredentialOfferStore = () => {
24277
25050
  };
24278
25051
  // src/oidc/vciRoutes.ts
24279
25052
  import { Elysia as Elysia38, t as t33 } from "elysia";
24280
- var HTTP_OK3 = 200;
24281
- var HTTP_BAD_REQUEST3 = 400;
24282
- var HTTP_UNAUTHORIZED3 = 401;
25053
+ var HTTP_OK4 = 200;
25054
+ var HTTP_BAD_REQUEST4 = 400;
25055
+ var HTTP_UNAUTHORIZED4 = 401;
24283
25056
  var BEARER_PREFIX5 = "Bearer ";
24284
25057
  var errorBody = (error, status) => new Response(JSON.stringify({ error }), {
24285
25058
  headers: { "content-type": "application/json" },
@@ -24308,7 +25081,7 @@ var vciRoutes = ({
24308
25081
  }))).post(credentialRoute, async ({ body, headers }) => {
24309
25082
  const accessToken = extractBearer(headers.authorization);
24310
25083
  if (accessToken === undefined) {
24311
- return errorBody("invalid_token", HTTP_UNAUTHORIZED3);
25084
+ return errorBody("invalid_token", HTTP_UNAUTHORIZED4);
24312
25085
  }
24313
25086
  const result = await issueCredential({
24314
25087
  config: vciConfig,
@@ -24321,8 +25094,8 @@ var vciRoutes = ({
24321
25094
  signingKey: vciSigningKey
24322
25095
  });
24323
25096
  if (!result.ok)
24324
- return errorBody(result.error, HTTP_BAD_REQUEST3);
24325
- return Response.json({ credential: result.credential, format: result.format }, { status: HTTP_OK3 });
25097
+ return errorBody(result.error, HTTP_BAD_REQUEST4);
25098
+ return Response.json({ credential: result.credential, format: result.format }, { status: HTTP_OK4 });
24326
25099
  }, {
24327
25100
  body: t33.Object({
24328
25101
  format: t33.Optional(t33.Union([t33.Literal("vc+sd-jwt")])),
@@ -24333,7 +25106,7 @@ var vciRoutes = ({
24333
25106
  })
24334
25107
  }).post(nonceRoute, async () => {
24335
25108
  if (vciConfig.credentialNonceStore === undefined) {
24336
- return errorBody("not_supported", HTTP_BAD_REQUEST3);
25109
+ return errorBody("not_supported", HTTP_BAD_REQUEST4);
24337
25110
  }
24338
25111
  const { generateSecureToken: generateSecureToken2, hashToken: hashToken2 } = await Promise.resolve().then(() => (init_crypto(), exports_crypto));
24339
25112
  const nonceBytes = 16;
@@ -24347,7 +25120,7 @@ var vciRoutes = ({
24347
25120
  return Response.json({
24348
25121
  c_nonce: nonce,
24349
25122
  c_nonce_expires_in: Math.floor(ttlMs / msPerSecond)
24350
- }, { status: HTTP_OK3 });
25123
+ }, { status: HTTP_OK4 });
24351
25124
  });
24352
25125
  };
24353
25126
  // src/vc/statusList.ts
@@ -24451,7 +25224,7 @@ var verifyStatusListJwt = async ({
24451
25224
  };
24452
25225
  // src/vc/statusListRoutes.ts
24453
25226
  import { Elysia as Elysia39, t as t34 } from "elysia";
24454
- var HTTP_OK4 = 200;
25227
+ var HTTP_OK5 = 200;
24455
25228
  var HTTP_NOT_FOUND = 404;
24456
25229
  var DEFAULT_STATUS_ROUTE = "/vc/status";
24457
25230
  var statusListRoutes = ({
@@ -24476,7 +25249,7 @@ var statusListRoutes = ({
24476
25249
  });
24477
25250
  return new Response(jwt, {
24478
25251
  headers: { "content-type": STATUS_LIST_SUB_TYP },
24479
- status: HTTP_OK4
25252
+ status: HTTP_OK5
24480
25253
  });
24481
25254
  }, { params: t34.Object({ listId: t34.String() }) });
24482
25255
  };
@@ -24684,8 +25457,8 @@ var createInMemoryPresentationRequestStore = () => {
24684
25457
  };
24685
25458
  // src/vc/vpRoutes.ts
24686
25459
  import { Elysia as Elysia40, t as t35 } from "elysia";
24687
- var HTTP_OK5 = 200;
24688
- var HTTP_BAD_REQUEST4 = 400;
25460
+ var HTTP_OK6 = 200;
25461
+ var HTTP_BAD_REQUEST5 = 400;
24689
25462
  var HTTP_NOT_FOUND2 = 404;
24690
25463
  var errorBody2 = (error, status) => new Response(JSON.stringify({ error }), {
24691
25464
  headers: { "content-type": "application/json" },
@@ -24718,7 +25491,7 @@ var vpRoutes = ({
24718
25491
  nonce: result.nonce,
24719
25492
  request_uri: result.requestUri,
24720
25493
  requestId: result.request.requestId
24721
- }, { status: HTTP_OK5 });
25494
+ }, { status: HTTP_OK6 });
24722
25495
  }, {
24723
25496
  body: t35.Object({
24724
25497
  client_id: t35.Optional(t35.String()),
@@ -24747,19 +25520,19 @@ var vpRoutes = ({
24747
25520
  headers: {
24748
25521
  "content-type": "application/oauth-authz-req+jwt"
24749
25522
  },
24750
- status: HTTP_OK5
25523
+ status: HTTP_OK6
24751
25524
  });
24752
25525
  }, { params: t35.Object({ id: t35.String() }) }).post(responseRoute, async ({ body }) => {
24753
25526
  const requestId = body.state;
24754
25527
  if (requestId === undefined) {
24755
- return errorBody2("missing_state", HTTP_BAD_REQUEST4);
25528
+ return errorBody2("missing_state", HTTP_BAD_REQUEST5);
24756
25529
  }
24757
25530
  const result = await verifyPresentationResponse({
24758
25531
  config: vpConfig,
24759
25532
  input: { requestId, vpToken: body.vp_token }
24760
25533
  });
24761
25534
  if (!result.ok)
24762
- return errorBody2(result.error, HTTP_BAD_REQUEST4);
25535
+ return errorBody2(result.error, HTTP_BAD_REQUEST5);
24763
25536
  if (onVerifiedPresentation !== undefined) {
24764
25537
  await onVerifiedPresentation({ verified: result.verified });
24765
25538
  }
@@ -24768,7 +25541,7 @@ var vpRoutes = ({
24768
25541
  holder_jwk: result.verified.holderJwk,
24769
25542
  protected_claims: result.verified.protectedClaims,
24770
25543
  verified: true
24771
- }, { status: HTTP_OK5 });
25544
+ }, { status: HTTP_OK6 });
24772
25545
  }, {
24773
25546
  body: t35.Object({
24774
25547
  presentation_submission: t35.Optional(t35.Unknown()),
@@ -24966,6 +25739,284 @@ var createPostgresScimTokenStore = (db) => ({
24966
25739
  });
24967
25740
  }
24968
25741
  });
25742
+ // src/agents/registrationClient.ts
25743
+ var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
25744
+ var secureUrl = (value, allowLocalhost) => {
25745
+ if (typeof value !== "string")
25746
+ return;
25747
+ try {
25748
+ const url = new URL(value);
25749
+ if (url.protocol === "https:")
25750
+ return url.toString();
25751
+ if (allowLocalhost && url.protocol === "http:" && (url.hostname === "localhost" || url.hostname === "127.0.0.1")) {
25752
+ return url.toString();
25753
+ }
25754
+ } catch {
25755
+ return;
25756
+ }
25757
+ return;
25758
+ };
25759
+ var readBoundedJson = async (response, maxBytes) => {
25760
+ const length = Number(response.headers.get("content-length"));
25761
+ if (Number.isFinite(length) && length > maxBytes) {
25762
+ throw new Error("Agent registration metadata exceeds the response limit");
25763
+ }
25764
+ const bytes = new Uint8Array(await response.arrayBuffer());
25765
+ if (bytes.byteLength > maxBytes) {
25766
+ throw new Error("Agent registration metadata exceeds the response limit");
25767
+ }
25768
+ const parsed = JSON.parse(new TextDecoder().decode(bytes));
25769
+ return parsed;
25770
+ };
25771
+ var stringArray = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
25772
+ var requestJson = async (request, url, init, maxBytes) => {
25773
+ const response = await request(url, {
25774
+ ...init,
25775
+ headers: {
25776
+ accept: "application/json",
25777
+ ...init.headers
25778
+ },
25779
+ redirect: "error"
25780
+ });
25781
+ const body = await readBoundedJson(response, maxBytes);
25782
+ if (!isObject2(body))
25783
+ throw new Error("Expected a JSON object");
25784
+ return { body, response };
25785
+ };
25786
+ var createAgentRegistrationClient = (discovery, options = {}) => {
25787
+ const request = options.request ?? fetch;
25788
+ const maxBytes = options.maxResponseBytes ?? 256 * 1024;
25789
+ const post = (url, body, form = false) => requestJson(request, url, {
25790
+ body: form ? new URLSearchParams(Object.fromEntries(Object.entries(body).map(([key, value]) => [
25791
+ key,
25792
+ String(value)
25793
+ ]))) : JSON.stringify(body),
25794
+ headers: {
25795
+ "content-type": form ? "application/x-www-form-urlencoded" : "application/json"
25796
+ },
25797
+ method: "POST"
25798
+ }, maxBytes);
25799
+ return {
25800
+ beginAnonymous: () => post(discovery.agentAuth.identityEndpoint, { type: "anonymous" }),
25801
+ beginServiceAuth: (loginHint) => post(discovery.agentAuth.identityEndpoint, {
25802
+ login_hint: loginHint,
25803
+ type: "service_auth"
25804
+ }),
25805
+ beginVerified: (assertion) => {
25806
+ if (!discovery.agentAuth.identityAssertionTypes.includes(AGENT_IDENTITY_ASSERTION_TYPE)) {
25807
+ throw new Error("Service does not accept ID-JAG assertions");
25808
+ }
25809
+ return post(discovery.agentAuth.identityEndpoint, {
25810
+ assertion,
25811
+ assertion_type: AGENT_IDENTITY_ASSERTION_TYPE,
25812
+ type: "identity_assertion"
25813
+ });
25814
+ },
25815
+ claim: (claimToken, email) => post(discovery.agentAuth.claimEndpoint, {
25816
+ claim_token: claimToken,
25817
+ email
25818
+ }),
25819
+ exchangeAssertion: (assertion) => post(discovery.tokenEndpoint, {
25820
+ assertion,
25821
+ grant_type: AGENT_IDENTITY_ASSERTION_GRANT_TYPE,
25822
+ resource: discovery.resource
25823
+ }, true),
25824
+ pollClaim: (claimToken) => post(discovery.tokenEndpoint, {
25825
+ claim_token: claimToken,
25826
+ grant_type: AGENT_CLAIM_GRANT_TYPE
25827
+ }, true)
25828
+ };
25829
+ };
25830
+ var discoverAgentRegistration = async (resource, options = {}) => {
25831
+ const request = options.request ?? fetch;
25832
+ const maxBytes = options.maxResponseBytes ?? 256 * 1024;
25833
+ const allowLocalhost = options.allowInsecureLocalhost === true;
25834
+ const resourceUrl = secureUrl(resource, allowLocalhost);
25835
+ if (resourceUrl === undefined)
25836
+ throw new Error("Resource URL must use HTTPS");
25837
+ const resourceMetadataUrl = new URL("/.well-known/oauth-protected-resource", resourceUrl).toString();
25838
+ const prm = await requestJson(request, resourceMetadataUrl, {}, maxBytes);
25839
+ const advertisedResource = secureUrl(prm.body.resource, allowLocalhost);
25840
+ if (advertisedResource === undefined || advertisedResource !== resourceUrl) {
25841
+ throw new Error("Protected resource metadata identity mismatch");
25842
+ }
25843
+ const authorizationServer = secureUrl(stringArray(prm.body.authorization_servers)[0], allowLocalhost);
25844
+ if (authorizationServer === undefined) {
25845
+ throw new Error("No secure authorization server is advertised");
25846
+ }
25847
+ const asUrl = new URL("/.well-known/oauth-authorization-server", authorizationServer).toString();
25848
+ const metadata = await requestJson(request, asUrl, {}, maxBytes);
25849
+ if (secureUrl(metadata.body.issuer, allowLocalhost) !== authorizationServer) {
25850
+ throw new Error("Authorization server issuer mismatch");
25851
+ }
25852
+ const tokenEndpoint = secureUrl(metadata.body.token_endpoint, allowLocalhost);
25853
+ const agentAuth = metadata.body.agent_auth;
25854
+ if (tokenEndpoint === undefined || !isObject2(agentAuth)) {
25855
+ throw new Error("Authorization server does not advertise agent registration");
25856
+ }
25857
+ const identityEndpoint = secureUrl(agentAuth.identity_endpoint, allowLocalhost);
25858
+ const claimEndpoint = secureUrl(agentAuth.claim_endpoint, allowLocalhost);
25859
+ const skill = secureUrl(agentAuth.skill, allowLocalhost);
25860
+ const assertionMetadata = agentAuth.identity_assertion;
25861
+ if (identityEndpoint === undefined || claimEndpoint === undefined || skill === undefined || !isObject2(assertionMetadata)) {
25862
+ throw new Error("Agent registration metadata is incomplete");
25863
+ }
25864
+ return {
25865
+ agentAuth: {
25866
+ claimEndpoint,
25867
+ identityAssertionTypes: stringArray(assertionMetadata.assertion_types_supported),
25868
+ identityEndpoint,
25869
+ identityTypes: stringArray(agentAuth.identity_types_supported),
25870
+ skill
25871
+ },
25872
+ authorizationServer,
25873
+ resource: resourceUrl,
25874
+ resourceMetadataUrl,
25875
+ scopes: stringArray(prm.body.scopes_supported),
25876
+ tokenEndpoint
25877
+ };
25878
+ };
25879
+ // src/agents/idJag.ts
25880
+ init_constants();
25881
+ var createInMemoryAgentIdentityAssertionJtiStore = () => {
25882
+ const entries = new Map;
25883
+ return {
25884
+ recordIfFresh: async (issuer, jti, expiresAt) => {
25885
+ const now = Date.now();
25886
+ for (const [key2, expiry] of entries) {
25887
+ if (expiry <= now)
25888
+ entries.delete(key2);
25889
+ }
25890
+ const key = `${issuer}\x00${jti}`;
25891
+ if (entries.has(key))
25892
+ return false;
25893
+ entries.set(key, expiresAt);
25894
+ return true;
25895
+ }
25896
+ };
25897
+ };
25898
+ var issueAgentIdentityAssertion = async ({
25899
+ agentContextId,
25900
+ agentPlatform,
25901
+ audience,
25902
+ clientId,
25903
+ issuer,
25904
+ now = Date.now(),
25905
+ resource,
25906
+ signingKey,
25907
+ ttlMs = 5 * 60 * MILLISECONDS_IN_A_SECOND,
25908
+ user
25909
+ }) => {
25910
+ if (user.emailVerified !== true && user.phoneNumberVerified !== true) {
25911
+ throw new Error("ID-JAG issuance requires a verified email or phone number");
25912
+ }
25913
+ const expiresAt = now + ttlMs;
25914
+ const payload = {
25915
+ aud: audience,
25916
+ auth_time: Math.floor(user.authenticatedAt / MILLISECONDS_IN_A_SECOND),
25917
+ client_id: clientId,
25918
+ exp: Math.floor(expiresAt / MILLISECONDS_IN_A_SECOND),
25919
+ iat: Math.floor(now / MILLISECONDS_IN_A_SECOND),
25920
+ iss: issuer,
25921
+ jti: crypto.randomUUID(),
25922
+ sub: user.subject
25923
+ };
25924
+ if (user.email !== undefined)
25925
+ payload.email = user.email;
25926
+ if (user.emailVerified !== undefined)
25927
+ payload.email_verified = user.emailVerified;
25928
+ if (user.name !== undefined)
25929
+ payload.name = user.name;
25930
+ if (user.phoneNumber !== undefined)
25931
+ payload.phone_number = user.phoneNumber;
25932
+ if (user.phoneNumberVerified !== undefined)
25933
+ payload.phone_number_verified = user.phoneNumberVerified;
25934
+ if (user.methods !== undefined)
25935
+ payload.amr = user.methods;
25936
+ if (resource !== undefined)
25937
+ payload.resource = resource;
25938
+ if (agentPlatform !== undefined)
25939
+ payload.agent_platform = agentPlatform;
25940
+ if (agentContextId !== undefined)
25941
+ payload.agent_context_id = agentContextId;
25942
+ return {
25943
+ assertion: await signJwt(payload, signingKey, "oauth-id-jag+jwt"),
25944
+ assertionType: AGENT_IDENTITY_ASSERTION_TYPE,
25945
+ expiresAt
25946
+ };
25947
+ };
25948
+ var numberClaim2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
25949
+ var stringClaim2 = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
25950
+ var createAgentIdentityAssertionVerifier = ({
25951
+ audience,
25952
+ clockSkewMs = MILLISECONDS_IN_A_SECOND * 60,
25953
+ jtiStore,
25954
+ maxAssertionLifetimeMs = MILLISECONDS_IN_A_SECOND * 60 * 60,
25955
+ maxAuthenticationAgeMs = MILLISECONDS_IN_A_SECOND * 60 * 60,
25956
+ resolveIssuer
25957
+ }) => async (assertion, now = Date.now()) => {
25958
+ const segments = assertion.split(".");
25959
+ if (segments.length !== 3 || segments[1] === undefined)
25960
+ return;
25961
+ let decoded;
25962
+ try {
25963
+ decoded = JSON.parse(Buffer.from(segments[1], "base64url").toString("utf8"));
25964
+ } catch {
25965
+ return;
25966
+ }
25967
+ if (typeof decoded !== "object" || decoded === null || Array.isArray(decoded)) {
25968
+ return;
25969
+ }
25970
+ const unverified = Object.fromEntries(Object.entries(decoded));
25971
+ const issuer = stringClaim2(unverified.iss);
25972
+ if (issuer === undefined)
25973
+ return;
25974
+ const trusted = await resolveIssuer(issuer);
25975
+ if (trusted === undefined)
25976
+ return;
25977
+ const verified = await verifyJwt(assertion, trusted.publicJwk);
25978
+ if (verified?.header?.typ !== "oauth-id-jag+jwt")
25979
+ return;
25980
+ const { payload } = verified;
25981
+ const subject = stringClaim2(payload.sub);
25982
+ const jti = stringClaim2(payload.jti);
25983
+ const clientId = stringClaim2(payload.client_id);
25984
+ const expiresAtSeconds = numberClaim2(payload.exp);
25985
+ const issuedAtSeconds = numberClaim2(payload.iat);
25986
+ const authenticatedAtSeconds = numberClaim2(payload.auth_time);
25987
+ if (payload.iss !== issuer || payload.aud !== audience || subject === undefined || jti === undefined || clientId === undefined || expiresAtSeconds === undefined || issuedAtSeconds === undefined || authenticatedAtSeconds === undefined) {
25988
+ return;
25989
+ }
25990
+ const expiresAt = expiresAtSeconds * MILLISECONDS_IN_A_SECOND;
25991
+ const issuedAt = issuedAtSeconds * MILLISECONDS_IN_A_SECOND;
25992
+ const authenticatedAt = authenticatedAtSeconds * MILLISECONDS_IN_A_SECOND;
25993
+ if (expiresAt <= now - clockSkewMs || issuedAt > now + clockSkewMs || expiresAt <= issuedAt || expiresAt - issuedAt > maxAssertionLifetimeMs + clockSkewMs || authenticatedAt > now + clockSkewMs || authenticatedAt > issuedAt + clockSkewMs || now - authenticatedAt > maxAuthenticationAgeMs + clockSkewMs) {
25994
+ return;
25995
+ }
25996
+ if (trusted.allowedClientIds !== undefined && !trusted.allowedClientIds.includes(clientId)) {
25997
+ return;
25998
+ }
25999
+ const email = stringClaim2(payload.email);
26000
+ const phoneNumber = stringClaim2(payload.phone_number);
26001
+ const emailVerified = payload.email_verified === true;
26002
+ const phoneNumberVerified = payload.phone_number_verified === true;
26003
+ if (!emailVerified && !phoneNumberVerified)
26004
+ return;
26005
+ if (!await jtiStore.recordIfFresh(issuer, jti, expiresAt)) {
26006
+ return;
26007
+ }
26008
+ return {
26009
+ authenticatedAt,
26010
+ clientId,
26011
+ email,
26012
+ emailVerified,
26013
+ issuer,
26014
+ name: stringClaim2(payload.name),
26015
+ phoneNumber,
26016
+ phoneNumberVerified,
26017
+ subject
26018
+ };
26019
+ };
24969
26020
  // src/agents/oidcAdapter.ts
24970
26021
  var BEARER_PREFIX6 = "Bearer ";
24971
26022
  var MS_PER_SECOND7 = 1000;
@@ -25027,6 +26078,11 @@ var createOidcAgentCredentialVerifier = ({
25027
26078
  return verifier;
25028
26079
  };
25029
26080
  // src/agents/inMemoryStores.ts
26081
+ var cloneIdentityRegistration = (value) => ({
26082
+ ...value,
26083
+ claimAttempt: value.claimAttempt === undefined ? undefined : { ...value.claimAttempt },
26084
+ upstream: value.upstream === undefined ? undefined : { ...value.upstream }
26085
+ });
25030
26086
  var cloneRegistration = (value) => ({
25031
26087
  ...value,
25032
26088
  allowedScopes: [...value.allowedScopes],
@@ -25061,6 +26117,48 @@ var createInMemoryAgentDelegationStore = () => {
25061
26117
  }
25062
26118
  };
25063
26119
  };
26120
+ var createInMemoryAgentIdentityRegistrationStore = () => {
26121
+ const registrations = new Map;
26122
+ return {
26123
+ create: async (registration) => {
26124
+ const conflicts = [...registrations.values()].some((existing) => existing.registrationId === registration.registrationId || existing.agentId === registration.agentId || existing.claimTokenHash === registration.claimTokenHash || existing.claimAttempt !== undefined && existing.claimAttempt.tokenHash === registration.claimAttempt?.tokenHash || existing.upstream !== undefined && registration.upstream !== undefined && existing.upstream.clientId === registration.upstream.clientId && existing.upstream.issuer === registration.upstream.issuer && existing.upstream.subject === registration.upstream.subject);
26125
+ if (conflicts)
26126
+ return false;
26127
+ registrations.set(registration.registrationId, cloneIdentityRegistration(registration));
26128
+ return true;
26129
+ },
26130
+ findByAgentId: async (agentId) => {
26131
+ const value = [...registrations.values()].find((registration) => registration.agentId === agentId);
26132
+ return value === undefined ? undefined : cloneIdentityRegistration(value);
26133
+ },
26134
+ findByAttemptTokenHash: async (attemptTokenHash) => {
26135
+ const value = [...registrations.values()].find((registration) => registration.claimAttempt?.tokenHash === attemptTokenHash);
26136
+ return value === undefined ? undefined : cloneIdentityRegistration(value);
26137
+ },
26138
+ findByClaimTokenHash: async (claimTokenHash) => {
26139
+ const value = [...registrations.values()].find((registration) => registration.claimTokenHash === claimTokenHash);
26140
+ return value === undefined ? undefined : cloneIdentityRegistration(value);
26141
+ },
26142
+ findByRegistrationId: async (registrationId) => {
26143
+ const value = registrations.get(registrationId);
26144
+ return value === undefined ? undefined : cloneIdentityRegistration(value);
26145
+ },
26146
+ findByUpstreamIdentity: async ({ clientId, issuer, subject }) => {
26147
+ const value = [...registrations.values()].find((registration) => registration.upstream?.clientId === clientId && registration.upstream?.issuer === issuer && registration.upstream.subject === subject);
26148
+ return value === undefined ? undefined : cloneIdentityRegistration(value);
26149
+ },
26150
+ replace: async (registration, expectedVersion) => {
26151
+ const current = registrations.get(registration.registrationId);
26152
+ if (current?.version !== expectedVersion)
26153
+ return false;
26154
+ registrations.set(registration.registrationId, cloneIdentityRegistration({
26155
+ ...registration,
26156
+ version: expectedVersion + 1
26157
+ }));
26158
+ return true;
26159
+ }
26160
+ };
26161
+ };
25064
26162
  var createInMemoryAgentRegistrationStore = () => {
25065
26163
  const registrations = new Map;
25066
26164
  return {
@@ -25096,6 +26194,38 @@ var agentDelegationsTable = pgTable("auth_agent_delegations", {
25096
26194
  updated_at_ms: bigint("updated_at_ms", { mode: "number" }).notNull(),
25097
26195
  user_id: varchar("user_id", { length: ID_LENGTH7 }).notNull()
25098
26196
  });
26197
+ var agentIdentityRegistrationsTable = pgTable("auth_agent_identity_registrations", {
26198
+ agent_id: varchar("agent_id", { length: ID_LENGTH7 }).notNull().unique(),
26199
+ claim_attempt: jsonb("claim_attempt").$type(),
26200
+ claim_attempt_token_hash: varchar("claim_attempt_token_hash", {
26201
+ length: ID_LENGTH7
26202
+ }).unique(),
26203
+ claim_expires_at_ms: bigint("claim_expires_at_ms", {
26204
+ mode: "number"
26205
+ }).notNull(),
26206
+ claim_token_hash: varchar("claim_token_hash", {
26207
+ length: ID_LENGTH7
26208
+ }).notNull().unique(),
26209
+ created_at_ms: bigint("created_at_ms", { mode: "number" }).notNull(),
26210
+ expires_at_ms: bigint("expires_at_ms", { mode: "number" }).notNull(),
26211
+ kind: varchar("kind", { length: 32 }).$type().notNull(),
26212
+ last_polled_at_ms: bigint("last_polled_at_ms", { mode: "number" }),
26213
+ login_hint: varchar("login_hint", { length: ID_LENGTH7 }),
26214
+ registration_id: varchar("registration_id", {
26215
+ length: ID_LENGTH7
26216
+ }).primaryKey(),
26217
+ status: varchar("status", { length: STATUS_LENGTH2 }).$type().notNull(),
26218
+ updated_at_ms: bigint("updated_at_ms", { mode: "number" }).notNull(),
26219
+ upstream_client_id: varchar("upstream_client_id", {
26220
+ length: ID_LENGTH7
26221
+ }),
26222
+ upstream_issuer: varchar("upstream_issuer", { length: ID_LENGTH7 }),
26223
+ upstream_subject: varchar("upstream_subject", { length: ID_LENGTH7 }),
26224
+ user_id: varchar("user_id", { length: ID_LENGTH7 }),
26225
+ version: integer("version").notNull()
26226
+ }, (table) => [
26227
+ uniqueIndex("auth_agent_identity_upstream_unique").on(table.upstream_issuer, table.upstream_subject, table.upstream_client_id)
26228
+ ]);
25099
26229
  var agentRegistrationsTable = pgTable("auth_agent_registrations", {
25100
26230
  agent_id: varchar("agent_id", { length: ID_LENGTH7 }).primaryKey(),
25101
26231
  allowed_scopes: jsonb("allowed_scopes").$type().notNull().default([]),
@@ -25128,7 +26258,49 @@ var toDelegation = (row) => ({
25128
26258
  updatedAt: row.updated_at_ms,
25129
26259
  userId: row.user_id
25130
26260
  });
26261
+ var toIdentityRegistration = (row) => ({
26262
+ agentId: row.agent_id,
26263
+ claimAttempt: row.claim_attempt ?? undefined,
26264
+ claimExpiresAt: row.claim_expires_at_ms,
26265
+ claimTokenHash: row.claim_token_hash,
26266
+ createdAt: row.created_at_ms,
26267
+ expiresAt: row.expires_at_ms,
26268
+ kind: row.kind,
26269
+ lastPolledAt: row.last_polled_at_ms ?? undefined,
26270
+ loginHint: row.login_hint ?? undefined,
26271
+ registrationId: row.registration_id,
26272
+ status: row.status,
26273
+ updatedAt: row.updated_at_ms,
26274
+ upstream: row.upstream_client_id === null || row.upstream_issuer === null || row.upstream_subject === null ? undefined : {
26275
+ clientId: row.upstream_client_id,
26276
+ issuer: row.upstream_issuer,
26277
+ subject: row.upstream_subject
26278
+ },
26279
+ userId: row.user_id ?? undefined,
26280
+ version: row.version
26281
+ });
26282
+ var identityRegistrationValues = (registration) => ({
26283
+ agent_id: registration.agentId,
26284
+ claim_attempt: registration.claimAttempt ?? null,
26285
+ claim_attempt_token_hash: registration.claimAttempt?.tokenHash ?? null,
26286
+ claim_expires_at_ms: registration.claimExpiresAt,
26287
+ claim_token_hash: registration.claimTokenHash,
26288
+ created_at_ms: registration.createdAt,
26289
+ expires_at_ms: registration.expiresAt,
26290
+ kind: registration.kind,
26291
+ last_polled_at_ms: registration.lastPolledAt ?? null,
26292
+ login_hint: registration.loginHint ?? null,
26293
+ registration_id: registration.registrationId,
26294
+ status: registration.status,
26295
+ updated_at_ms: registration.updatedAt,
26296
+ upstream_client_id: registration.upstream?.clientId ?? null,
26297
+ upstream_issuer: registration.upstream?.issuer ?? null,
26298
+ upstream_subject: registration.upstream?.subject ?? null,
26299
+ user_id: registration.userId ?? null,
26300
+ version: registration.version
26301
+ });
25131
26302
  var createNeonAgentDelegationStore = (databaseUrl) => createPostgresAgentDelegationStore(createNeonDatabase(databaseUrl));
26303
+ var createNeonAgentIdentityRegistrationStore = (databaseUrl) => createPostgresAgentIdentityRegistrationStore(createNeonDatabase(databaseUrl));
25132
26304
  var createNeonAgentRegistrationStore = (databaseUrl) => createPostgresAgentRegistrationStore(createNeonDatabase(databaseUrl));
25133
26305
  var createPostgresAgentDelegationStore = (db) => ({
25134
26306
  findActiveDelegation: async ({
@@ -25169,6 +26341,41 @@ var createPostgresAgentDelegationStore = (db) => ({
25169
26341
  });
25170
26342
  }
25171
26343
  });
26344
+ var createPostgresAgentIdentityRegistrationStore = (db) => ({
26345
+ create: async (registration) => {
26346
+ const rows = await db.insert(agentIdentityRegistrationsTable).values(identityRegistrationValues(registration)).onConflictDoNothing().returning({ id: agentIdentityRegistrationsTable.registration_id });
26347
+ return rows.length === 1;
26348
+ },
26349
+ findByAgentId: async (agentId) => {
26350
+ const [row] = await db.select().from(agentIdentityRegistrationsTable).where(eq(agentIdentityRegistrationsTable.agent_id, agentId)).limit(1);
26351
+ return row === undefined ? undefined : toIdentityRegistration(row);
26352
+ },
26353
+ findByAttemptTokenHash: async (attemptTokenHash) => {
26354
+ const [row] = await db.select().from(agentIdentityRegistrationsTable).where(eq(agentIdentityRegistrationsTable.claim_attempt_token_hash, attemptTokenHash)).limit(1);
26355
+ return row === undefined ? undefined : toIdentityRegistration(row);
26356
+ },
26357
+ findByClaimTokenHash: async (claimTokenHash) => {
26358
+ const [row] = await db.select().from(agentIdentityRegistrationsTable).where(eq(agentIdentityRegistrationsTable.claim_token_hash, claimTokenHash)).limit(1);
26359
+ return row === undefined ? undefined : toIdentityRegistration(row);
26360
+ },
26361
+ findByRegistrationId: async (registrationId) => {
26362
+ const [row] = await db.select().from(agentIdentityRegistrationsTable).where(eq(agentIdentityRegistrationsTable.registration_id, registrationId)).limit(1);
26363
+ return row === undefined ? undefined : toIdentityRegistration(row);
26364
+ },
26365
+ findByUpstreamIdentity: async ({ clientId, issuer, subject }) => {
26366
+ const [row] = await db.select().from(agentIdentityRegistrationsTable).where(and(eq(agentIdentityRegistrationsTable.upstream_client_id, clientId), eq(agentIdentityRegistrationsTable.upstream_issuer, issuer), eq(agentIdentityRegistrationsTable.upstream_subject, subject))).limit(1);
26367
+ return row === undefined ? undefined : toIdentityRegistration(row);
26368
+ },
26369
+ replace: async (registration, expectedVersion) => {
26370
+ const next = {
26371
+ ...registration,
26372
+ version: expectedVersion + 1
26373
+ };
26374
+ const values = identityRegistrationValues(next);
26375
+ const rows = await db.update(agentIdentityRegistrationsTable).set(values).where(and(eq(agentIdentityRegistrationsTable.registration_id, registration.registrationId), eq(agentIdentityRegistrationsTable.version, expectedVersion))).returning({ id: agentIdentityRegistrationsTable.registration_id });
26376
+ return rows.length === 1;
26377
+ }
26378
+ });
25172
26379
  var createPostgresAgentRegistrationStore = (db) => ({
25173
26380
  findByAgentId: async (agentId) => {
25174
26381
  const [row] = await db.select().from(agentRegistrationsTable).where(eq(agentRegistrationsTable.agent_id, agentId)).limit(1);
@@ -25402,7 +26609,7 @@ var createPostgresApiKeyStore = (db) => ({
25402
26609
  }
25403
26610
  });
25404
26611
  // src/oidc/clientIdMetadata.ts
25405
- var secureUrl = (value) => {
26612
+ var secureUrl2 = (value) => {
25406
26613
  try {
25407
26614
  return new URL(value).protocol === "https:";
25408
26615
  } catch {
@@ -25413,7 +26620,7 @@ var validateClientIdMetadataDocument = (document, expectedClientId) => {
25413
26620
  const errors = [];
25414
26621
  if (document.client_id !== expectedClientId)
25415
26622
  errors.push("client_id does not match the metadata document URL");
25416
- if (!secureUrl(document.client_id))
26623
+ if (!secureUrl2(document.client_id))
25417
26624
  errors.push("client_id must use HTTPS");
25418
26625
  if (!Array.isArray(document.redirect_uris) || document.redirect_uris.length === 0)
25419
26626
  errors.push("redirect_uris is required");
@@ -25433,7 +26640,7 @@ var validateClientIdMetadataDocument = (document, expectedClientId) => {
25433
26640
  ["tos_uri", document.tos_uri],
25434
26641
  ["jwks_uri", document.jwks_uri]
25435
26642
  ]) {
25436
- if (value !== undefined && !secureUrl(value))
26643
+ if (value !== undefined && !secureUrl2(value))
25437
26644
  errors.push(`${name2} must use HTTPS`);
25438
26645
  }
25439
26646
  return errors;
@@ -25456,7 +26663,7 @@ var createClientIdMetadataResolver = ({
25456
26663
  }) => {
25457
26664
  const cache = new Map;
25458
26665
  return async (clientId) => {
25459
- if (!secureUrl(clientId) || !await allow(clientId))
26666
+ if (!secureUrl2(clientId) || !await allow(clientId))
25460
26667
  return;
25461
26668
  const cached = cache.get(clientId);
25462
26669
  if (cached !== undefined && cached.expiresAt > now())
@@ -27368,10 +28575,20 @@ var mfaTotpLockoutMigration = {
27368
28575
  };
27369
28576
  var blockMigrations = {
27370
28577
  adaptive: initMigration("adaptive", [knownDevicesTable, loginHistoryTable]),
27371
- agents: initMigration("agents", [
27372
- agentRegistrationsTable,
27373
- agentDelegationsTable
27374
- ]),
28578
+ agents: {
28579
+ block: "agents",
28580
+ migrations: [
28581
+ ...initMigration("agents", [
28582
+ agentRegistrationsTable,
28583
+ agentDelegationsTable,
28584
+ agentIdentityRegistrationsTable
28585
+ ]).migrations,
28586
+ {
28587
+ id: "0002_identity_registration",
28588
+ sql: tablesToInitSql([agentIdentityRegistrationsTable])
28589
+ }
28590
+ ]
28591
+ },
27375
28592
  apikeys: initMigration("apikeys", [
27376
28593
  accessTokensTable,
27377
28594
  apiClientsTable,
@@ -27434,17 +28651,17 @@ var blockMigrations = {
27434
28651
  };
27435
28652
  // src/sso/samlIdpRoutes.ts
27436
28653
  import { Elysia as Elysia41, t as t36 } from "elysia";
27437
- var HTTP_BAD_REQUEST5 = 400;
27438
- var HTTP_UNAUTHORIZED4 = 401;
28654
+ var HTTP_BAD_REQUEST6 = 400;
28655
+ var HTTP_UNAUTHORIZED5 = 401;
27439
28656
  var HTTP_FOUND2 = 302;
27440
- var HTTP_OK6 = 200;
28657
+ var HTTP_OK7 = 200;
27441
28658
  var xmlResponse = (body) => new Response(body, {
27442
28659
  headers: { "content-type": "application/samlmetadata+xml" },
27443
- status: HTTP_OK6
28660
+ status: HTTP_OK7
27444
28661
  });
27445
28662
  var htmlResponse = (body) => new Response(body, {
27446
28663
  headers: { "content-type": "text/html; charset=utf-8" },
27447
- status: HTTP_OK6
28664
+ status: HTTP_OK7
27448
28665
  });
27449
28666
  var redirectTo2 = (url) => new Response(null, { headers: { location: url }, status: HTTP_FOUND2 });
27450
28667
  var errorJson = (status, error) => new Response(JSON.stringify({ error }), {
@@ -27496,7 +28713,7 @@ var samlIdpRoutes = ({
27496
28713
  userSessionIdValue
27497
28714
  }) => {
27498
28715
  if (body.SAMLRequest === undefined) {
27499
- return errorJson(HTTP_BAD_REQUEST5, "missing_saml_request");
28716
+ return errorJson(HTTP_BAD_REQUEST6, "missing_saml_request");
27500
28717
  }
27501
28718
  let firstPass;
27502
28719
  try {
@@ -27505,11 +28722,11 @@ var samlIdpRoutes = ({
27505
28722
  samlRequest: body.SAMLRequest
27506
28723
  });
27507
28724
  } catch {
27508
- return errorJson(HTTP_BAD_REQUEST5, "invalid_authn_request");
28725
+ return errorJson(HTTP_BAD_REQUEST6, "invalid_authn_request");
27509
28726
  }
27510
28727
  const serviceProvider = await samlServiceProviderStore.findServiceProvider(firstPass.issuer);
27511
28728
  if (serviceProvider === undefined) {
27512
- return errorJson(HTTP_BAD_REQUEST5, "unknown_service_provider");
28729
+ return errorJson(HTTP_BAD_REQUEST6, "unknown_service_provider");
27513
28730
  }
27514
28731
  let parsed;
27515
28732
  try {
@@ -27522,7 +28739,7 @@ var samlIdpRoutes = ({
27522
28739
  signedQueryString: binding === "Redirect" ? new URL(request.url).search.slice(1) : undefined
27523
28740
  });
27524
28741
  } catch {
27525
- return errorJson(HTTP_BAD_REQUEST5, "invalid_authn_request");
28742
+ return errorJson(HTTP_BAD_REQUEST6, "invalid_authn_request");
27526
28743
  }
27527
28744
  const userSession = await loadSessionFromSource({
27528
28745
  authSessionStore,
@@ -27531,7 +28748,7 @@ var samlIdpRoutes = ({
27531
28748
  });
27532
28749
  if (userSession === undefined || parsed.forceAuthn === true) {
27533
28750
  if (loginUrl === undefined) {
27534
- return errorJson(HTTP_UNAUTHORIZED4, "login_required");
28751
+ return errorJson(HTTP_UNAUTHORIZED5, "login_required");
27535
28752
  }
27536
28753
  return redirectTo2(`${loginUrl}?return_to=${encodeURIComponent(request.url)}`);
27537
28754
  }
@@ -27580,11 +28797,11 @@ var samlIdpRoutes = ({
27580
28797
  store
27581
28798
  }) => {
27582
28799
  if (serviceProviderEntityId === undefined) {
27583
- return errorJson(HTTP_BAD_REQUEST5, "missing_sp");
28800
+ return errorJson(HTTP_BAD_REQUEST6, "missing_sp");
27584
28801
  }
27585
28802
  const serviceProvider = await samlServiceProviderStore.findServiceProvider(serviceProviderEntityId);
27586
28803
  if (serviceProvider === undefined) {
27587
- return errorJson(HTTP_BAD_REQUEST5, "unknown_service_provider");
28804
+ return errorJson(HTTP_BAD_REQUEST6, "unknown_service_provider");
27588
28805
  }
27589
28806
  const userSession = authSessionStore === undefined ? await loadSessionFromSource({
27590
28807
  session: store.session,
@@ -27596,7 +28813,7 @@ var samlIdpRoutes = ({
27596
28813
  });
27597
28814
  if (userSession === undefined) {
27598
28815
  if (loginUrl === undefined) {
27599
- return errorJson(HTTP_UNAUTHORIZED4, "login_required");
28816
+ return errorJson(HTTP_UNAUTHORIZED5, "login_required");
27600
28817
  }
27601
28818
  return redirectTo2(`${loginUrl}?return_to=${encodeURIComponent(request.url)}`);
27602
28819
  }
@@ -27893,6 +29110,38 @@ var auth = async ({
27893
29110
  onRevocationError,
27894
29111
  onSessionCleanup
27895
29112
  }) => {
29113
+ if (agentAuth?.agentRegistration !== undefined) {
29114
+ if (oidc === undefined) {
29115
+ throw new Error("agentAuth.agentRegistration requires the OIDC provider");
29116
+ }
29117
+ if (agentAuth.authorizationServer !== oidc.issuer) {
29118
+ throw new Error("agentAuth.authorizationServer must equal oidc.issuer");
29119
+ }
29120
+ const registration2 = agentAuth.agentRegistration;
29121
+ const unknownScopes = [
29122
+ ...registration2.preClaimScopes ?? [],
29123
+ ...registration2.postClaimScopes
29124
+ ].filter((scope) => !agentAuth.scopes.includes(scope));
29125
+ if (unknownScopes.length > 0) {
29126
+ throw new Error(`Agent registration uses undeclared scopes: ${[...new Set(unknownScopes)].join(", ")}`);
29127
+ }
29128
+ if (registration2.allowAnonymous === true && registration2.revokeAccessTokens === undefined) {
29129
+ throw new Error("Anonymous agent registration requires revokeAccessTokens");
29130
+ }
29131
+ for (const [name2, value] of Object.entries({
29132
+ assertionTtlMs: registration2.assertionTtlMs,
29133
+ attemptTtlMs: registration2.attemptTtlMs,
29134
+ claimTtlMs: registration2.claimTtlMs,
29135
+ maxAuthenticationAgeMs: registration2.maxAuthenticationAgeMs,
29136
+ maxCodeAttempts: registration2.maxCodeAttempts,
29137
+ pollIntervalSeconds: registration2.pollIntervalSeconds,
29138
+ tokenTtlMs: registration2.tokenTtlMs
29139
+ })) {
29140
+ if (value !== undefined && (!Number.isFinite(value) || value <= 0)) {
29141
+ throw new Error(`agentAuth.agentRegistration.${name2} must be positive`);
29142
+ }
29143
+ }
29144
+ }
27896
29145
  if (tracing !== undefined)
27897
29146
  await initTracing(tracing);
27898
29147
  const clientProviders = await buildClientProviders(providersConfiguration, createOAuth2Client, customProviders);
@@ -27906,8 +29155,34 @@ var auth = async ({
27906
29155
  }
27907
29156
  }) : undefined;
27908
29157
  const lockoutGuard = lockout ? createLockoutGuard(lockout) : undefined;
29158
+ const resolvedAgentAuth = agentAuth === undefined ? undefined : {
29159
+ ...agentAuth,
29160
+ oidcRoute: agentAuth.oidcRoute ?? oidc?.oidcRoute
29161
+ };
27909
29162
  const oidcConfig = oidc ? {
27910
29163
  ...oidc,
29164
+ additionalDiscoveryMetadata: {
29165
+ ...oidc.additionalDiscoveryMetadata,
29166
+ ...resolvedAgentAuth?.agentRegistration === undefined ? {} : {
29167
+ agent_auth: agentRegistrationDiscoveryMetadata(resolvedAgentAuth)
29168
+ }
29169
+ },
29170
+ publicTokenGrantTypes: [
29171
+ ...oidc.publicTokenGrantTypes ?? [],
29172
+ ...resolvedAgentAuth?.agentRegistration === undefined ? [] : [
29173
+ AGENT_CLAIM_GRANT_TYPE,
29174
+ AGENT_IDENTITY_ASSERTION_GRANT_TYPE
29175
+ ]
29176
+ ],
29177
+ handlePublicTokenGrant: async (context) => {
29178
+ const consumerResult = await oidc.handlePublicTokenGrant?.(context);
29179
+ if (consumerResult !== undefined)
29180
+ return consumerResult;
29181
+ if (resolvedAgentAuth?.agentRegistration === undefined) {
29182
+ return;
29183
+ }
29184
+ return handleAgentTokenGrant(resolvedAgentAuth, context.body);
29185
+ },
27911
29186
  onClientRegistered: async (context) => {
27912
29187
  await oidc.onClientRegistered?.(context);
27913
29188
  if (agentAuth?.registerDynamicClients !== true)
@@ -27932,28 +29207,28 @@ var auth = async ({
27932
29207
  await oidc.onDeviceAuthorizationApproved?.(context);
27933
29208
  if (agentAuth === undefined)
27934
29209
  return;
27935
- const registration = await agentAuth.registrationStore.findByClientId(context.clientId);
27936
- if (registration === undefined || registration.status !== "active") {
29210
+ const registration2 = await agentAuth.registrationStore.findByClientId(context.clientId);
29211
+ if (registration2 === undefined || registration2.status !== "active") {
27937
29212
  return;
27938
29213
  }
27939
29214
  const now = Date.now();
27940
29215
  const existing = await agentAuth.delegationStore.findActiveDelegation({
27941
- agentId: registration.agentId,
29216
+ agentId: registration2.agentId,
27942
29217
  now,
27943
29218
  userId: context.userSub
27944
29219
  });
27945
29220
  await agentAuth.delegationStore.saveDelegation({
27946
- agentId: registration.agentId,
29221
+ agentId: registration2.agentId,
27947
29222
  createdAt: existing?.createdAt ?? now,
27948
29223
  delegationId: existing?.delegationId ?? `agd_${crypto.randomUUID()}`,
27949
- scopes: context.scopes.filter((scope) => registration.allowedScopes.includes(scope) && agentAuth.scopes.includes(scope)),
29224
+ scopes: context.scopes.filter((scope) => registration2.allowedScopes.includes(scope) && agentAuth.scopes.includes(scope)),
27950
29225
  status: "active",
27951
29226
  updatedAt: now,
27952
29227
  userId: context.userSub
27953
29228
  });
27954
29229
  await auditEmit?.({
27955
29230
  at: now,
27956
- metadata: { agentId: registration.agentId },
29231
+ metadata: { agentId: registration2.agentId },
27957
29232
  type: "agent_delegated",
27958
29233
  userId: context.userSub
27959
29234
  });
@@ -28066,7 +29341,7 @@ var auth = async ({
28066
29341
  ...htmx,
28067
29342
  authSessionStore
28068
29343
  }) : new Elysia42);
28069
- const authWithAgent = composedAuth.use(agentAuthPlugin(agentAuth));
29344
+ const authWithAgent = composedAuth.use(agentAuthPlugin(resolvedAgentAuth));
28070
29345
  return authWithAgent;
28071
29346
  };
28072
29347
  export {
@@ -28118,6 +29393,7 @@ export {
28118
29393
  stepUpPlugin,
28119
29394
  statusListRoutes,
28120
29395
  startImpersonation,
29396
+ startAgentRegistration,
28121
29397
  ssoDiscoveryRoute,
28122
29398
  ssoConnectionsTable,
28123
29399
  signWebhook,
@@ -28144,6 +29420,7 @@ export {
28144
29420
  roleRoutes,
28145
29421
  revokeUserSessions,
28146
29422
  revokeRefreshToken,
29423
+ revokeAgentIdentityRegistration,
28147
29424
  revocableProviderOptions,
28148
29425
  resolveSetupSession,
28149
29426
  resolveScimOrganization,
@@ -28223,6 +29500,8 @@ export {
28223
29500
  issueDeviceAuthorization,
28224
29501
  issueCredential,
28225
29502
  issueBackchannelAuth,
29503
+ issueAgentServiceAssertion,
29504
+ issueAgentIdentityAssertion,
28226
29505
  isValidUser,
28227
29506
  isValidProviderOption,
28228
29507
  isUserSessionId,
@@ -28251,6 +29530,7 @@ export {
28251
29530
  hashAuditEvent,
28252
29531
  hasScopes,
28253
29532
  hasOrganizationScope,
29533
+ handleAgentTokenGrant,
28254
29534
  getUserSessionId,
28255
29535
  getStatus,
28256
29536
  getRegisteredClient,
@@ -28262,6 +29542,7 @@ export {
28262
29542
  generateSecureToken,
28263
29543
  generateEncryptionKey,
28264
29544
  generateBackupCodes,
29545
+ generateAgentRegistrationGuide,
28265
29546
  fromBase64Url2 as fromBase64Url,
28266
29547
  fingerprintDevice,
28267
29548
  fetchUserInfo,
@@ -28279,6 +29560,7 @@ export {
28279
29560
  endImpersonation,
28280
29561
  encryptTotpSecret,
28281
29562
  encryptSecret,
29563
+ discoverAgentRegistration,
28282
29564
  diffScimGroupMembers,
28283
29565
  denyDeviceAuthorization,
28284
29566
  denyBackchannelAuth,
@@ -28349,6 +29631,7 @@ export {
28349
29631
  createPostgresApiKeyStore,
28350
29632
  createPostgresApiClientStore,
28351
29633
  createPostgresAgentRegistrationStore,
29634
+ createPostgresAgentIdentityRegistrationStore,
28352
29635
  createPostgresAgentDelegationStore,
28353
29636
  createPostgresAccessTokenStore,
28354
29637
  createOrganization,
@@ -28391,6 +29674,7 @@ export {
28391
29674
  createNeonApiKeyStore,
28392
29675
  createNeonApiClientStore,
28393
29676
  createNeonAgentRegistrationStore,
29677
+ createNeonAgentIdentityRegistrationStore,
28394
29678
  createNeonAgentDelegationStore,
28395
29679
  createNeonAccessTokenStore,
28396
29680
  createMfaGate,
@@ -28433,6 +29717,8 @@ export {
28433
29717
  createInMemoryApiKeyStore,
28434
29718
  createInMemoryApiClientStore,
28435
29719
  createInMemoryAgentRegistrationStore,
29720
+ createInMemoryAgentIdentityRegistrationStore,
29721
+ createInMemoryAgentIdentityAssertionJtiStore,
28436
29722
  createInMemoryAgentDelegationStore,
28437
29723
  createInMemoryAccessTokenStore,
28438
29724
  createFgaEngine,
@@ -28446,6 +29732,9 @@ export {
28446
29732
  createApiKey,
28447
29733
  createApiClient,
28448
29734
  createAnonymousSession,
29735
+ createAgentRegistrationCredentialVerifier,
29736
+ createAgentRegistrationClient,
29737
+ createAgentIdentityAssertionVerifier,
28449
29738
  createActionPipeline,
28450
29739
  createAbuseGuard,
28451
29740
  consumePushedRequest,
@@ -28453,6 +29742,7 @@ export {
28453
29742
  constantTimeEqual,
28454
29743
  computeCertThumbprint,
28455
29744
  complianceRoutes,
29745
+ completeAgentClaim,
28456
29746
  clientIdMetadataToOAuthClient,
28457
29747
  check2 as check,
28458
29748
  buildStatusClaim,
@@ -28460,6 +29750,7 @@ export {
28460
29750
  buildHolderKeyBindingJwt,
28461
29751
  buildClientProviders,
28462
29752
  blockMigrations,
29753
+ beginAgentClaim,
28463
29754
  base32Encode,
28464
29755
  base32Decode,
28465
29756
  authProviderOption,
@@ -28475,7 +29766,10 @@ export {
28475
29766
  apiKeysRoutes,
28476
29767
  apiClientsTable,
28477
29768
  agentRegistrationsTable,
29769
+ agentRegistrationEndpoints,
29770
+ agentRegistrationDiscoveryMetadata,
28478
29771
  agentProtectedResourceMetadata,
29772
+ agentIdentityRegistrationsTable,
28479
29773
  agentHasScopes,
28480
29774
  agentDelegationsTable,
28481
29775
  agentAuthPlugin,
@@ -28526,8 +29820,11 @@ export {
28526
29820
  DEFAULT_AGENT_RESOURCE_METADATA_ROUTE,
28527
29821
  CLIENT_ASSERTION_TYPE,
28528
29822
  CIBA_GRANT_TYPE,
28529
- AuthIdentityConflictError
29823
+ AuthIdentityConflictError,
29824
+ AGENT_IDENTITY_ASSERTION_TYPE,
29825
+ AGENT_IDENTITY_ASSERTION_GRANT_TYPE,
29826
+ AGENT_CLAIM_GRANT_TYPE
28530
29827
  };
28531
29828
 
28532
- //# debugId=D8A79A996B677FF164756E2164756E21
29829
+ //# debugId=A2D2B9523667A9C564756E2164756E21
28533
29830
  //# sourceMappingURL=index.js.map