@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/CHANGELOG.md +14 -0
- package/README.md +16 -0
- package/changelog.json +27 -0
- package/dist/apikeys/config.d.ts +1 -1
- package/dist/apikeys/tokenRoutes.d.ts +7 -0
- package/dist/index.js +604 -592
- package/dist/index.js.map +9 -8
- package/dist/server.js +604 -592
- package/dist/server.js.map +9 -8
- package/dist/types.d.ts +1 -1
- package/package.json +11 -7
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 = "/
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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 ??
|
|
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=
|
|
41460
|
+
//# debugId=7BA88CF5784F986664756E2164756E21
|
|
41449
41461
|
//# sourceMappingURL=index.js.map
|