@absolutejs/auth 0.78.0 → 0.79.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3027,7 +3027,7 @@ import { Elysia, t } from "elysia";
3027
3027
  // src/apikeys/config.ts
3028
3028
  init_constants();
3029
3029
  init_crypto();
3030
- var DEFAULT_TOKEN_ROUTE = "/oauth2/token";
3030
+ var DEFAULT_TOKEN_ROUTE = "/auth/api/token";
3031
3031
  var ACCESS_TOKEN_PREFIX = "at_";
3032
3032
  var API_KEY_PREFIX = "sk_";
3033
3033
  var BEARER_PREFIX = "Bearer ";
@@ -3239,6 +3239,602 @@ var apiKeysRoutes = ({
3239
3239
  });
3240
3240
  };
3241
3241
 
3242
+ // src/oidc/config.ts
3243
+ init_constants();
3244
+ init_crypto();
3245
+
3246
+ // src/oidc/keys.ts
3247
+ var ENCODER = new TextEncoder;
3248
+ var ES256 = { hash: "SHA-256", name: "ECDSA" };
3249
+ var KEY_PARAMS = { name: "ECDSA", namedCurve: "P-256" };
3250
+ var ES256_JOSE_SIGNATURE_BYTES = 64;
3251
+ var toBase64Url = (bytes) => Buffer.from(bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)).toString("base64url");
3252
+ var fromBase64Url = (value) => new Uint8Array(Buffer.from(value, "base64url"));
3253
+ var encodeSegment = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
3254
+ var decodeSegment = (segment) => {
3255
+ try {
3256
+ const value = JSON.parse(Buffer.from(segment, "base64url").toString("utf8"));
3257
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
3258
+ return;
3259
+ }
3260
+ return Object.fromEntries(Object.entries(value));
3261
+ } catch {
3262
+ return;
3263
+ }
3264
+ };
3265
+ var generateSigningKey = async () => {
3266
+ const pair = await crypto.subtle.generateKey(KEY_PARAMS, true, [
3267
+ "sign",
3268
+ "verify"
3269
+ ]);
3270
+ const privateJwk = await crypto.subtle.exportKey("jwk", pair.privateKey);
3271
+ const publicJwk = await crypto.subtle.exportKey("jwk", pair.publicKey);
3272
+ return { kid: await jwkThumbprint(publicJwk), privateJwk, publicJwk };
3273
+ };
3274
+ var jwkThumbprint = async (jwk) => {
3275
+ const canonical = JSON.stringify({
3276
+ crv: jwk.crv,
3277
+ kty: jwk.kty,
3278
+ x: jwk.x,
3279
+ y: jwk.y
3280
+ });
3281
+ return toBase64Url(await crypto.subtle.digest("SHA-256", ENCODER.encode(canonical)));
3282
+ };
3283
+ var signJwt = async (payload, signing, typ = "JWT") => {
3284
+ const input = `${encodeSegment({ alg: "ES256", kid: signing.kid, typ })}.${encodeSegment(payload)}`;
3285
+ const encoded = ENCODER.encode(input);
3286
+ let signature;
3287
+ if (signing.sign !== undefined) {
3288
+ signature = await signing.sign(encoded);
3289
+ } else {
3290
+ const key = await crypto.subtle.importKey("jwk", signing.privateJwk, KEY_PARAMS, false, ["sign"]);
3291
+ signature = await crypto.subtle.sign(ES256, key, encoded);
3292
+ }
3293
+ if (signature.byteLength !== ES256_JOSE_SIGNATURE_BYTES) {
3294
+ throw new Error("ES256 signer must return a 64-byte JOSE signature");
3295
+ }
3296
+ return `${input}.${toBase64Url(signature)}`;
3297
+ };
3298
+ var toPublicJwk = (key) => ({
3299
+ alg: "ES256",
3300
+ crv: key.publicJwk.crv,
3301
+ kid: key.kid,
3302
+ kty: key.publicJwk.kty,
3303
+ use: "sig",
3304
+ x: key.publicJwk.x,
3305
+ y: key.publicJwk.y
3306
+ });
3307
+ var verifyJwt = async (token, publicJwk) => {
3308
+ const [headerSegment, payloadSegment, signatureSegment] = token.split(".");
3309
+ if (headerSegment === undefined || payloadSegment === undefined || signatureSegment === undefined) {
3310
+ return;
3311
+ }
3312
+ const key = await crypto.subtle.importKey("jwk", publicJwk, KEY_PARAMS, false, ["verify"]);
3313
+ const valid = await crypto.subtle.verify(ES256, key, fromBase64Url(signatureSegment), ENCODER.encode(`${headerSegment}.${payloadSegment}`));
3314
+ if (!valid)
3315
+ return;
3316
+ const header = decodeSegment(headerSegment);
3317
+ const payload = decodeSegment(payloadSegment);
3318
+ if (header === undefined || payload === undefined)
3319
+ return;
3320
+ return {
3321
+ header,
3322
+ payload
3323
+ };
3324
+ };
3325
+ var signingVerificationKeys = (active, previous = []) => {
3326
+ const keys = [active, ...previous];
3327
+ const keyIds = new Set(keys.map(({ kid }) => kid));
3328
+ if (keyIds.size !== keys.length)
3329
+ throw new Error("OIDC signing key IDs must be unique");
3330
+ return keys;
3331
+ };
3332
+ var verifyJwtWithKeys = async (token, keys) => {
3333
+ const [headerSegment] = token.split(".");
3334
+ if (!headerSegment)
3335
+ return;
3336
+ const header = decodeSegment(headerSegment);
3337
+ const kid = header?.kid;
3338
+ if (typeof kid !== "string" || kid.length === 0)
3339
+ return;
3340
+ const key = keys.find((candidate) => candidate.kid === kid);
3341
+ if (!key)
3342
+ return;
3343
+ return verifyJwt(token, key.publicJwk);
3344
+ };
3345
+
3346
+ // src/oidc/config.ts
3347
+ var DEFAULT_OIDC_ROUTE = "/oauth2";
3348
+ var MS_PER_SECOND = 1000;
3349
+ var TOKEN_BYTES2 = 32;
3350
+ var REFRESH_TTL_DAYS = 30;
3351
+ var DEFAULT_ACCESS_TOKEN_TTL_MS2 = MILLISECONDS_IN_AN_HOUR;
3352
+ var DEFAULT_ID_TOKEN_TTL_MS = MILLISECONDS_IN_AN_HOUR;
3353
+ var DEFAULT_REFRESH_TOKEN_TTL_MS = MILLISECONDS_IN_A_DAY * REFRESH_TTL_DAYS;
3354
+ var resolveAccessTtl = (ttl, scopes) => {
3355
+ if (typeof ttl === "function")
3356
+ return ttl({ scopes });
3357
+ return ttl ?? DEFAULT_ACCESS_TOKEN_TTL_MS2;
3358
+ };
3359
+ var nowSeconds = (milliseconds) => Math.floor(milliseconds / MS_PER_SECOND);
3360
+ var narrowScopes = (available, requested) => requested === undefined || requested.length === 0 ? available : requested.filter((scope) => available.includes(scope));
3361
+ var RESERVED_ACCESS_CLAIMS = new Set([
3362
+ "act",
3363
+ "aud",
3364
+ "client_id",
3365
+ "cnf",
3366
+ "exp",
3367
+ "iat",
3368
+ "iss",
3369
+ "jti",
3370
+ "scope",
3371
+ "sub",
3372
+ "token_use"
3373
+ ]);
3374
+ var buildAccessClaims = ({
3375
+ act,
3376
+ audience,
3377
+ clientCertThumbprint,
3378
+ clientId,
3379
+ dpopJkt,
3380
+ extraClaims,
3381
+ issuer,
3382
+ now,
3383
+ scopes,
3384
+ sub,
3385
+ ttl
3386
+ }) => {
3387
+ const safeExtra = extraClaims === undefined ? {} : Object.fromEntries(Object.entries(extraClaims).filter(([key]) => !RESERVED_ACCESS_CLAIMS.has(key)));
3388
+ const claims = {
3389
+ ...safeExtra,
3390
+ aud: audience ?? clientId,
3391
+ client_id: clientId,
3392
+ exp: nowSeconds(now + ttl),
3393
+ iat: nowSeconds(now),
3394
+ iss: issuer,
3395
+ jti: crypto.randomUUID(),
3396
+ scope: scopes.join(" "),
3397
+ sub,
3398
+ token_use: "access"
3399
+ };
3400
+ if (act !== undefined)
3401
+ claims.act = act;
3402
+ const cnf = {
3403
+ ...dpopJkt === undefined ? {} : { jkt: dpopJkt },
3404
+ ...clientCertThumbprint === undefined ? {} : { "x5t#S256": clientCertThumbprint }
3405
+ };
3406
+ if (Object.keys(cnf).length > 0) {
3407
+ claims.cnf = cnf;
3408
+ }
3409
+ return claims;
3410
+ };
3411
+ var exchangeToken = async ({
3412
+ actorClientId,
3413
+ audience,
3414
+ config,
3415
+ dpopJkt,
3416
+ now = Date.now(),
3417
+ requestedScopes,
3418
+ subjectToken
3419
+ }) => {
3420
+ const verified = await verifyJwtWithKeys(subjectToken, signingVerificationKeys(config.signingKey, config.previousSigningKeys));
3421
+ const payload = verified?.payload;
3422
+ if (payload === undefined || typeof payload.sub !== "string" || typeof payload.exp !== "number" || payload.exp <= nowSeconds(now)) {
3423
+ return { error: "invalid_grant", ok: false };
3424
+ }
3425
+ const available = typeof payload.scope === "string" ? payload.scope.split(" ") : [];
3426
+ if (requestedScopes?.some((scope) => !available.includes(scope)) === true) {
3427
+ return { error: "invalid_scope", ok: false };
3428
+ }
3429
+ const scopes = narrowScopes(available, requestedScopes);
3430
+ const ttl = resolveAccessTtl(config.accessTokenTtlMs, scopes);
3431
+ const extraClaims = await config.getAccessTokenClaims?.({
3432
+ audience,
3433
+ clientId: actorClientId,
3434
+ scopes,
3435
+ sub: payload.sub
3436
+ });
3437
+ return {
3438
+ accessToken: await signJwt(buildAccessClaims({
3439
+ act: { sub: actorClientId },
3440
+ audience,
3441
+ clientId: actorClientId,
3442
+ dpopJkt,
3443
+ extraClaims,
3444
+ issuer: config.issuer,
3445
+ now,
3446
+ scopes,
3447
+ sub: payload.sub,
3448
+ ttl
3449
+ }), config.signingKey),
3450
+ expiresIn: Math.floor(ttl / MS_PER_SECOND),
3451
+ ok: true,
3452
+ scope: scopes.join(" ")
3453
+ };
3454
+ };
3455
+ var issueTokenSet = async ({
3456
+ acr,
3457
+ audience,
3458
+ claims,
3459
+ clientCertThumbprint,
3460
+ clientId,
3461
+ config,
3462
+ dpopJkt,
3463
+ familyId,
3464
+ nonce,
3465
+ now = Date.now(),
3466
+ persistRefreshToken,
3467
+ scopes,
3468
+ sub
3469
+ }) => {
3470
+ const accessTtl = resolveAccessTtl(config.accessTokenTtlMs, scopes);
3471
+ const idTtl = config.idTokenTtlMs ?? DEFAULT_ID_TOKEN_TTL_MS;
3472
+ const refreshTtl = config.refreshTokenTtlMs ?? DEFAULT_REFRESH_TOKEN_TTL_MS;
3473
+ const accessExtra = await config.getAccessTokenClaims?.({
3474
+ audience,
3475
+ clientId,
3476
+ scopes,
3477
+ sub
3478
+ });
3479
+ const accessPayload = buildAccessClaims({
3480
+ audience,
3481
+ clientCertThumbprint,
3482
+ clientId,
3483
+ dpopJkt,
3484
+ extraClaims: { ...accessExtra, ...acr === undefined ? {} : { acr } },
3485
+ issuer: config.issuer,
3486
+ now,
3487
+ scopes,
3488
+ sub,
3489
+ ttl: accessTtl
3490
+ });
3491
+ const idPayload = {
3492
+ ...claims,
3493
+ aud: clientId,
3494
+ exp: nowSeconds(now + idTtl),
3495
+ iat: nowSeconds(now),
3496
+ iss: config.issuer,
3497
+ sub
3498
+ };
3499
+ if (nonce !== undefined)
3500
+ idPayload.nonce = nonce;
3501
+ if (acr !== undefined)
3502
+ idPayload.acr = acr;
3503
+ const refreshToken = generateSecureToken(TOKEN_BYTES2);
3504
+ const refreshRecord = {
3505
+ acr,
3506
+ audience,
3507
+ claims,
3508
+ clientId,
3509
+ createdAt: now,
3510
+ dpopJkt,
3511
+ expiresAt: now + refreshTtl,
3512
+ familyId: familyId ?? crypto.randomUUID(),
3513
+ scopes,
3514
+ tokenHash: await hashToken(refreshToken),
3515
+ userId: sub
3516
+ };
3517
+ if (persistRefreshToken)
3518
+ await persistRefreshToken(refreshRecord);
3519
+ else
3520
+ await config.refreshTokenStore.saveToken(refreshRecord);
3521
+ return {
3522
+ access_token: await signJwt(accessPayload, config.signingKey),
3523
+ expires_in: Math.floor(accessTtl / MS_PER_SECOND),
3524
+ id_token: await signJwt(idPayload, config.signingKey),
3525
+ refresh_token: refreshToken,
3526
+ scope: scopes.join(" "),
3527
+ token_type: dpopJkt === undefined ? "Bearer" : "DPoP"
3528
+ };
3529
+ };
3530
+ var mcpProtectedResourceMetadata = ({
3531
+ issuer,
3532
+ resource,
3533
+ scopes
3534
+ }) => ({
3535
+ authorization_servers: [issuer],
3536
+ resource,
3537
+ scopes_supported: scopes ?? []
3538
+ });
3539
+ var verifyPkce = async (codeVerifier, codeChallenge) => await hashToken(codeVerifier) === codeChallenge;
3540
+ var inactive = { active: false };
3541
+ var introspectToken = async ({
3542
+ config,
3543
+ hint,
3544
+ now = Date.now(),
3545
+ token
3546
+ }) => {
3547
+ if (hint !== "refresh_token") {
3548
+ const verified = await verifyJwtWithKeys(token, signingVerificationKeys(config.signingKey, config.previousSigningKeys));
3549
+ const payload = verified?.payload;
3550
+ if (payload !== undefined && typeof payload.sub === "string" && typeof payload.exp === "number" && payload.exp > nowSeconds(now)) {
3551
+ return {
3552
+ active: true,
3553
+ client_id: typeof payload.client_id === "string" ? payload.client_id : "",
3554
+ exp: payload.exp,
3555
+ iat: typeof payload.iat === "number" ? payload.iat : 0,
3556
+ scope: typeof payload.scope === "string" ? payload.scope : "",
3557
+ sub: payload.sub,
3558
+ token_type: "access_token"
3559
+ };
3560
+ }
3561
+ }
3562
+ if (hint !== "access_token") {
3563
+ const refresh = await config.refreshTokenStore.getToken(await hashToken(token));
3564
+ if (refresh && refresh.expiresAt > now) {
3565
+ return {
3566
+ active: true,
3567
+ client_id: refresh.clientId,
3568
+ exp: nowSeconds(refresh.expiresAt),
3569
+ iat: nowSeconds(refresh.createdAt),
3570
+ scope: refresh.scopes.join(" "),
3571
+ sub: refresh.userId,
3572
+ token_type: "refresh_token"
3573
+ };
3574
+ }
3575
+ }
3576
+ return inactive;
3577
+ };
3578
+ var revokeRefreshToken = async (config, token) => {
3579
+ const consumed = await config.refreshTokenStore.consumeToken(await hashToken(token));
3580
+ return consumed !== undefined;
3581
+ };
3582
+ var DEVICE_CODE_BYTES = 32;
3583
+ var USER_CODE_HALF_LENGTH = 4;
3584
+ var USER_CODE_ALPHABET = "BCDFGHJKLMNPQRSTVWXZ23456789";
3585
+ var DEFAULT_DEVICE_CODE_TTL_MINUTES = 15;
3586
+ var DEFAULT_DEVICE_CODE_TTL_MS = MILLISECONDS_IN_A_MINUTE * DEFAULT_DEVICE_CODE_TTL_MINUTES;
3587
+ var DEFAULT_DEVICE_POLL_INTERVAL_SECONDS = 5;
3588
+ var generateUserCode = () => {
3589
+ const length = USER_CODE_HALF_LENGTH * 2;
3590
+ const random = crypto.getRandomValues(new Uint8Array(length));
3591
+ let code = "";
3592
+ for (const byte of random) {
3593
+ code += USER_CODE_ALPHABET[byte % USER_CODE_ALPHABET.length];
3594
+ }
3595
+ return `${code.slice(0, USER_CODE_HALF_LENGTH)}-${code.slice(USER_CODE_HALF_LENGTH)}`;
3596
+ };
3597
+ var issueDeviceAuthorization = async ({
3598
+ audience,
3599
+ clientId,
3600
+ config,
3601
+ now = Date.now(),
3602
+ requestedScopes
3603
+ }) => {
3604
+ if (!config.deviceAuthorizationStore) {
3605
+ throw new Error("oidc.deviceAuthorizationStore is not configured \u2014 cannot start a device flow");
3606
+ }
3607
+ const deviceCode = generateSecureToken(DEVICE_CODE_BYTES);
3608
+ const userCode = generateUserCode();
3609
+ const ttl = config.deviceCodeTtlMs ?? DEFAULT_DEVICE_CODE_TTL_MS;
3610
+ const interval = config.devicePollIntervalSeconds ?? DEFAULT_DEVICE_POLL_INTERVAL_SECONDS;
3611
+ await config.deviceAuthorizationStore.saveDeviceAuthorization({
3612
+ audience,
3613
+ clientId,
3614
+ createdAt: now,
3615
+ deviceCodeHash: await hashToken(deviceCode),
3616
+ expiresAt: now + ttl,
3617
+ intervalSeconds: interval,
3618
+ scopes: requestedScopes,
3619
+ status: "pending",
3620
+ userCode
3621
+ });
3622
+ const verificationUri = `${config.issuer}${config.oidcRoute ?? DEFAULT_OIDC_ROUTE}/device`;
3623
+ return {
3624
+ device_code: deviceCode,
3625
+ expires_in: Math.floor(ttl / MS_PER_SECOND),
3626
+ interval,
3627
+ user_code: userCode,
3628
+ verification_uri: verificationUri,
3629
+ verification_uri_complete: `${verificationUri}?user_code=${encodeURIComponent(userCode)}`
3630
+ };
3631
+ };
3632
+ var decideDeviceAuthorization = async (config, userCode, approval) => {
3633
+ if (!config.deviceAuthorizationStore) {
3634
+ return { error: "not_configured", ok: false };
3635
+ }
3636
+ const record = await config.deviceAuthorizationStore.findByUserCode(userCode);
3637
+ if (!record)
3638
+ return { error: "invalid_user_code", ok: false };
3639
+ if (record.expiresAt < Date.now()) {
3640
+ return { error: "expired_token", ok: false };
3641
+ }
3642
+ if (record.status !== "pending") {
3643
+ return { error: "already_decided", ok: false };
3644
+ }
3645
+ await config.deviceAuthorizationStore.updateStatus(record.deviceCodeHash, approval.status, approval.userSub);
3646
+ if (approval.status === "approved" && approval.userSub !== undefined) {
3647
+ await config.onDeviceAuthorizationApproved?.({
3648
+ clientId: record.clientId,
3649
+ scopes: record.scopes,
3650
+ userSub: approval.userSub
3651
+ });
3652
+ }
3653
+ return { ok: true };
3654
+ };
3655
+ var approveDeviceAuthorization = async ({
3656
+ config,
3657
+ userCode,
3658
+ userSub
3659
+ }) => decideDeviceAuthorization(config, userCode, {
3660
+ status: "approved",
3661
+ userSub
3662
+ });
3663
+ var denyDeviceAuthorization = async ({
3664
+ config,
3665
+ userCode
3666
+ }) => decideDeviceAuthorization(config, userCode, { status: "denied" });
3667
+ var exchangeDeviceCode = async ({
3668
+ clientId,
3669
+ config,
3670
+ deviceCode,
3671
+ dpopJkt,
3672
+ now = Date.now()
3673
+ }) => {
3674
+ if (!config.deviceAuthorizationStore) {
3675
+ return { error: "invalid_grant", ok: false };
3676
+ }
3677
+ const deviceCodeHash = await hashToken(deviceCode);
3678
+ const record = await config.deviceAuthorizationStore.findByDeviceCodeHash(deviceCodeHash);
3679
+ if (!record || record.clientId !== clientId) {
3680
+ return { error: "invalid_grant", ok: false };
3681
+ }
3682
+ if (record.expiresAt < now) {
3683
+ await config.deviceAuthorizationStore.deleteByDeviceCodeHash(deviceCodeHash);
3684
+ return { error: "expired_token", ok: false };
3685
+ }
3686
+ if (record.status === "pending") {
3687
+ return { error: "authorization_pending", ok: false };
3688
+ }
3689
+ if (record.status === "denied" || record.userSub === undefined) {
3690
+ return { error: "access_denied", ok: false };
3691
+ }
3692
+ await config.deviceAuthorizationStore.deleteByDeviceCodeHash(deviceCodeHash);
3693
+ const tokenSet = await issueTokenSet({
3694
+ audience: record.audience,
3695
+ clientId,
3696
+ config,
3697
+ dpopJkt,
3698
+ now,
3699
+ scopes: record.scopes,
3700
+ sub: record.userSub
3701
+ });
3702
+ return { ...tokenSet, ok: true };
3703
+ };
3704
+ var AUTH_REQ_ID_BYTES = 32;
3705
+ var DEFAULT_BACKCHANNEL_TTL_MINUTES = 10;
3706
+ var DEFAULT_BACKCHANNEL_TTL_MS = MILLISECONDS_IN_A_MINUTE * DEFAULT_BACKCHANNEL_TTL_MINUTES;
3707
+ var DEFAULT_BACKCHANNEL_POLL_INTERVAL_SECONDS = 5;
3708
+ var CIBA_GRANT_TYPE = "urn:openid:params:grant-type:ciba";
3709
+ var issueBackchannelAuth = async ({
3710
+ clientId,
3711
+ config,
3712
+ loginHint,
3713
+ bindingMessage,
3714
+ now = Date.now(),
3715
+ requestedScopes
3716
+ }) => {
3717
+ if (!config.backchannelAuthStore || !config.resolveBackchannelUser) {
3718
+ return { error: "invalid_request", ok: false };
3719
+ }
3720
+ const client = await config.clientStore.findClient(clientId) ?? await config.resolveClientIdMetadata?.(clientId);
3721
+ if (!client)
3722
+ return { error: "invalid_client", ok: false };
3723
+ const resolved = await config.resolveBackchannelUser({
3724
+ client,
3725
+ loginHint
3726
+ });
3727
+ if (!resolved)
3728
+ return { error: "unknown_user_id", ok: false };
3729
+ const authReqId = generateSecureToken(AUTH_REQ_ID_BYTES);
3730
+ const ttl = config.backchannelAuthTtlMs ?? DEFAULT_BACKCHANNEL_TTL_MS;
3731
+ const interval = config.backchannelPollIntervalSeconds ?? DEFAULT_BACKCHANNEL_POLL_INTERVAL_SECONDS;
3732
+ await config.backchannelAuthStore.saveBackchannelAuth({
3733
+ authReqId,
3734
+ bindingMessage,
3735
+ clientId,
3736
+ createdAt: now,
3737
+ expiresAt: now + ttl,
3738
+ intervalSeconds: interval,
3739
+ scopes: requestedScopes,
3740
+ status: "pending",
3741
+ userSub: resolved.sub
3742
+ });
3743
+ await config.onBackchannelAuthRequest?.({
3744
+ authReqId,
3745
+ bindingMessage,
3746
+ clientId,
3747
+ scopes: requestedScopes,
3748
+ userSub: resolved.sub
3749
+ });
3750
+ return {
3751
+ auth_req_id: authReqId,
3752
+ expires_in: Math.floor(ttl / MS_PER_SECOND),
3753
+ interval,
3754
+ ok: true
3755
+ };
3756
+ };
3757
+ var decideBackchannel = async (config, authReqId, approval) => {
3758
+ if (!config.backchannelAuthStore) {
3759
+ return { error: "not_configured", ok: false };
3760
+ }
3761
+ const record = await config.backchannelAuthStore.findByAuthReqId(authReqId);
3762
+ if (!record)
3763
+ return { error: "invalid_auth_req_id", ok: false };
3764
+ if (record.expiresAt < Date.now()) {
3765
+ return { error: "expired_token", ok: false };
3766
+ }
3767
+ if (record.status !== "pending") {
3768
+ return { error: "already_decided", ok: false };
3769
+ }
3770
+ await config.backchannelAuthStore.updateStatus(authReqId, approval.status, approval.userSub ?? record.userSub);
3771
+ return { ok: true };
3772
+ };
3773
+ var approveBackchannelAuth = async ({
3774
+ authReqId,
3775
+ config,
3776
+ userSub
3777
+ }) => decideBackchannel(config, authReqId, {
3778
+ status: "approved",
3779
+ userSub
3780
+ });
3781
+ var denyBackchannelAuth = async ({
3782
+ authReqId,
3783
+ config
3784
+ }) => decideBackchannel(config, authReqId, { status: "denied" });
3785
+ var exchangeBackchannelAuth = async ({
3786
+ authReqId,
3787
+ clientCertThumbprint,
3788
+ clientId,
3789
+ config,
3790
+ dpopJkt,
3791
+ now = Date.now()
3792
+ }) => {
3793
+ if (!config.backchannelAuthStore) {
3794
+ return { error: "invalid_grant", ok: false };
3795
+ }
3796
+ const record = await config.backchannelAuthStore.findByAuthReqId(authReqId);
3797
+ if (!record || record.clientId !== clientId) {
3798
+ return { error: "invalid_grant", ok: false };
3799
+ }
3800
+ if (record.expiresAt < now) {
3801
+ await config.backchannelAuthStore.deleteByAuthReqId(authReqId);
3802
+ return { error: "expired_token", ok: false };
3803
+ }
3804
+ if (record.lastPolledAt !== undefined && now - record.lastPolledAt < record.intervalSeconds * MS_PER_SECOND) {
3805
+ return { error: "slow_down", ok: false };
3806
+ }
3807
+ await config.backchannelAuthStore.recordPoll(authReqId, now);
3808
+ if (record.status === "pending") {
3809
+ return { error: "authorization_pending", ok: false };
3810
+ }
3811
+ if (record.status === "denied" || record.userSub === undefined) {
3812
+ return { error: "access_denied", ok: false };
3813
+ }
3814
+ await config.backchannelAuthStore.deleteByAuthReqId(authReqId);
3815
+ const tokenSet = await issueTokenSet({
3816
+ clientCertThumbprint,
3817
+ clientId,
3818
+ config,
3819
+ dpopJkt,
3820
+ now,
3821
+ scopes: record.scopes,
3822
+ sub: record.userSub
3823
+ });
3824
+ return { ...tokenSet, ok: true };
3825
+ };
3826
+
3827
+ // src/apikeys/tokenRoutes.ts
3828
+ var assertTokenRouteConfiguration = (apikeys, oidc) => {
3829
+ if (apikeys?.apiClientStore === undefined || apikeys.accessTokenStore === undefined || oidc === undefined)
3830
+ return;
3831
+ const apiTokenRoute = apikeys.tokenRoute ?? DEFAULT_TOKEN_ROUTE;
3832
+ const oidcTokenRoute = `${oidc.oidcRoute ?? DEFAULT_OIDC_ROUTE}/token`;
3833
+ if (apiTokenRoute.replace(/\/$/u, "") === oidcTokenRoute) {
3834
+ throw new Error(`Conflicting auth token routes: POST ${apiTokenRoute} is configured for both API client credentials and OIDC. Set apikeys.tokenRoute to a separate path (default: ${DEFAULT_TOKEN_ROUTE}).`);
3835
+ }
3836
+ };
3837
+
3242
3838
  // src/agents/routes.ts
