@absolutejs/auth 0.77.1 → 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/server.js CHANGED
@@ -4023,7 +4023,7 @@ import { Elysia, t } from "elysia";
4023
4023
  // src/apikeys/config.ts
4024
4024
  init_constants();
4025
4025
  init_crypto();
4026
- var DEFAULT_TOKEN_ROUTE = "/oauth2/token";
4026
+ var DEFAULT_TOKEN_ROUTE = "/auth/api/token";
4027
4027
  var ACCESS_TOKEN_PREFIX = "at_";
4028
4028
  var API_KEY_PREFIX = "sk_";
4029
4029
  var BEARER_PREFIX = "Bearer ";
@@ -4235,6 +4235,602 @@ var apiKeysRoutes = ({
4235
4235
  });
4236
4236
  };
4237
4237
 
4238
+ // src/oidc/config.ts
4239
+ init_constants();
4240
+ init_crypto();
4241
+
4242
+ // src/oidc/keys.ts
4243
+ var ENCODER = new TextEncoder;
4244
+ var ES256 = { hash: "SHA-256", name: "ECDSA" };
4245
+ var KEY_PARAMS = { name: "ECDSA", namedCurve: "P-256" };
4246
+ var ES256_JOSE_SIGNATURE_BYTES = 64;
4247
+ var toBase64Url = (bytes) => Buffer.from(bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)).toString("base64url");
4248
+ var fromBase64Url = (value) => new Uint8Array(Buffer.from(value, "base64url"));
4249
+ var encodeSegment = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
4250
+ var decodeSegment = (segment) => {
4251
+ try {
4252
+ const value = JSON.parse(Buffer.from(segment, "base64url").toString("utf8"));
4253
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
4254
+ return;
4255
+ }
4256
+ return Object.fromEntries(Object.entries(value));
4257
+ } catch {
4258
+ return;
4259
+ }
4260
+ };
4261
+ var generateSigningKey = async () => {
4262
+ const pair = await crypto.subtle.generateKey(KEY_PARAMS, true, [
4263
+ "sign",
4264
+ "verify"
4265
+ ]);
4266
+ const privateJwk = await crypto.subtle.exportKey("jwk", pair.privateKey);
4267
+ const publicJwk = await crypto.subtle.exportKey("jwk", pair.publicKey);
4268
+ return { kid: await jwkThumbprint(publicJwk), privateJwk, publicJwk };
4269
+ };
4270
+ var jwkThumbprint = async (jwk) => {
4271
+ const canonical = JSON.stringify({
4272
+ crv: jwk.crv,
4273
+ kty: jwk.kty,
4274
+ x: jwk.x,
4275
+ y: jwk.y
4276
+ });
4277
+ return toBase64Url(await crypto.subtle.digest("SHA-256", ENCODER.encode(canonical)));
4278
+ };
4279
+ var signJwt = async (payload, signing, typ = "JWT") => {
4280
+ const input = `${encodeSegment({ alg: "ES256", kid: signing.kid, typ })}.${encodeSegment(payload)}`;
4281
+ const encoded = ENCODER.encode(input);
4282
+ let signature;
4283
+ if (signing.sign !== undefined) {
4284
+ signature = await signing.sign(encoded);
4285
+ } else {
4286
+ const key = await crypto.subtle.importKey("jwk", signing.privateJwk, KEY_PARAMS, false, ["sign"]);
4287
+ signature = await crypto.subtle.sign(ES256, key, encoded);
4288
+ }
4289
+ if (signature.byteLength !== ES256_JOSE_SIGNATURE_BYTES) {
4290
+ throw new Error("ES256 signer must return a 64-byte JOSE signature");
4291
+ }
4292
+ return `${input}.${toBase64Url(signature)}`;
4293
+ };
4294
+ var toPublicJwk = (key) => ({
4295
+ alg: "ES256",
4296
+ crv: key.publicJwk.crv,
4297
+ kid: key.kid,
4298
+ kty: key.publicJwk.kty,
4299
+ use: "sig",
4300
+ x: key.publicJwk.x,
4301
+ y: key.publicJwk.y
4302
+ });
4303
+ var verifyJwt = async (token, publicJwk) => {
4304
+ const [headerSegment, payloadSegment, signatureSegment] = token.split(".");
4305
+ if (headerSegment === undefined || payloadSegment === undefined || signatureSegment === undefined) {
4306
+ return;
4307
+ }
4308
+ const key = await crypto.subtle.importKey("jwk", publicJwk, KEY_PARAMS, false, ["verify"]);
4309
+ const valid = await crypto.subtle.verify(ES256, key, fromBase64Url(signatureSegment), ENCODER.encode(`${headerSegment}.${payloadSegment}`));
4310
+ if (!valid)
4311
+ return;
4312
+ const header = decodeSegment(headerSegment);
4313
+ const payload = decodeSegment(payloadSegment);
4314
+ if (header === undefined || payload === undefined)
4315
+ return;
4316
+ return {
4317
+ header,
4318
+ payload
4319
+ };
4320
+ };
4321
+ var signingVerificationKeys = (active, previous = []) => {
4322
+ const keys = [active, ...previous];
4323
+ const keyIds = new Set(keys.map(({ kid }) => kid));
4324
+ if (keyIds.size !== keys.length)
4325
+ throw new Error("OIDC signing key IDs must be unique");
4326
+ return keys;
4327
+ };
4328
+ var verifyJwtWithKeys = async (token, keys) => {
4329
+ const [headerSegment] = token.split(".");
4330
+ if (!headerSegment)
4331
+ return;
4332
+ const header = decodeSegment(headerSegment);
4333
+ const kid = header?.kid;
4334
+ if (typeof kid !== "string" || kid.length === 0)
4335
+ return;
4336
+ const key = keys.find((candidate) => candidate.kid === kid);
4337
+ if (!key)
4338
+ return;
4339
+ return verifyJwt(token, key.publicJwk);
4340
+ };
4341
+
4342
+ // src/oidc/config.ts
4343
+ var DEFAULT_OIDC_ROUTE = "/oauth2";
4344
+ var MS_PER_SECOND = 1000;
4345
+ var TOKEN_BYTES2 = 32;
4346
+ var REFRESH_TTL_DAYS = 30;
4347
+ var DEFAULT_ACCESS_TOKEN_TTL_MS2 = MILLISECONDS_IN_AN_HOUR;
4348
+ var DEFAULT_ID_TOKEN_TTL_MS = MILLISECONDS_IN_AN_HOUR;
4349
+ var DEFAULT_REFRESH_TOKEN_TTL_MS = MILLISECONDS_IN_A_DAY * REFRESH_TTL_DAYS;
4350
+ var resolveAccessTtl = (ttl, scopes) => {
4351
+ if (typeof ttl === "function")
4352
+ return ttl({ scopes });
4353
+ return ttl ?? DEFAULT_ACCESS_TOKEN_TTL_MS2;
4354
+ };
4355
+ var nowSeconds = (milliseconds) => Math.floor(milliseconds / MS_PER_SECOND);
4356
+ var narrowScopes = (available, requested) => requested === undefined || requested.length === 0 ? available : requested.filter((scope) => available.includes(scope));
4357
+ var RESERVED_ACCESS_CLAIMS = new Set([
4358
+ "act",
4359
+ "aud",
4360
+ "client_id",
4361
+ "cnf",
4362
+ "exp",
4363
+ "iat",
4364
+ "iss",
4365
+ "jti",
4366
+ "scope",
4367
+ "sub",
4368
+ "token_use"
4369
+ ]);
4370
+ var buildAccessClaims = ({
4371
+ act,
4372
+ audience,
4373
+ clientCertThumbprint,
4374
+ clientId,
4375
+ dpopJkt,
4376
+ extraClaims,
4377
+ issuer,
4378
+ now,
4379
+ scopes,
4380
+ sub,
4381
+ ttl
4382
+ }) => {
4383
+ const safeExtra = extraClaims === undefined ? {} : Object.fromEntries(Object.entries(extraClaims).filter(([key]) => !RESERVED_ACCESS_CLAIMS.has(key)));
4384
+ const claims = {
4385
+ ...safeExtra,
4386
+ aud: audience ?? clientId,
4387
+ client_id: clientId,
4388
+ exp: nowSeconds(now + ttl),
4389
+ iat: nowSeconds(now),
4390
+ iss: issuer,
4391
+ jti: crypto.randomUUID(),
4392
+ scope: scopes.join(" "),
4393
+ sub,
4394
+ token_use: "access"
4395
+ };
4396
+ if (act !== undefined)
4397
+ claims.act = act;
4398
+ const cnf = {
4399
+ ...dpopJkt === undefined ? {} : { jkt: dpopJkt },
4400
+ ...clientCertThumbprint === undefined ? {} : { "x5t#S256": clientCertThumbprint }
4401
+ };
4402
+ if (Object.keys(cnf).length > 0) {
4403
+ claims.cnf = cnf;
4404
+ }
4405
+ return claims;
4406
+ };
4407
+ var exchangeToken = async ({
4408
+ actorClientId,
4409
+ audience,
4410
+ config,
4411
+ dpopJkt,
4412
+ now = Date.now(),
4413
+ requestedScopes,
4414
+ subjectToken
4415
+ }) => {
4416
+ const verified = await verifyJwtWithKeys(subjectToken, signingVerificationKeys(config.signingKey, config.previousSigningKeys));
4417
+ const payload = verified?.payload;
4418
+ if (payload === undefined || typeof payload.sub !== "string" || typeof payload.exp !== "number" || payload.exp <= nowSeconds(now)) {
4419
+ return { error: "invalid_grant", ok: false };
4420
+ }
4421
+ const available = typeof payload.scope === "string" ? payload.scope.split(" ") : [];
4422
+ if (requestedScopes?.some((scope) => !available.includes(scope)) === true) {
4423
+ return { error: "invalid_scope", ok: false };
4424
+ }
4425
+ const scopes = narrowScopes(available, requestedScopes);
4426
+ const ttl = resolveAccessTtl(config.accessTokenTtlMs, scopes);
4427
+ const extraClaims = await config.getAccessTokenClaims?.({
4428
+ audience,
4429
+ clientId: actorClientId,
4430
+ scopes,
4431
+ sub: payload.sub
4432
+ });
4433
+ return {
4434
+ accessToken: await signJwt(buildAccessClaims({
4435
+ act: { sub: actorClientId },
4436
+ audience,
4437
+ clientId: actorClientId,
4438
+ dpopJkt,
4439
+ extraClaims,
4440
+ issuer: config.issuer,
4441
+ now,
4442
+ scopes,
4443
+ sub: payload.sub,
4444
+ ttl
4445
+ }), config.signingKey),
4446
+ expiresIn: Math.floor(ttl / MS_PER_SECOND),
4447
+ ok: true,
4448
+ scope: scopes.join(" ")
4449
+ };
4450
+ };
4451
+ var issueTokenSet = async ({
4452
+ acr,
4453
+ audience,
4454
+ claims,
4455
+ clientCertThumbprint,
4456
+ clientId,
4457
+ config,
4458
+ dpopJkt,
4459
+ familyId,
4460
+ nonce,
4461
+ now = Date.now(),
4462
+ persistRefreshToken,
4463
+ scopes,
4464
+ sub
4465
+ }) => {
4466
+ const accessTtl = resolveAccessTtl(config.accessTokenTtlMs, scopes);
4467
+ const idTtl = config.idTokenTtlMs ?? DEFAULT_ID_TOKEN_TTL_MS;
4468
+ const refreshTtl = config.refreshTokenTtlMs ?? DEFAULT_REFRESH_TOKEN_TTL_MS;
4469
+ const accessExtra = await config.getAccessTokenClaims?.({
4470
+ audience,
4471
+ clientId,
4472
+ scopes,
4473
+ sub
4474
+ });
4475
+ const accessPayload = buildAccessClaims({
4476
+ audience,
4477
+ clientCertThumbprint,
4478
+ clientId,
4479
+ dpopJkt,
4480
+ extraClaims: { ...accessExtra, ...acr === undefined ? {} : { acr } },
4481
+ issuer: config.issuer,
4482
+ now,
4483
+ scopes,
4484
+ sub,
4485
+ ttl: accessTtl
4486
+ });
4487
+ const idPayload = {
4488
+ ...claims,
4489
+ aud: clientId,
4490
+ exp: nowSeconds(now + idTtl),
4491
+ iat: nowSeconds(now),
4492
+ iss: config.issuer,
4493
+ sub
4494
+ };
4495
+ if (nonce !== undefined)
4496
+ idPayload.nonce = nonce;
4497
+ if (acr !== undefined)
4498
+ idPayload.acr = acr;
4499
+ const refreshToken = generateSecureToken(TOKEN_BYTES2);
4500
+ const refreshRecord = {
4501
+ acr,
4502
+ audience,
4503
+ claims,
4504
+ clientId,
4505
+ createdAt: now,
4506
+ dpopJkt,
4507
+ expiresAt: now + refreshTtl,
4508
+ familyId: familyId ?? crypto.randomUUID(),
4509
+ scopes,
4510
+ tokenHash: await hashToken(refreshToken),
4511
+ userId: sub
4512
+ };
4513
+ if (persistRefreshToken)
4514
+ await persistRefreshToken(refreshRecord);
4515
+ else
4516
+ await config.refreshTokenStore.saveToken(refreshRecord);
4517
+ return {
4518
+ access_token: await signJwt(accessPayload, config.signingKey),
4519
+ expires_in: Math.floor(accessTtl / MS_PER_SECOND),
4520
+ id_token: await signJwt(idPayload, config.signingKey),
4521
+ refresh_token: refreshToken,
4522
+ scope: scopes.join(" "),
4523
+ token_type: dpopJkt === undefined ? "Bearer" : "DPoP"
4524
+ };
4525
+ };
4526
+ var mcpProtectedResourceMetadata = ({
4527
+ issuer,
4528
+ resource,
4529
+ scopes
4530
+ }) => ({
4531
+ authorization_servers: [issuer],
4532
+ resource,
4533
+ scopes_supported: scopes ?? []
4534
+ });
4535
+ var verifyPkce = async (codeVerifier, codeChallenge) => await hashToken(codeVerifier) === codeChallenge;
4536
+ var inactive = { active: false };
4537
+ var introspectToken = async ({
4538
+ config,
4539
+ hint,
4540
+ now = Date.now(),
4541
+ token
4542
+ }) => {
4543
+ if (hint !== "refresh_token") {
4544
+ const verified = await verifyJwtWithKeys(token, signingVerificationKeys(config.signingKey, config.previousSigningKeys));
4545
+ const payload = verified?.payload;
4546
+ if (payload !== undefined && typeof payload.sub === "string" && typeof payload.exp === "number" && payload.exp > nowSeconds(now)) {
4547
+ return {
4548
+ active: true,
4549
+ client_id: typeof payload.client_id === "string" ? payload.client_id : "",
4550
+ exp: payload.exp,
4551
+ iat: typeof payload.iat === "number" ? payload.iat : 0,
4552
+ scope: typeof payload.scope === "string" ? payload.scope : "",
4553
+ sub: payload.sub,
4554
+ token_type: "access_token"
4555
+ };
4556
+ }
4557
+ }
4558
+ if (hint !== "access_token") {
4559
+ const refresh = await config.refreshTokenStore.getToken(await hashToken(token));
4560
+ if (refresh && refresh.expiresAt > now) {
4561
+ return {
4562
+ active: true,
4563
+ client_id: refresh.clientId,
4564
+ exp: nowSeconds(refresh.expiresAt),
4565
+ iat: nowSeconds(refresh.createdAt),
4566
+ scope: refresh.scopes.join(" "),
4567
+ sub: refresh.userId,
4568
+ token_type: "refresh_token"
4569
+ };
4570
+ }
4571
+ }
4572
+ return inactive;
4573
+ };
4574
+ var revokeRefreshToken = async (config, token) => {
4575
+ const consumed = await config.refreshTokenStore.consumeToken(await hashToken(token));
4576
+ return consumed !== undefined;
4577
+ };
4578
+ var DEVICE_CODE_BYTES = 32;
4579
+ var USER_CODE_HALF_LENGTH = 4;
4580
+ var USER_CODE_ALPHABET = "BCDFGHJKLMNPQRSTVWXZ23456789";
4581
+ var DEFAULT_DEVICE_CODE_TTL_MINUTES = 15;
4582
+ var DEFAULT_DEVICE_CODE_TTL_MS = MILLISECONDS_IN_A_MINUTE * DEFAULT_DEVICE_CODE_TTL_MINUTES;
4583
+ var DEFAULT_DEVICE_POLL_INTERVAL_SECONDS = 5;
4584
+ var generateUserCode = () => {
4585
+ const length = USER_CODE_HALF_LENGTH * 2;
4586
+ const random = crypto.getRandomValues(new Uint8Array(length));
4587
+ let code = "";
4588
+ for (const byte of random) {
4589
+ code += USER_CODE_ALPHABET[byte % USER_CODE_ALPHABET.length];
4590
+ }
4591
+ return `${code.slice(0, USER_CODE_HALF_LENGTH)}-${code.slice(USER_CODE_HALF_LENGTH)}`;
4592
+ };
4593
+ var issueDeviceAuthorization = async ({
4594
+ audience,
4595
+ clientId,
4596
+ config,
4597
+ now = Date.now(),
4598
+ requestedScopes
4599
+ }) => {
4600
+ if (!config.deviceAuthorizationStore) {
4601
+ throw new Error("oidc.deviceAuthorizationStore is not configured \u2014 cannot start a device flow");
4602
+ }
4603
+ const deviceCode = generateSecureToken(DEVICE_CODE_BYTES);
4604
+ const userCode = generateUserCode();
4605
+ const ttl = config.deviceCodeTtlMs ?? DEFAULT_DEVICE_CODE_TTL_MS;
4606
+ const interval = config.devicePollIntervalSeconds ?? DEFAULT_DEVICE_POLL_INTERVAL_SECONDS;
4607
+ await config.deviceAuthorizationStore.saveDeviceAuthorization({
4608
+ audience,
4609
+ clientId,
4610
+ createdAt: now,
4611
+ deviceCodeHash: await hashToken(deviceCode),
4612
+ expiresAt: now + ttl,
4613
+ intervalSeconds: interval,
4614
+ scopes: requestedScopes,
4615
+ status: "pending",
4616
+ userCode
4617
+ });
4618
+ const verificationUri = `${config.issuer}${config.oidcRoute ?? DEFAULT_OIDC_ROUTE}/device`;
4619
+ return {
4620
+ device_code: deviceCode,
4621
+ expires_in: Math.floor(ttl / MS_PER_SECOND),
4622
+ interval,
4623
+ user_code: userCode,
4624
+ verification_uri: verificationUri,
4625
+ verification_uri_complete: `${verificationUri}?user_code=${encodeURIComponent(userCode)}`
4626
+ };
4627
+ };
4628
+ var decideDeviceAuthorization = async (config, userCode, approval) => {
4629
+ if (!config.deviceAuthorizationStore) {
4630
+ return { error: "not_configured", ok: false };
4631
+ }
4632
+ const record = await config.deviceAuthorizationStore.findByUserCode(userCode);
4633
+ if (!record)
4634
+ return { error: "invalid_user_code", ok: false };
4635
+ if (record.expiresAt < Date.now()) {
4636
+ return { error: "expired_token", ok: false };
4637
+ }
4638
+ if (record.status !== "pending") {
4639
+ return { error: "already_decided", ok: false };
4640
+ }
4641
+ await config.deviceAuthorizationStore.updateStatus(record.deviceCodeHash, approval.status, approval.userSub);
4642
+ if (approval.status === "approved" && approval.userSub !== undefined) {
4643
+ await config.onDeviceAuthorizationApproved?.({
4644
+ clientId: record.clientId,
4645
+ scopes: record.scopes,
4646
+ userSub: approval.userSub
4647
+ });
4648
+ }
4649
+ return { ok: true };
4650
+ };
4651
+ var approveDeviceAuthorization = async ({
4652
+ config,
4653
+ userCode,
4654
+ userSub
4655
+ }) => decideDeviceAuthorization(config, userCode, {
4656
+ status: "approved",
4657
+ userSub
4658
+ });
4659
+ var denyDeviceAuthorization = async ({
4660
+ config,
4661
+ userCode
4662
+ }) => decideDeviceAuthorization(config, userCode, { status: "denied" });
4663
+ var exchangeDeviceCode = async ({
4664
+ clientId,
4665
+ config,
4666
+ deviceCode,
4667
+ dpopJkt,
4668
+ now = Date.now()
4669
+ }) => {
4670
+ if (!config.deviceAuthorizationStore) {
4671
+ return { error: "invalid_grant", ok: false };
4672
+ }
4673
+ const deviceCodeHash = await hashToken(deviceCode);
4674
+ const record = await config.deviceAuthorizationStore.findByDeviceCodeHash(deviceCodeHash);
4675
+ if (!record || record.clientId !== clientId) {
4676
+ return { error: "invalid_grant", ok: false };
4677
+ }
4678
+ if (record.expiresAt < now) {
4679
+ await config.deviceAuthorizationStore.deleteByDeviceCodeHash(deviceCodeHash);
4680
+ return { error: "expired_token", ok: false };
4681
+ }
4682
+ if (record.status === "pending") {
4683
+ return { error: "authorization_pending", ok: false };
4684
+ }
4685
+ if (record.status === "denied" || record.userSub === undefined) {
4686
+ return { error: "access_denied", ok: false };
4687
+ }
4688
+ await config.deviceAuthorizationStore.deleteByDeviceCodeHash(deviceCodeHash);
4689
+ const tokenSet = await issueTokenSet({
4690
+ audience: record.audience,
4691
+ clientId,
4692
+ config,
4693
+ dpopJkt,
4694
+ now,
4695
+ scopes: record.scopes,
4696
+ sub: record.userSub
4697
+ });
4698
+ return { ...tokenSet, ok: true };
4699
+ };
4700
+ var AUTH_REQ_ID_BYTES = 32;
4701
+ var DEFAULT_BACKCHANNEL_TTL_MINUTES = 10;
4702
+ var DEFAULT_BACKCHANNEL_TTL_MS = MILLISECONDS_IN_A_MINUTE * DEFAULT_BACKCHANNEL_TTL_MINUTES;
4703
+ var DEFAULT_BACKCHANNEL_POLL_INTERVAL_SECONDS = 5;
4704
+ var CIBA_GRANT_TYPE = "urn:openid:params:grant-type:ciba";
4705
+ var issueBackchannelAuth = async ({
4706
+ clientId,
4707
+ config,
4708
+ loginHint,
4709
+ bindingMessage,
4710
+ now = Date.now(),
4711
+ requestedScopes
4712
+ }) => {
4713
+ if (!config.backchannelAuthStore || !config.resolveBackchannelUser) {
4714
+ return { error: "invalid_request", ok: false };
4715
+ }
4716
+ const client = await config.clientStore.findClient(clientId) ?? await config.resolveClientIdMetadata?.(clientId);
4717
+ if (!client)
4718
+ return { error: "invalid_client", ok: false };
4719
+ const resolved = await config.resolveBackchannelUser({
4720
+ client,
4721
+ loginHint
4722
+ });
4723
+ if (!resolved)
4724
+ return { error: "unknown_user_id", ok: false };
4725
+ const authReqId = generateSecureToken(AUTH_REQ_ID_BYTES);
4726
+ const ttl = config.backchannelAuthTtlMs ?? DEFAULT_BACKCHANNEL_TTL_MS;
4727
+ const interval = config.backchannelPollIntervalSeconds ?? DEFAULT_BACKCHANNEL_POLL_INTERVAL_SECONDS;
4728
+ await config.backchannelAuthStore.saveBackchannelAuth({
4729
+ authReqId,
4730
+ bindingMessage,
4731
+ clientId,
4732
+ createdAt: now,
4733
+ expiresAt: now + ttl,
4734
+ intervalSeconds: interval,
4735
+ scopes: requestedScopes,
4736
+ status: "pending",
4737
+ userSub: resolved.sub
4738
+ });
4739
+ await config.onBackchannelAuthRequest?.({
4740
+ authReqId,
4741
+ bindingMessage,
4742
+ clientId,
4743
+ scopes: requestedScopes,
4744
+ userSub: resolved.sub
4745
+ });
4746
+ return {
4747
+ auth_req_id: authReqId,
4748
+ expires_in: Math.floor(ttl / MS_PER_SECOND),
4749
+ interval,
4750
+ ok: true
4751
+ };
4752
+ };
4753
+ var decideBackchannel = async (config, authReqId, approval) => {
4754
+ if (!config.backchannelAuthStore) {
4755
+ return { error: "not_configured", ok: false };
4756
+ }
4757
+ const record = await config.backchannelAuthStore.findByAuthReqId(authReqId);
4758
+ if (!record)
4759
+ return { error: "invalid_auth_req_id", ok: false };
4760
+ if (record.expiresAt < Date.now()) {
4761
+ return { error: "expired_token", ok: false };
4762
+ }
4763
+ if (record.status !== "pending") {
4764
+ return { error: "already_decided", ok: false };
4765
+ }
4766
+ await config.backchannelAuthStore.updateStatus(authReqId, approval.status, approval.userSub ?? record.userSub);
4767
+ return { ok: true };
4768
+ };
4769
+ var approveBackchannelAuth = async ({
4770
+ authReqId,
4771
+ config,
4772
+ userSub
4773
+ }) => decideBackchannel(config, authReqId, {
4774
+ status: "approved",
4775
+ userSub
4776
+ });
4777
+ var denyBackchannelAuth = async ({
4778
+ authReqId,
4779
+ config
4780
+ }) => decideBackchannel(config, authReqId, { status: "denied" });
4781
+ var exchangeBackchannelAuth = async ({
4782
+ authReqId,
4783
+ clientCertThumbprint,
4784
+ clientId,
4785
+ config,
4786
+ dpopJkt,
4787
+ now = Date.now()
4788
+ }) => {
4789
+ if (!config.backchannelAuthStore) {
4790
+ return { error: "invalid_grant", ok: false };
4791
+ }
4792
+ const record = await config.backchannelAuthStore.findByAuthReqId(authReqId);
4793
+ if (!record || record.clientId !== clientId) {
4794
+ return { error: "invalid_grant", ok: false };
4795
+ }
4796
+ if (record.expiresAt < now) {
4797
+ await config.backchannelAuthStore.deleteByAuthReqId(authReqId);
4798
+ return { error: "expired_token", ok: false };
4799
+ }
4800
+ if (record.lastPolledAt !== undefined && now - record.lastPolledAt < record.intervalSeconds * MS_PER_SECOND) {
4801
+ return { error: "slow_down", ok: false };
4802
+ }
4803
+ await config.backchannelAuthStore.recordPoll(authReqId, now);
4804
+ if (record.status === "pending") {
4805
+ return { error: "authorization_pending", ok: false };
4806
+ }
4807
+ if (record.status === "denied" || record.userSub === undefined) {
4808
+ return { error: "access_denied", ok: false };
4809
+ }
4810
+ await config.backchannelAuthStore.deleteByAuthReqId(authReqId);
4811
+ const tokenSet = await issueTokenSet({
4812
+ clientCertThumbprint,
4813
+ clientId,
4814
+ config,
4815
+ dpopJkt,
4816
+ now,
4817
+ scopes: record.scopes,
4818
+ sub: record.userSub
4819
+ });
4820
+ return { ...tokenSet, ok: true };
4821
+ };
4822
+
4823
+ // src/apikeys/tokenRoutes.ts
4824
+ var assertTokenRouteConfiguration = (apikeys, oidc) => {
4825
+ if (apikeys?.apiClientStore === undefined || apikeys.accessTokenStore === undefined || oidc === undefined)
4826
+ return;
4827
+ const apiTokenRoute = apikeys.tokenRoute ?? DEFAULT_TOKEN_ROUTE;
4828
+ const oidcTokenRoute = `${oidc.oidcRoute ?? DEFAULT_OIDC_ROUTE}/token`;
4829
+ if (apiTokenRoute.replace(/\/$/u, "") === oidcTokenRoute) {
4830
+ 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}).`);
4831
+ }
4832
+ };
4833
+
4238
4834
  // src/agents/routes.ts
4239
4835
  import { Elysia as Elysia3 } from "elysia";
4240
4836
 
@@ -4491,108 +5087,6 @@ one resource or transport is not authority for another.
4491
5087
  // src/agents/registration.ts
4492
5088
  init_constants();
4493
5089
  init_crypto();
4494
-
4495
- // src/oidc/keys.ts
4496
- var ENCODER = new TextEncoder;
4497
- var ES256 = { hash: "SHA-256", name: "ECDSA" };
4498
- var KEY_PARAMS = { name: "ECDSA", namedCurve: "P-256" };
4499
- var ES256_JOSE_SIGNATURE_BYTES = 64;
4500
- var toBase64Url = (bytes) => Buffer.from(bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)).toString("base64url");
4501
- var fromBase64Url = (value) => new Uint8Array(Buffer.from(value, "base64url"));
4502
- var encodeSegment = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
4503
- var decodeSegment = (segment) => {
4504
- try {
4505
- const value = JSON.parse(Buffer.from(segment, "base64url").toString("utf8"));
4506
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
4507
- return;
4508
- }
4509
- return Object.fromEntries(Object.entries(value));
4510
- } catch {
4511
- return;
4512
- }
4513
- };
4514
- var generateSigningKey = async () => {
4515
- const pair = await crypto.subtle.generateKey(KEY_PARAMS, true, [
4516
- "sign",
4517
- "verify"
4518
- ]);
4519
- const privateJwk = await crypto.subtle.exportKey("jwk", pair.privateKey);
4520
- const publicJwk = await crypto.subtle.exportKey("jwk", pair.publicKey);
4521
- return { kid: await jwkThumbprint(publicJwk), privateJwk, publicJwk };
4522
- };
4523
- var jwkThumbprint = async (jwk) => {
4524
- const canonical = JSON.stringify({
4525
- crv: jwk.crv,
4526
- kty: jwk.kty,
4527
- x: jwk.x,
4528
- y: jwk.y
4529
- });
4530
- return toBase64Url(await crypto.subtle.digest("SHA-256", ENCODER.encode(canonical)));
4531
- };
4532
- var signJwt = async (payload, signing, typ = "JWT") => {
4533
- const input = `${encodeSegment({ alg: "ES256", kid: signing.kid, typ })}.${encodeSegment(payload)}`;
4534
- const encoded = ENCODER.encode(input);
4535
- let signature;
4536
- if (signing.sign !== undefined) {
4537
- signature = await signing.sign(encoded);
4538
- } else {
4539
- const key = await crypto.subtle.importKey("jwk", signing.privateJwk, KEY_PARAMS, false, ["sign"]);
4540
- signature = await crypto.subtle.sign(ES256, key, encoded);
4541
- }
4542
- if (signature.byteLength !== ES256_JOSE_SIGNATURE_BYTES) {
4543
- throw new Error("ES256 signer must return a 64-byte JOSE signature");
4544
- }
4545
- return `${input}.${toBase64Url(signature)}`;
4546
- };
4547
- var toPublicJwk = (key) => ({
4548
- alg: "ES256",
4549
- crv: key.publicJwk.crv,
4550
- kid: key.kid,
4551
- kty: key.publicJwk.kty,
4552
- use: "sig",
4553
- x: key.publicJwk.x,
4554
- y: key.publicJwk.y
4555
- });
4556
- var verifyJwt = async (token, publicJwk) => {
4557
- const [headerSegment, payloadSegment, signatureSegment] = token.split(".");
4558
- if (headerSegment === undefined || payloadSegment === undefined || signatureSegment === undefined) {
4559
- return;
4560
- }
4561
- const key = await crypto.subtle.importKey("jwk", publicJwk, KEY_PARAMS, false, ["verify"]);
4562
- const valid = await crypto.subtle.verify(ES256, key, fromBase64Url(signatureSegment), ENCODER.encode(`${headerSegment}.${payloadSegment}`));
4563
- if (!valid)
4564
- return;
4565
- const header = decodeSegment(headerSegment);
4566
- const payload = decodeSegment(payloadSegment);
4567
- if (header === undefined || payload === undefined)
4568
- return;
4569
- return {
4570
- header,
4571
- payload
4572
- };
4573
- };
4574
- var signingVerificationKeys = (active, previous = []) => {
4575
- const keys = [active, ...previous];
4576
- const keyIds = new Set(keys.map(({ kid }) => kid));
4577
- if (keyIds.size !== keys.length)
4578
- throw new Error("OIDC signing key IDs must be unique");
4579
- return keys;
4580
- };
4581
- var verifyJwtWithKeys = async (token, keys) => {
4582
- const [headerSegment] = token.split(".");
4583
- if (!headerSegment)
4584
- return;
4585
- const header = decodeSegment(headerSegment);
4586
- const kid = header?.kid;
4587
- if (typeof kid !== "string" || kid.length === 0)
4588
- return;
4589
- const key = keys.find((candidate) => candidate.kid === kid);
4590
- if (!key)
4591
- return;
4592
- return verifyJwt(token, key.publicJwk);
4593
- };
4594
-
4595
- // src/agents/registration.ts
4596
5090
  var AGENT_CLAIM_GRANT_TYPE = "urn:workos:agent-auth:grant-type:claim";
4597
5091
  var AGENT_IDENTITY_ASSERTION_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer";
4598
5092
  var AGENT_IDENTITY_ASSERTION_TYPE = "urn:ietf:params:oauth:token-type:id-jag";
@@ -4603,12 +5097,12 @@ var DEFAULT_GUIDE_ROUTE2 = "/auth.md";
4603
5097
  var DEFAULT_CLAIM_TTL_MS = 24 * 60 * MILLISECONDS_IN_A_MINUTE;
4604
5098
  var DEFAULT_ATTEMPT_TTL_MS = 10 * MILLISECONDS_IN_A_MINUTE;
4605
5099
  var DEFAULT_ASSERTION_TTL_MS = 60 * MILLISECONDS_IN_A_MINUTE;
4606
- var DEFAULT_ACCESS_TOKEN_TTL_MS2 = 15 * MILLISECONDS_IN_A_MINUTE;
5100
+ var DEFAULT_ACCESS_TOKEN_TTL_MS3 = 15 * MILLISECONDS_IN_A_MINUTE;
4607
5101
  var DEFAULT_MAX_AUTH_AGE_MS = 60 * MILLISECONDS_IN_A_MINUTE;
4608
5102
  var DEFAULT_POLL_INTERVAL_SECONDS = 5;
4609
5103
  var DEFAULT_MAX_CODE_ATTEMPTS = 5;
4610
5104
  var MAX_CONCURRENT_UPDATE_RETRIES = 5;
4611
- var TOKEN_BYTES2 = 32;
5105
+ var TOKEN_BYTES3 = 32;
4612
5106
  var agentRegistrationDiscoveryMetadata = (config) => {
4613
5107
  const registration = requiredRegistration(config);
4614
5108
  const endpoints = agentRegistrationEndpoints(config);
@@ -4724,7 +5218,7 @@ var randomCode = () => {
4724
5218
  const value = new DataView(bytes.buffer).getUint32(0) % 1e6;
4725
5219
  return value.toString().padStart(6, "0");
4726
5220
  };
4727
- var makeSecret = (prefix) => `${prefix}_${generateSecureToken(TOKEN_BYTES2)}`;
5221
+ var makeSecret = (prefix) => `${prefix}_${generateSecureToken(TOKEN_BYTES3)}`;
4728
5222
  var makeAttempt = async ({
4729
5223
  email,
4730
5224
  now,
@@ -5053,9 +5547,9 @@ var completeAgentClaim = async (config, input, now = Date.now()) => {
5053
5547
  };
5054
5548
  var issueAgentAccessToken = async (config, flow, now) => {
5055
5549
  const registration = requiredRegistration(config);
5056
- const accessToken = `at_${generateSecureToken(TOKEN_BYTES2)}`;
5550
+ const accessToken = `at_${generateSecureToken(TOKEN_BYTES3)}`;
5057
5551
  const scopes = (flow.status === "claimed" ? registration.postClaimScopes : registration.preClaimScopes ?? []).filter((scope) => config.scopes.includes(scope));
5058
- const expiresAt = now + (registration.tokenTtlMs ?? DEFAULT_ACCESS_TOKEN_TTL_MS2);
5552
+ const expiresAt = now + (registration.tokenTtlMs ?? DEFAULT_ACCESS_TOKEN_TTL_MS3);
5059
5553
  await registration.accessTokenStore.saveToken({
5060
5554
  clientId: flow.agentId,
5061
5555
  createdAt: now,
@@ -8287,489 +8781,6 @@ init_constants();
8287
8781
  init_crypto();
8288
8782
  import { Elysia as Elysia24, t as t17 } from "elysia";
8289
8783
 
8290
- // src/oidc/config.ts
8291
- init_constants();
8292
- init_crypto();
8293
- var DEFAULT_OIDC_ROUTE = "/oauth2";
8294
- var MS_PER_SECOND = 1000;
8295
- var TOKEN_BYTES3 = 32;
8296
- var REFRESH_TTL_DAYS = 30;
8297
- var DEFAULT_ACCESS_TOKEN_TTL_MS3 = MILLISECONDS_IN_AN_HOUR;
8298
- var DEFAULT_ID_TOKEN_TTL_MS = MILLISECONDS_IN_AN_HOUR;
8299
- var DEFAULT_REFRESH_TOKEN_TTL_MS = MILLISECONDS_IN_A_DAY * REFRESH_TTL_DAYS;
8300
- var resolveAccessTtl = (ttl, scopes) => {
8301
- if (typeof ttl === "function")
8302
- return ttl({ scopes });
8303
- return ttl ?? DEFAULT_ACCESS_TOKEN_TTL_MS3;
8304
- };
8305
- var nowSeconds = (milliseconds) => Math.floor(milliseconds / MS_PER_SECOND);
8306
- var narrowScopes = (available, requested) => requested === undefined || requested.length === 0 ? available : requested.filter((scope) => available.includes(scope));
8307
- var RESERVED_ACCESS_CLAIMS = new Set([
8308
- "act",
8309
- "aud",
8310
- "client_id",
8311
- "cnf",
8312
- "exp",
8313
- "iat",
8314
- "iss",
8315
- "jti",
8316
- "scope",
8317
- "sub",
8318
- "token_use"
8319
- ]);
8320
- var buildAccessClaims = ({
8321
- act,
8322
- audience,
8323
- clientCertThumbprint,
8324
- clientId,
8325
- dpopJkt,
8326
- extraClaims,
8327
- issuer,
8328
- now,
8329
- scopes,
8330
- sub,
8331
- ttl
8332
- }) => {
8333
- const safeExtra = extraClaims === undefined ? {} : Object.fromEntries(Object.entries(extraClaims).filter(([key]) => !RESERVED_ACCESS_CLAIMS.has(key)));
8334
- const claims = {
8335
- ...safeExtra,
8336
- aud: audience ?? clientId,
8337
- client_id: clientId,
8338
- exp: nowSeconds(now + ttl),
8339
- iat: nowSeconds(now),
8340
- iss: issuer,
8341
- jti: crypto.randomUUID(),
8342
- scope: scopes.join(" "),
8343
- sub,
8344
- token_use: "access"
8345
- };
8346
- if (act !== undefined)
8347
- claims.act = act;
8348
- const cnf = {
8349
- ...dpopJkt === undefined ? {} : { jkt: dpopJkt },
8350
- ...clientCertThumbprint === undefined ? {} : { "x5t#S256": clientCertThumbprint }
8351
- };
8352
- if (Object.keys(cnf).length > 0) {
8353
- claims.cnf = cnf;
8354
- }
8355
- return claims;
8356
- };
8357
- var exchangeToken = async ({
8358
- actorClientId,
8359
- audience,
8360
- config,
8361
- dpopJkt,
8362
- now = Date.now(),
8363
- requestedScopes,
8364
- subjectToken
8365
- }) => {
8366
- const verified = await verifyJwtWithKeys(subjectToken, signingVerificationKeys(config.signingKey, config.previousSigningKeys));
8367
- const payload = verified?.payload;
8368
- if (payload === undefined || typeof payload.sub !== "string" || typeof payload.exp !== "number" || payload.exp <= nowSeconds(now)) {
8369
- return { error: "invalid_grant", ok: false };
8370
- }
8371
- const available = typeof payload.scope === "string" ? payload.scope.split(" ") : [];
8372
- if (requestedScopes?.some((scope) => !available.includes(scope)) === true) {
8373
- return { error: "invalid_scope", ok: false };
8374
- }
8375
- const scopes = narrowScopes(available, requestedScopes);
8376
- const ttl = resolveAccessTtl(config.accessTokenTtlMs, scopes);
8377
- const extraClaims = await config.getAccessTokenClaims?.({
8378
- audience,
8379
- clientId: actorClientId,
8380
- scopes,
8381
- sub: payload.sub
8382
- });
8383
- return {
8384
- accessToken: await signJwt(buildAccessClaims({
8385
- act: { sub: actorClientId },
8386
- audience,
8387
- clientId: actorClientId,
8388
- dpopJkt,
8389
- extraClaims,
8390
- issuer: config.issuer,
8391
- now,
8392
- scopes,
8393
- sub: payload.sub,
8394
- ttl
8395
- }), config.signingKey),
8396
- expiresIn: Math.floor(ttl / MS_PER_SECOND),
8397
- ok: true,
8398
- scope: scopes.join(" ")
8399
- };
8400
- };
8401
- var issueTokenSet = async ({
8402
- acr,
8403
- audience,
8404
- claims,
8405
- clientCertThumbprint,
8406
- clientId,
8407
- config,
8408
- dpopJkt,
8409
- familyId,
8410
- nonce,
8411
- now = Date.now(),
8412
- persistRefreshToken,
8413
- scopes,
8414
- sub
8415
- }) => {
8416
- const accessTtl = resolveAccessTtl(config.accessTokenTtlMs, scopes);
8417
- const idTtl = config.idTokenTtlMs ?? DEFAULT_ID_TOKEN_TTL_MS;
8418
- const refreshTtl = config.refreshTokenTtlMs ?? DEFAULT_REFRESH_TOKEN_TTL_MS;
8419
- const accessExtra = await config.getAccessTokenClaims?.({
8420
- audience,
8421
- clientId,
8422
- scopes,
8423
- sub
8424
- });
8425
- const accessPayload = buildAccessClaims({
8426
- audience,
8427
- clientCertThumbprint,
8428
- clientId,
8429
- dpopJkt,
8430
- extraClaims: { ...accessExtra, ...acr === undefined ? {} : { acr } },
8431
- issuer: config.issuer,
8432
- now,
8433
- scopes,
8434
- sub,
8435
- ttl: accessTtl
8436
- });
8437
- const idPayload = {
8438
- ...claims,
8439
- aud: clientId,
8440
- exp: nowSeconds(now + idTtl),
8441
- iat: nowSeconds(now),
8442
- iss: config.issuer,
8443
- sub
8444
- };
8445
- if (nonce !== undefined)
8446
- idPayload.nonce = nonce;
8447
- if (acr !== undefined)
8448
- idPayload.acr = acr;
8449
- const refreshToken = generateSecureToken(TOKEN_BYTES3);
8450
- const refreshRecord = {
8451
- acr,
8452
- audience,
8453
- claims,
8454
- clientId,
8455
- createdAt: now,
8456
- dpopJkt,
8457
- expiresAt: now + refreshTtl,
8458
- familyId: familyId ?? crypto.randomUUID(),
8459
- scopes,
8460
- tokenHash: await hashToken(refreshToken),
8461
- userId: sub
8462
- };
8463
- if (persistRefreshToken)
8464
- await persistRefreshToken(refreshRecord);
8465
- else
8466
- await config.refreshTokenStore.saveToken(refreshRecord);
8467
- return {
8468
- access_token: await signJwt(accessPayload, config.signingKey),
8469
- expires_in: Math.floor(accessTtl / MS_PER_SECOND),
8470
- id_token: await signJwt(idPayload, config.signingKey),
8471
- refresh_token: refreshToken,
8472
- scope: scopes.join(" "),
8473
- token_type: dpopJkt === undefined ? "Bearer" : "DPoP"
8474
- };
8475
- };
8476
- var mcpProtectedResourceMetadata = ({
8477
- issuer,
8478
- resource,
8479
- scopes
8480
- }) => ({
8481
- authorization_servers: [issuer],
8482
- resource,
8483
- scopes_supported: scopes ?? []
8484
- });
8485
- var verifyPkce = async (codeVerifier, codeChallenge) => await hashToken(codeVerifier) === codeChallenge;
8486
- var inactive = { active: false };
8487
- var introspectToken = async ({
8488
- config,
8489
- hint,
8490
- now = Date.now(),
8491
- token
8492
- }) => {
8493
- if (hint !== "refresh_token") {
8494
- const verified = await verifyJwtWithKeys(token, signingVerificationKeys(config.signingKey, config.previousSigningKeys));
8495
- const payload = verified?.payload;
8496
- if (payload !== undefined && typeof payload.sub === "string" && typeof payload.exp === "number" && payload.exp > nowSeconds(now)) {
8497
- return {
8498
- active: true,
8499
- client_id: typeof payload.client_id === "string" ? payload.client_id : "",
8500
- exp: payload.exp,
8501
- iat: typeof payload.iat === "number" ? payload.iat : 0,
8502
- scope: typeof payload.scope === "string" ? payload.scope : "",
8503
- sub: payload.sub,
8504
- token_type: "access_token"
8505
- };
8506
- }
8507
- }
8508
- if (hint !== "access_token") {
8509
- const refresh = await config.refreshTokenStore.getToken(await hashToken(token));
8510
- if (refresh && refresh.expiresAt > now) {
8511
- return {
8512
- active: true,
8513
- client_id: refresh.clientId,
8514
- exp: nowSeconds(refresh.expiresAt),
8515
- iat: nowSeconds(refresh.createdAt),
8516
- scope: refresh.scopes.join(" "),
8517
- sub: refresh.userId,
8518
- token_type: "refresh_token"
8519
- };
8520
- }
8521
- }
8522
- return inactive;
8523
- };
8524
- var revokeRefreshToken = async (config, token) => {
8525
- const consumed = await config.refreshTokenStore.consumeToken(await hashToken(token));
8526
- return consumed !== undefined;
8527
- };
8528
- var DEVICE_CODE_BYTES = 32;
8529
- var USER_CODE_HALF_LENGTH = 4;
8530
- var USER_CODE_ALPHABET = "BCDFGHJKLMNPQRSTVWXZ23456789";
8531
- var DEFAULT_DEVICE_CODE_TTL_MINUTES = 15;
8532
- var DEFAULT_DEVICE_CODE_TTL_MS = MILLISECONDS_IN_A_MINUTE * DEFAULT_DEVICE_CODE_TTL_MINUTES;
8533
- var DEFAULT_DEVICE_POLL_INTERVAL_SECONDS = 5;
8534
- var generateUserCode = () => {
8535
- const length = USER_CODE_HALF_LENGTH * 2;
8536
- const random = crypto.getRandomValues(new Uint8Array(length));
8537
- let code = "";
8538
- for (const byte of random) {
8539
- code += USER_CODE_ALPHABET[byte % USER_CODE_ALPHABET.length];
8540
- }
8541
- return `${code.slice(0, USER_CODE_HALF_LENGTH)}-${code.slice(USER_CODE_HALF_LENGTH)}`;
8542
- };
8543
- var issueDeviceAuthorization = async ({
8544
- audience,
8545
- clientId,
8546
- config,
8547
- now = Date.now(),
8548
- requestedScopes
8549
- }) => {
8550
- if (!config.deviceAuthorizationStore) {
8551
- throw new Error("oidc.deviceAuthorizationStore is not configured \u2014 cannot start a device flow");
8552
- }
8553
- const deviceCode = generateSecureToken(DEVICE_CODE_BYTES);
8554
- const userCode = generateUserCode();
8555
- const ttl = config.deviceCodeTtlMs ?? DEFAULT_DEVICE_CODE_TTL_MS;
8556
- const interval = config.devicePollIntervalSeconds ?? DEFAULT_DEVICE_POLL_INTERVAL_SECONDS;
8557
- await config.deviceAuthorizationStore.saveDeviceAuthorization({
8558
- audience,
8559
- clientId,
8560
- createdAt: now,
8561
- deviceCodeHash: await hashToken(deviceCode),
8562
- expiresAt: now + ttl,
8563
- intervalSeconds: interval,
8564
- scopes: requestedScopes,
8565
- status: "pending",
8566
- userCode
8567
- });
8568
- const verificationUri = `${config.issuer}${config.oidcRoute ?? DEFAULT_OIDC_ROUTE}/device`;
8569
- return {
8570
- device_code: deviceCode,
8571
- expires_in: Math.floor(ttl / MS_PER_SECOND),
8572
- interval,
8573
- user_code: userCode,
8574
- verification_uri: verificationUri,
8575
- verification_uri_complete: `${verificationUri}?user_code=${encodeURIComponent(userCode)}`
8576
- };
8577
- };
8578
- var decideDeviceAuthorization = async (config, userCode, approval) => {
8579
- if (!config.deviceAuthorizationStore) {
8580
- return { error: "not_configured", ok: false };
8581
- }
8582
- const record = await config.deviceAuthorizationStore.findByUserCode(userCode);
8583
- if (!record)
8584
- return { error: "invalid_user_code", ok: false };
8585
- if (record.expiresAt < Date.now()) {
8586
- return { error: "expired_token", ok: false };
8587
- }
8588
- if (record.status !== "pending") {
8589
- return { error: "already_decided", ok: false };
8590
- }
8591
- await config.deviceAuthorizationStore.updateStatus(record.deviceCodeHash, approval.status, approval.userSub);
8592
- if (approval.status === "approved" && approval.userSub !== undefined) {
8593
- await config.onDeviceAuthorizationApproved?.({
8594
- clientId: record.clientId,
8595
- scopes: record.scopes,
8596
- userSub: approval.userSub
8597
- });
8598
- }
8599
- return { ok: true };
8600
- };
8601
- var approveDeviceAuthorization = async ({
8602
- config,
8603
- userCode,
8604
- userSub
8605
- }) => decideDeviceAuthorization(config, userCode, {
8606
- status: "approved",
8607
- userSub
8608
- });
8609
- var denyDeviceAuthorization = async ({
8610
- config,
8611
- userCode
8612
- }) => decideDeviceAuthorization(config, userCode, { status: "denied" });
8613
- var exchangeDeviceCode = async ({
8614
- clientId,
8615
- config,
8616
- deviceCode,
8617
- dpopJkt,
8618
- now = Date.now()
8619
- }) => {
8620
- if (!config.deviceAuthorizationStore) {
8621
- return { error: "invalid_grant", ok: false };
8622
- }
8623
- const deviceCodeHash = await hashToken(deviceCode);
8624
- const record = await config.deviceAuthorizationStore.findByDeviceCodeHash(deviceCodeHash);
8625
- if (!record || record.clientId !== clientId) {
8626
- return { error: "invalid_grant", ok: false };
8627
- }
8628
- if (record.expiresAt < now) {
8629
- await config.deviceAuthorizationStore.deleteByDeviceCodeHash(deviceCodeHash);
8630
- return { error: "expired_token", ok: false };
8631
- }
8632
- if (record.status === "pending") {
8633
- return { error: "authorization_pending", ok: false };
8634
- }
8635
- if (record.status === "denied" || record.userSub === undefined) {
8636
- return { error: "access_denied", ok: false };
8637
- }
8638
- await config.deviceAuthorizationStore.deleteByDeviceCodeHash(deviceCodeHash);
8639
- const tokenSet = await issueTokenSet({
8640
- audience: record.audience,
8641
- clientId,
8642
- config,
8643
- dpopJkt,
8644
- now,
8645
- scopes: record.scopes,
8646
- sub: record.userSub
8647
- });
8648
- return { ...tokenSet, ok: true };
8649
- };
8650
- var AUTH_REQ_ID_BYTES = 32;
8651
- var DEFAULT_BACKCHANNEL_TTL_MINUTES = 10;
8652
- var DEFAULT_BACKCHANNEL_TTL_MS = MILLISECONDS_IN_A_MINUTE * DEFAULT_BACKCHANNEL_TTL_MINUTES;
8653
- var DEFAULT_BACKCHANNEL_POLL_INTERVAL_SECONDS = 5;
8654
- var CIBA_GRANT_TYPE = "urn:openid:params:grant-type:ciba";
8655
- var issueBackchannelAuth = async ({
8656
- clientId,
8657
- config,
8658
- loginHint,
8659
- bindingMessage,
8660
- now = Date.now(),
8661
- requestedScopes
8662
- }) => {
8663
- if (!config.backchannelAuthStore || !config.resolveBackchannelUser) {
8664
- return { error: "invalid_request", ok: false };
8665
- }
8666
- const client = await config.clientStore.findClient(clientId) ?? await config.resolveClientIdMetadata?.(clientId);
8667
- if (!client)
8668
- return { error: "invalid_client", ok: false };
8669
- const resolved = await config.resolveBackchannelUser({
8670
- client,
8671
- loginHint
8672
- });
8673
- if (!resolved)
8674
- return { error: "unknown_user_id", ok: false };
8675
- const authReqId = generateSecureToken(AUTH_REQ_ID_BYTES);
8676
- const ttl = config.backchannelAuthTtlMs ?? DEFAULT_BACKCHANNEL_TTL_MS;
8677
- const interval = config.backchannelPollIntervalSeconds ?? DEFAULT_BACKCHANNEL_POLL_INTERVAL_SECONDS;
8678
- await config.backchannelAuthStore.saveBackchannelAuth({
8679
- authReqId,
8680
- bindingMessage,
8681
- clientId,
8682
- createdAt: now,
8683
- expiresAt: now + ttl,
8684
- intervalSeconds: interval,
8685
- scopes: requestedScopes,
8686
- status: "pending",
8687
- userSub: resolved.sub
8688
- });
8689
- await config.onBackchannelAuthRequest?.({
8690
- authReqId,
8691
- bindingMessage,
8692
- clientId,
8693
- scopes: requestedScopes,
8694
- userSub: resolved.sub
8695
- });
8696
- return {
8697
- auth_req_id: authReqId,
8698
- expires_in: Math.floor(ttl / MS_PER_SECOND),
8699
- interval,
8700
- ok: true
8701
- };
8702
- };
8703
- var decideBackchannel = async (config, authReqId, approval) => {
8704
- if (!config.backchannelAuthStore) {
8705
- return { error: "not_configured", ok: false };
8706
- }
8707
- const record = await config.backchannelAuthStore.findByAuthReqId(authReqId);
8708
- if (!record)
8709
- return { error: "invalid_auth_req_id", ok: false };
8710
- if (record.expiresAt < Date.now()) {
8711
- return { error: "expired_token", ok: false };
8712
- }
8713
- if (record.status !== "pending") {
8714
- return { error: "already_decided", ok: false };
8715
- }
8716
- await config.backchannelAuthStore.updateStatus(authReqId, approval.status, approval.userSub ?? record.userSub);
8717
- return { ok: true };
8718
- };
8719
- var approveBackchannelAuth = async ({
8720
- authReqId,
8721
- config,
8722
- userSub
8723
- }) => decideBackchannel(config, authReqId, {
8724
- status: "approved",
8725
- userSub
8726
- });
8727
- var denyBackchannelAuth = async ({
8728
- authReqId,
8729
- config
8730
- }) => decideBackchannel(config, authReqId, { status: "denied" });
8731
- var exchangeBackchannelAuth = async ({
8732
- authReqId,
8733
- clientCertThumbprint,
8734
- clientId,
8735
- config,
8736
- dpopJkt,
8737
- now = Date.now()
8738
- }) => {
8739
- if (!config.backchannelAuthStore) {
8740
- return { error: "invalid_grant", ok: false };
8741
- }
8742
- const record = await config.backchannelAuthStore.findByAuthReqId(authReqId);
8743
- if (!record || record.clientId !== clientId) {
8744
- return { error: "invalid_grant", ok: false };
8745
- }
8746
- if (record.expiresAt < now) {
8747
- await config.backchannelAuthStore.deleteByAuthReqId(authReqId);
8748
- return { error: "expired_token", ok: false };
8749
- }
8750
- if (record.lastPolledAt !== undefined && now - record.lastPolledAt < record.intervalSeconds * MS_PER_SECOND) {
8751
- return { error: "slow_down", ok: false };
8752
- }
8753
- await config.backchannelAuthStore.recordPoll(authReqId, now);
8754
- if (record.status === "pending") {
8755
- return { error: "authorization_pending", ok: false };
8756
- }
8757
- if (record.status === "denied" || record.userSub === undefined) {
8758
- return { error: "access_denied", ok: false };
8759
- }
8760
- await config.backchannelAuthStore.deleteByAuthReqId(authReqId);
8761
- const tokenSet = await issueTokenSet({
8762
- clientCertThumbprint,
8763
- clientId,
8764
- config,
8765
- dpopJkt,
8766
- now,
8767
- scopes: record.scopes,
8768
- sub: record.userSub
8769
- });
8770
- return { ...tokenSet, ok: true };
8771
- };
8772
-
8773
8784
  // src/oidc/clientAuth.ts
8774
8785
  init_constants();
8775
8786
  var CLIENT_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
@@ -35333,7 +35344,7 @@ var linkedProviderBindingsTable = pgTable("linked_provider_bindings", {
35333
35344
  available_scopes: jsonb("available_scopes").$type().notNull().default([]),
35334
35345
  capabilities: jsonb("capabilities").$type().default([]),
35335
35346
  connector_provider: varchar("connector_provider", { length: 64 }).notNull(),
35336
- created_at: timestamp("created_at").notNull().defaultNow(),
35347
+ created_at: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
35337
35348
  email: varchar("email", { length: 320 }),
35338
35349
  external_account_id: varchar("external_account_id", {
35339
35350
  length: 255
@@ -35346,18 +35357,18 @@ var linkedProviderBindingsTable = pgTable("linked_provider_bindings", {
35346
35357
  label: varchar("label", { length: 255 }),
35347
35358
  metadata: jsonb("metadata").$type().default({}),
35348
35359
  status: varchar("status", { length: 64 }).$type().notNull(),
35349
- updated_at: timestamp("updated_at").notNull().defaultNow(),
35360
+ updated_at: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
35350
35361
  username: varchar("username", { length: 255 })
35351
35362
  });
35352
35363
  var linkedProviderGrantsTable = pgTable("linked_provider_grants", {
35353
35364
  access_token_ciphertext: text("access_token_ciphertext"),
35354
35365
  auth_provider_key: varchar("auth_provider_key", { length: 64 }).notNull(),
35355
- created_at: timestamp("created_at").notNull().defaultNow(),
35356
- expires_at: timestamp("expires_at"),
35366
+ created_at: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
35367
+ expires_at: timestamp("expires_at", { withTimezone: true }),
35357
35368
  granted_scopes: jsonb("granted_scopes").$type().notNull().default([]),
35358
35369
  id: varchar("id", { length: 255 }).primaryKey(),
35359
35370
  last_refresh_error: text("last_refresh_error"),
35360
- last_refreshed_at: timestamp("last_refreshed_at"),
35371
+ last_refreshed_at: timestamp("last_refreshed_at", { withTimezone: true }),
35361
35372
  metadata: jsonb("metadata").$type().default({}),
35362
35373
  owner_ref: varchar("owner_ref", { length: 255 }).notNull(),
35363
35374
  provider_family: varchar("provider_family", { length: 64 }).notNull(),
@@ -35365,7 +35376,7 @@ var linkedProviderGrantsTable = pgTable("linked_provider_grants", {
35365
35376
  refresh_token_ciphertext: text("refresh_token_ciphertext"),
35366
35377
  status: varchar("status", { length: 64 }).$type().notNull(),
35367
35378
  token_type: varchar("token_type", { length: 64 }),
35368
- updated_at: timestamp("updated_at").notNull().defaultNow()
35379
+ updated_at: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
35369
35380
  });
35370
35381
  var toTimestamp = (value) => value === undefined ? null : new Date(value);
35371
35382
  var fromTimestamp = (value) => value === null ? undefined : value.getTime();
@@ -41599,6 +41610,7 @@ var buildAuthApplications = async (configuration) => {
41599
41610
  onRevocationError,
41600
41611
  onSessionCleanup
41601
41612
  } = configuration;
41613
+ assertTokenRouteConfiguration(apikeys, oidc);
41602
41614
  if (push && nativePush2)
41603
41615
  throw new Error("Configure `push`, not both `push` and `nativePush`");
41604
41616
  const pushConfig = push ?? nativePush2;
@@ -42085,5 +42097,5 @@ export {
42085
42097
  userSessionIdTypebox
42086
42098
  };
42087
42099
 
42088
- //# debugId=7D4E0B917AED1AD264756E2164756E21
42100
+ //# debugId=56F634EDC9043EB964756E2164756E21
42089
42101
  //# sourceMappingURL=server.js.map