3243
3839
  import { Elysia as Elysia3 } from "elysia";
3244
3840
 
@@ -3495,108 +4091,6 @@ one resource or transport is not authority for another.
3495
4091
  // src/agents/registration.ts
3496
4092
  init_constants();
3497
4093
  init_crypto();
3498
-
3499
- // src/oidc/keys.ts
3500
- var ENCODER = new TextEncoder;
3501
- var ES256 = { hash: "SHA-256", name: "ECDSA" };
3502
- var KEY_PARAMS = { name: "ECDSA", namedCurve: "P-256" };
3503
- var ES256_JOSE_SIGNATURE_BYTES = 64;
3504
- var toBase64Url = (bytes) => Buffer.from(bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)).toString("base64url");
3505
- var fromBase64Url = (value) => new Uint8Array(Buffer.from(value, "base64url"));
3506
- var encodeSegment = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
3507
- var decodeSegment = (segment) => {
3508
- try {
3509
- const value = JSON.parse(Buffer.from(segment, "base64url").toString("utf8"));
3510
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
3511
- return;
3512
- }
3513
- return Object.fromEntries(Object.entries(value));
3514
- } catch {
3515
- return;
3516
- }
3517
- };
3518
- var generateSigningKey = async () => {
3519
- const pair = await crypto.subtle.generateKey(KEY_PARAMS, true, [
3520
- "sign",
3521
- "verify"
3522
- ]);
3523
- const privateJwk = await crypto.subtle.exportKey("jwk", pair.privateKey);
3524
- const publicJwk = await crypto.subtle.exportKey("jwk", pair.publicKey);
3525
- return { kid: await jwkThumbprint(publicJwk), privateJwk, publicJwk };
3526
- };
3527
- var jwkThumbprint = async (jwk) => {
3528
- const canonical = JSON.stringify({
3529
- crv: jwk.crv,
3530
- kty: jwk.kty,
3531
- x: jwk.x,
3532
- y: jwk.y
3533
- });
3534
- return toBase64Url(await crypto.subtle.digest("SHA-256", ENCODER.encode(canonical)));
3535
- };
3536
- var signJwt = async (payload, signing, typ = "JWT") => {
3537
- const input = `${encodeSegment({ alg: "ES256", kid: signing.kid, typ })}.${encodeSegment(payload)}`;
3538
- const encoded = ENCODER.encode(input);
3539
- let signature;
3540
- if (signing.sign !== undefined) {
3541
- signature = await signing.sign(encoded);
3542
- } else {
3543
- const key = await crypto.subtle.importKey("jwk", signing.privateJwk, KEY_PARAMS, false, ["sign"]);
3544
- signature = await crypto.subtle.sign(ES256, key, encoded);
3545
- }
3546
- if (signature.byteLength !== ES256_JOSE_SIGNATURE_BYTES) {
3547
- throw new Error("ES256 signer must return a 64-byte JOSE signature");
3548
- }
3549
- return `${input}.${toBase64Url(signature)}`;
3550
- };
3551
- var toPublicJwk = (key) => ({
3552
- alg: "ES256",
3553
- crv: key.publicJwk.crv,
3554
- kid: key.kid,
3555
- kty: key.publicJwk.kty,
3556
- use: "sig",
3557
- x: key.publicJwk.x,
3558
- y: key.publicJwk.y
3559
- });
3560
- var verifyJwt = async (token, publicJwk) => {
3561
- const [headerSegment, payloadSegment, signatureSegment] = token.split(".");
3562
- if (headerSegment === undefined || payloadSegment === undefined || signatureSegment === undefined) {
3563
- return;
3564
- }
3565
- const key = await crypto.subtle.importKey("jwk", publicJwk, KEY_PARAMS, false, ["verify"]);
3566
- const valid = await crypto.subtle.verify(ES256, key, fromBase64Url(signatureSegment), ENCODER.encode(`${headerSegment}.${payloadSegment}`));
3567
- if (!valid)
3568
- return;
3569
- const header = decodeSegment(headerSegment);
3570
- const payload = decodeSegment(payloadSegment);
3571
- if (header === undefined || payload === undefined)
3572
- return;
3573
- return {
3574
- header,
3575
- payload
3576
- };
3577
- };
3578
- var signingVerificationKeys = (active, previous = []) => {
3579
- const keys = [active, ...previous];
3580
- const keyIds = new Set(keys.map(({ kid }) => kid));
3581
- if (keyIds.size !== keys.length)
3582
- throw new Error("OIDC signing key IDs must be unique");
3583
- return keys;
3584
- };
3585
- var verifyJwtWithKeys = async (token, keys) => {
3586
- const [headerSegment] = token.split(".");
3587
- if (!headerSegment)
3588
- return;
3589
- const header = decodeSegment(headerSegment);
3590
- const kid = header?.kid;
3591
- if (typeof kid !== "string" || kid.length === 0)
3592
- return;
3593
- const key = keys.find((candidate) => candidate.kid === kid);
3594
- if (!key)
3595
- return;
3596
- return verifyJwt(token, key.publicJwk);
3597
- };
3598
-
3599
- // src/agents/registration.ts
3600
4094
  var AGENT_CLAIM_GRANT_TYPE = "urn:workos:agent-auth:grant-type:claim";
3601
4095
  var AGENT_IDENTITY_ASSERTION_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer";
3602
4096
  var AGENT_IDENTITY_ASSERTION_TYPE = "urn:ietf:params:oauth:token-type:id-jag";
@@ -3607,12 +4101,12 @@ var DEFAULT_GUIDE_ROUTE2 = "/auth.md";
3607
4101
  var DEFAULT_CLAIM_TTL_MS = 24 * 60 * MILLISECONDS_IN_A_MINUTE;
3608
4102
  var DEFAULT_ATTEMPT_TTL_MS = 10 * MILLISECONDS_IN_A_MINUTE;
3609
4103
  var DEFAULT_ASSERTION_TTL_MS = 60 * MILLISECONDS_IN_A_MINUTE;
3610
- var DEFAULT_ACCESS_TOKEN_TTL_MS2 = 15 * MILLISECONDS_IN_A_MINUTE;
4104
+ var DEFAULT_ACCESS_TOKEN_TTL_MS3 = 15 * MILLISECONDS_IN_A_MINUTE;
3611
4105
  var DEFAULT_MAX_AUTH_AGE_MS = 60 * MILLISECONDS_IN_A_MINUTE;
3612
4106
  var DEFAULT_POLL_INTERVAL_SECONDS = 5;
3613
4107
  var DEFAULT_MAX_CODE_ATTEMPTS = 5;
3614
4108
  var MAX_CONCURRENT_UPDATE_RETRIES = 5;
3615
- var TOKEN_BYTES2 = 32;
4109
+ var TOKEN_BYTES3 = 32;
3616
4110
  var agentRegistrationDiscoveryMetadata = (config) => {
3617
4111
  const registration = requiredRegistration(config);
3618
4112
  const endpoints = agentRegistrationEndpoints(config);
@@ -3728,7 +4222,7 @@ var randomCode = () => {
3728
4222
  const value = new DataView(bytes.buffer).getUint32(0) % 1e6;
3729
4223
  return value.toString().padStart(6, "0");
3730
4224
  };
3731
- var makeSecret = (prefix) => `${prefix}_${generateSecureToken(TOKEN_BYTES2)}`;
4225
+ var makeSecret = (prefix) => `${prefix}_${generateSecureToken(TOKEN_BYTES3)}`;
3732
4226
  var makeAttempt = async ({
3733
4227
  email,
3734
4228
  now,
@@ -4057,9 +4551,9 @@ var completeAgentClaim = async (config, input, now = Date.now()) => {
4057
4551
  };
4058
4552
  var issueAgentAccessToken = async (config, flow, now) => {
4059
4553
  const registration = requiredRegistration(config);
4060
- const accessToken = `at_${generateSecureToken(TOKEN_BYTES2)}`;
4554
+ const accessToken = `at_${generateSecureToken(TOKEN_BYTES3)}`;
4061
4555
  const scopes = (flow.status === "claimed" ? registration.postClaimScopes : registration.preClaimScopes ?? []).filter((scope) => config.scopes.includes(scope));
4062
- const expiresAt = now + (registration.tokenTtlMs ?? DEFAULT_ACCESS_TOKEN_TTL_MS2);
4556
+ const expiresAt = now + (registration.tokenTtlMs ?? DEFAULT_ACCESS_TOKEN_TTL_MS3);
4063
4557
  await registration.accessTokenStore.saveToken({
4064
4558
  clientId: flow.agentId,
4065
4559
  createdAt: now,
@@ -7291,489 +7785,6 @@ init_constants();
7291
7785
  init_crypto();
7292
7786
  import { Elysia as Elysia24, t as t17 } from "elysia";
7293
7787
 
7294
- // src/oidc/config.ts
7295
- init_constants();
7296
- init_crypto();
7297
- var DEFAULT_OIDC_ROUTE = "/oauth2";
7298
- var MS_PER_SECOND = 1000;
7299
- var TOKEN_BYTES3 = 32;
7300
- var REFRESH_TTL_DAYS = 30;
7301
- var DEFAULT_ACCESS_TOKEN_TTL_MS3 = MILLISECONDS_IN_AN_HOUR;
7302
- var DEFAULT_ID_TOKEN_TTL_MS = MILLISECONDS_IN_AN_HOUR;
7303
- var DEFAULT_REFRESH_TOKEN_TTL_MS = MILLISECONDS_IN_A_DAY * REFRESH_TTL_DAYS;
7304
- var resolveAccessTtl = (ttl, scopes) => {
7305
- if (typeof ttl === "function")
7306
- return ttl({ scopes });
7307
- return ttl ?? DEFAULT_ACCESS_TOKEN_TTL_MS3;
7308
- };
7309
- var nowSeconds = (milliseconds) => Math.floor(milliseconds / MS_PER_SECOND);
7310
- var narrowScopes = (available, requested) => requested === undefined || requested.length === 0 ? available : requested.filter((scope) => available.includes(scope));
7311
- var RESERVED_ACCESS_CLAIMS = new Set([
7312
- "act",
7313
- "aud",
7314
- "client_id",
7315
- "cnf",
7316
- "exp",
7317
- "iat",
7318
- "iss",
7319
- "jti",
7320
- "scope",
7321
- "sub",
7322
- "token_use"
7323
- ]);
7324
- var buildAccessClaims = ({
7325
- act,
7326
- audience,
7327
- clientCertThumbprint,
7328
- clientId,
7329
- dpopJkt,
7330
- extraClaims,
7331
- issuer,
7332
- now,
7333
- scopes,
7334
- sub,
7335
- ttl
7336
- }) => {
7337
- const safeExtra = extraClaims === undefined ? {} : Object.fromEntries(Object.entries(extraClaims).filter(([key]) => !RESERVED_ACCESS_CLAIMS.has(key)));
7338
- const claims = {
7339
- ...safeExtra,
7340
- aud: audience ?? clientId,
7341
- client_id: clientId,
7342
- exp: nowSeconds(now + ttl),
7343
- iat: nowSeconds(now),
7344
- iss: issuer,
7345
- jti: crypto.randomUUID(),
7346
- scope: scopes.join(" "),
7347
- sub,
7348
- token_use: "access"
7349
- };
7350
- if (act !== undefined)
7351
- claims.act = act;
7352
- const cnf = {
7353
- ...dpopJkt === undefined ? {} : { jkt: dpopJkt },
7354
- ...clientCertThumbprint === undefined ? {} : { "x5t#S256": clientCertThumbprint }
7355
- };
7356
- if (Object.keys(cnf).length > 0) {
7357
- claims.cnf = cnf;
7358
- }
7359
- return claims;
7360
- };
7361
- var exchangeToken = async ({
7362
- actorClientId,
7363
- audience,
7364
- config,
7365
- dpopJkt,
7366
- now = Date.now(),
7367
- requestedScopes,
7368
- subjectToken
7369
- }) => {
7370
- const verified = await verifyJwtWithKeys(subjectToken, signingVerificationKeys(config.signingKey, config.previousSigningKeys));
7371
- const payload = verified?.payload;
7372
- if (payload === undefined || typeof payload.sub !== "string" || typeof payload.exp !== "number" || payload.exp <= nowSeconds(now)) {
7373
- return { error: "invalid_grant", ok: false };
7374
- }
7375
- const available = typeof payload.scope === "string" ? payload.scope.split(" ") : [];
7376
- if (requestedScopes?.some((scope) => !available.includes(scope)) === true) {
7377
- return { error: "invalid_scope", ok: false };
7378
- }
7379
- const scopes = narrowScopes(available, requestedScopes);
7380
- const ttl = resolveAccessTtl(config.accessTokenTtlMs, scopes);
7381
- const extraClaims = await config.getAccessTokenClaims?.({
7382
- audience,
7383
- clientId: actorClientId,
7384
- scopes,
7385
- sub: payload.sub
7386
- });
7387
- return {
7388
- accessToken: await signJwt(buildAccessClaims({
7389
- act: { sub: actorClientId },
7390
- audience,
7391
- clientId: actorClientId,
7392
- dpopJkt,
7393
- extraClaims,
7394
- issuer: config.issuer,
7395
- now,
7396
- scopes,
7397
- sub: payload.sub,
7398
- ttl
7399
- }), config.signingKey),
7400
- expiresIn: Math.floor(ttl / MS_PER_SECOND),
7401
- ok: true,
7402
- scope: scopes.join(" ")
7403
- };
7404
- };
7405
- var issueTokenSet = async ({
7406
- acr,
7407
- audience,
7408
- claims,
7409
- clientCertThumbprint,
7410
- clientId,
7411
- config,
7412
- dpopJkt,
7413
- familyId,
7414
- nonce,
7415
- now = Date.now(),
7416
- persistRefreshToken,
7417
- scopes,
7418
- sub
7419
- }) => {
7420
- const accessTtl = resolveAccessTtl(config.accessTokenTtlMs, scopes);
7421
- const idTtl = config.idTokenTtlMs ?? DEFAULT_ID_TOKEN_TTL_MS;
7422
- const refreshTtl = config.refreshTokenTtlMs ?? DEFAULT_REFRESH_TOKEN_TTL_MS;
7423
- const accessExtra = await config.getAccessTokenClaims?.({
7424
- audience,
7425
- clientId,
7426
- scopes,
7427
- sub
7428
- });
7429
- const accessPayload = buildAccessClaims({
7430
- audience,
7431
- clientCertThumbprint,
7432
- clientId,
7433
- dpopJkt,
7434
- extraClaims: { ...accessExtra, ...acr === undefined ? {} : { acr } },
7435
- issuer: config.issuer,
7436
- now,
7437
- scopes,
7438
- sub,
7439
- ttl: accessTtl
7440
- });
7441
- const idPayload = {
7442
- ...claims,
7443
- aud: clientId,
7444
- exp: nowSeconds(now + idTtl),
7445
- iat: nowSeconds(now),
7446
- iss: config.issuer,
7447
- sub
7448
- };
7449
- if (nonce !== undefined)
7450
- idPayload.nonce = nonce;
7451
- if (acr !== undefined)
7452
- idPayload.acr = acr;
7453
- const refreshToken = generateSecureToken(TOKEN_BYTES3);
7454
- const refreshRecord = {
7455
- acr,
7456
- audience,
7457
- claims,
7458
- clientId,
7459
- createdAt: now,
7460
- dpopJkt,
7461
- expiresAt: now + refreshTtl,
7462
- familyId: familyId ?? crypto.randomUUID(),
7463
- scopes,
7464
- tokenHash: await hashToken(refreshToken),
7465
- userId: sub
7466
- };
7467
- if (persistRefreshToken)
7468
- await persistRefreshToken(refreshRecord);
7469
- else
7470
- await config.refreshTokenStore.saveToken(refreshRecord);
7471
- return {
7472
- access_token: await signJwt(accessPayload, config.signingKey),
7473
- expires_in: Math.floor(accessTtl / MS_PER_SECOND),
7474
- id_token: await signJwt(idPayload, config.signingKey),
7475
- refresh_token: refreshToken,
7476
- scope: scopes.join(" "),
7477
- token_type: dpopJkt === undefined ? "Bearer" : "DPoP"
7478
- };
7479
- };
7480
- var mcpProtectedResourceMetadata = ({
7481
- issuer,
7482
- resource,
7483
- scopes
7484
- }) => ({
7485
- authorization_servers: [issuer],
7486
- resource,
7487
- scopes_supported: scopes ?? []
7488
- });
7489
- var verifyPkce = async (codeVerifier, codeChallenge) => await hashToken(codeVerifier) === codeChallenge;
7490
- var inactive = { active: false };
7491
- var introspectToken = async ({
7492
- config,
7493
- hint,
7494
- now = Date.now(),
7495
- token
7496
- }) => {
7497
- if (hint !== "refresh_token") {
7498
- const verified = await verifyJwtWithKeys(token, signingVerificationKeys(config.signingKey, config.previousSigningKeys));
7499
- const payload = verified?.payload;
7500
- if (payload !== undefined && typeof payload.sub === "string" && typeof payload.exp === "number" && payload.exp > nowSeconds(now)) {
7501
- return {
7502
- active: true,
7503
- client_id: typeof payload.client_id === "string" ? payload.client_id : "",
7504
- exp: payload.exp,
7505
- iat: typeof payload.iat === "number" ? payload.iat : 0,
7506
- scope: typeof payload.scope === "string" ? payload.scope : "",
7507
- sub: payload.sub,
7508
- token_type: "access_token"
7509
- };
7510
- }
7511
- }
7512
- if (hint !== "access_token") {
7513
- const refresh = await config.refreshTokenStore.getToken(await hashToken(token));
7514
- if (refresh && refresh.expiresAt > now) {
7515
- return {
7516
- active: true,
7517
- client_id: refresh.clientId,
7518
- exp: nowSeconds(refresh.expiresAt),
7519
- iat: nowSeconds(refresh.createdAt),
7520
- scope: refresh.scopes.join(" "),
7521
- sub: refresh.userId,
7522
- token_type: "refresh_token"
7523
- };
7524
- }
7525
- }
7526
- return inactive;
7527
- };
7528
- var revokeRefreshToken = async (config, token) => {
7529
- const consumed = await config.refreshTokenStore.consumeToken(await hashToken(token));
7530
- return consumed !== undefined;
7531
- };
7532
- var DEVICE_CODE_BYTES = 32;
7533
- var USER_CODE_HALF_LENGTH = 4;
7534
- var USER_CODE_ALPHABET = "BCDFGHJKLMNPQRSTVWXZ23456789";
7535
- var DEFAULT_DEVICE_CODE_TTL_MINUTES = 15;
7536
- var DEFAULT_DEVICE_CODE_TTL_MS = MILLISECONDS_IN_A_MINUTE * DEFAULT_DEVICE_CODE_TTL_MINUTES;
7537
- var DEFAULT_DEVICE_POLL_INTERVAL_SECONDS = 5;
7538
- var generateUserCode = () => {
7539
- const length = USER_CODE_HALF_LENGTH * 2;
7540
- const random = crypto.getRandomValues(new Uint8Array(length));
7541
- let code = "";
7542
- for (const byte of random) {
7543
- code += USER_CODE_ALPHABET[byte % USER_CODE_ALPHABET.length];
7544
- }
7545
- return `${code.slice(0, USER_CODE_HALF_LENGTH)}-${code.slice(USER_CODE_HALF_LENGTH)}`;
7546
- };
7547
- var issueDeviceAuthorization = async ({
7548
- audience,
7549
- clientId,
7550
- config,
7551
- now = Date.now(),
7552
- requestedScopes
7553
- }) => {
7554
- if (!config.deviceAuthorizationStore) {
7555
- throw new Error("oidc.deviceAuthorizationStore is not configured \u2014 cannot start a device flow");
7556
- }
7557
- const deviceCode = generateSecureToken(DEVICE_CODE_BYTES);
7558
- const userCode = generateUserCode();
7559
- const ttl = config.deviceCodeTtlMs ?? DEFAULT_DEVICE_CODE_TTL_MS;
7560
- const interval = config.devicePollIntervalSeconds ?? DEFAULT_DEVICE_POLL_INTERVAL_SECONDS;
7561
- await config.deviceAuthorizationStore.saveDeviceAuthorization({
7562
- audience,
7563
- clientId,
7564
- createdAt: now,
7565
- deviceCodeHash: await hashToken(deviceCode),
7566
- expiresAt: now + ttl,
7567
- intervalSeconds: interval,
7568
- scopes: requestedScopes,
7569
- status: "pending",
7570
- userCode
7571
- });
7572
- const verificationUri = `${config.issuer}${config.oidcRoute ?? DEFAULT_OIDC_ROUTE}/device`;
7573
- return {
7574
- device_code: deviceCode,
7575
- expires_in: Math.floor(ttl / MS_PER_SECOND),
7576
- interval,
7577
- user_code: userCode,
7578
- verification_uri: verificationUri,
7579
- verification_uri_complete: `${verificationUri}?user_code=${encodeURIComponent(userCode)}`
7580
- };
7581
- };
7582
- var decideDeviceAuthorization = async (config, userCode, approval) => {
7583
- if (!config.deviceAuthorizationStore) {
7584
- return { error: "not_configured", ok: false };
7585
- }
7586
- const record = await config.deviceAuthorizationStore.findByUserCode(userCode);
7587
- if (!record)
7588
- return { error: "invalid_user_code", ok: false };
7589
- if (record.expiresAt < Date.now()) {
7590
- return { error: "expired_token", ok: false };
7591
- }
7592
- if (record.status !== "pending") {
7593
- return { error: "already_decided", ok: false };
7594
- }
7595
- await config.deviceAuthorizationStore.updateStatus(record.deviceCodeHash, approval.status, approval.userSub);
7596
- if (approval.status === "approved" && approval.userSub !== undefined) {
7597
- await config.onDeviceAuthorizationApproved?.({
7598
- clientId: record.clientId,
7599
- scopes: record.scopes,
7600
- userSub: approval.userSub
7601
- });
7602
- }
7603
- return { ok: true };
7604
- };
7605
- var approveDeviceAuthorization = async ({
7606
- config,
7607
- userCode,
7608
- userSub
7609
- }) => decideDeviceAuthorization(config, userCode, {
7610
- status: "approved",
7611
- userSub
7612
- });
7613
- var denyDeviceAuthorization = async ({
7614
- config,
7615
- userCode
7616
- }) => decideDeviceAuthorization(config, userCode, { status: "denied" });
7617
- var exchangeDeviceCode = async ({
7618
- clientId,
7619
- config,
7620
- deviceCode,
7621
- dpopJkt,
7622
- now = Date.now()
7623
- }) => {
7624
- if (!config.deviceAuthorizationStore) {
7625
- return { error: "invalid_grant", ok: false };
7626
- }
7627
- const deviceCodeHash = await hashToken(deviceCode);
7628
- const record = await config.deviceAuthorizationStore.findByDeviceCodeHash(deviceCodeHash);
7629
- if (!record || record.clientId !== clientId) {
7630
- return { error: "invalid_grant", ok: false };
7631
- }
7632
- if (record.expiresAt < now) {
7633
- await config.deviceAuthorizationStore.deleteByDeviceCodeHash(deviceCodeHash);
7634
- return { error: "expired_token", ok: false };
7635
- }
7636
- if (record.status === "pending") {
7637
- return { error: "authorization_pending", ok: false };
7638
- }
7639
- if (record.status === "denied" || record.userSub === undefined) {
7640
- return { error: "access_denied", ok: false };
7641
- }
7642
- await config.deviceAuthorizationStore.deleteByDeviceCodeHash(deviceCodeHash);
7643
- const tokenSet = await issueTokenSet({
7644
- audience: record.audience,
7645
- clientId,
7646
- config,
7647
- dpopJkt,
7648
- now,
7649
- scopes: record.scopes,
7650
- sub: record.userSub
7651
- });
7652
- return { ...tokenSet, ok: true };
7653
- };
7654
- var AUTH_REQ_ID_BYTES = 32;
7655
- var DEFAULT_BACKCHANNEL_TTL_MINUTES = 10;
7656
- var DEFAULT_BACKCHANNEL_TTL_MS = MILLISECONDS_IN_A_MINUTE * DEFAULT_BACKCHANNEL_TTL_MINUTES;
7657
- var DEFAULT_BACKCHANNEL_POLL_INTERVAL_SECONDS = 5;
7658
- var CIBA_GRANT_TYPE = "urn:openid:params:grant-type:ciba";
7659
- var issueBackchannelAuth = async ({
7660
- clientId,
7661
- config,
7662
- loginHint,
7663
- bindingMessage,
7664
- now = Date.now(),
7665
- requestedScopes
7666
- }) => {
7667
- if (!config.backchannelAuthStore || !config.resolveBackchannelUser) {
7668
- return { error: "invalid_request", ok: false };
7669
- }
7670
- const client = await config.clientStore.findClient(clientId) ?? await config.resolveClientIdMetadata?.(clientId);
7671
- if (!client)
7672
- return { error: "invalid_client", ok: false };
7673
- const resolved = await config.resolveBackchannelUser({
7674
- client,
7675
- loginHint
7676
- });
7677
- if (!resolved)
7678
- return { error: "unknown_user_id", ok: false };
7679
- const authReqId = generateSecureToken(AUTH_REQ_ID_BYTES);
7680
- const ttl = config.backchannelAuthTtlMs ?? DEFAULT_BACKCHANNEL_TTL_MS;
7681
- const interval = config.backchannelPollIntervalSeconds ?? DEFAULT_BACKCHANNEL_POLL_INTERVAL_SECONDS;
7682
- await config.backchannelAuthStore.saveBackchannelAuth({
7683
- authReqId,
7684
- bindingMessage,
7685
- clientId,
7686
- createdAt: now,
7687
- expiresAt: now + ttl,
7688
- intervalSeconds: interval,
7689
- scopes: requestedScopes,
7690
- status: "pending",
7691
- userSub: resolved.sub
7692
- });
7693
- await config.onBackchannelAuthRequest?.({
7694
- authReqId,
7695
- bindingMessage,
7696
- clientId,
7697
- scopes: requestedScopes,
7698
- userSub: resolved.sub
7699
- });
7700
- return {
7701
- auth_req_id: authReqId,
7702
- expires_in: Math.floor(ttl / MS_PER_SECOND),
7703
- interval,
7704
- ok: true
7705
- };
7706
- };
7707
- var decideBackchannel = async (config, authReqId, approval) => {
7708
- if (!config.backchannelAuthStore) {
7709
- return { error: "not_configured", ok: false };
7710
- }
7711
- const record = await config.backchannelAuthStore.findByAuthReqId(authReqId);
7712
- if (!record)
7713
- return { error: "invalid_auth_req_id", ok: false };
7714
- if (record.expiresAt < Date.now()) {
7715
- return { error: "expired_token", ok: false };
7716
- }
7717
- if (record.status !== "pending") {
7718
- return { error: "already_decided", ok: false };
7719
- }
7720
- await config.backchannelAuthStore.updateStatus(authReqId, approval.status, approval.userSub ?? record.userSub);
7721
- return { ok: true };
7722
- };
7723
- var approveBackchannelAuth = async ({
7724
- authReqId,
7725
- config,
7726
- userSub
7727
- }) => decideBackchannel(config, authReqId, {
7728
- status: "approved",
7729
- userSub
7730
- });
7731
- var denyBackchannelAuth = async ({
7732
- authReqId,
7733
- config
7734
- }) => decideBackchannel(config, authReqId, { status: "denied" });
7735
- var exchangeBackchannelAuth = async ({
7736
- authReqId,
7737
- clientCertThumbprint,
7738
- clientId,
7739
- config,
7740
- dpopJkt,
7741
- now = Date.now()
7742
- }) => {
7743
- if (!config.backchannelAuthStore) {
7744
- return { error: "invalid_grant", ok: false };
7745
- }
7746
- const record = await config.backchannelAuthStore.findByAuthReqId(authReqId);
7747
- if (!record || record.clientId !== clientId) {
7748
- return { error: "invalid_grant", ok: false };
7749
- }
7750
- if (record.expiresAt < now) {
7751
- await config.backchannelAuthStore.deleteByAuthReqId(authReqId);
7752
- return { error: "expired_token", ok: false };
7753
- }
7754
- if (record.lastPolledAt !== undefined && now - record.lastPolledAt < record.intervalSeconds * MS_PER_SECOND) {
7755
- return { error: "slow_down", ok: false };
7756
- }
7757
- await config.backchannelAuthStore.recordPoll(authReqId, now);
7758
- if (record.status === "pending") {
7759
- return { error: "authorization_pending", ok: false };
7760
- }
7761
- if (record.status === "denied" || record.userSub === undefined) {
7762
- return { error: "access_denied", ok: false };
7763
- }
7764
- await config.backchannelAuthStore.deleteByAuthReqId(authReqId);
7765
- const tokenSet = await issueTokenSet({
7766
- clientCertThumbprint,
7767
- clientId,
7768
- config,
7769
- dpopJkt,
7770
- now,
7771
- scopes: record.scopes,
7772
- sub: record.userSub
7773
- });
7774
- return { ...tokenSet, ok: true };
7775
- };
7776
-
7777
7788
  // src/oidc/clientAuth.ts
7778
7789
  init_constants();
7779
7790
  var CLIENT_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
@@ -40603,6 +40614,7 @@ var buildAuthApplications = async (configuration) => {
40603
40614
  onRevocationError,
40604
40615
  onSessionCleanup
40605
40616
  } = configuration;
40617
+ assertTokenRouteConfiguration(apikeys, oidc);
40606
40618
  if (push && nativePush2)
40607
40619
  throw new Error("Configure `push`, not both `push` and `nativePush`");
40608
40620
  const pushConfig = push ?? nativePush2;
@@ -41445,5 +41457,5 @@ export {
41445
41457
  writeWarrant
41446
41458
  };
41447
41459
 
41448
- //# debugId=ED19DD93253BFAE664756E2164756E21
41460
+ //# debugId=7BA88CF5784F986664756E2164756E21
41449
41461
  //# sourceMappingURL=index.js.map