@absolutejs/auth 0.56.13 → 0.56.14

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
@@ -3206,6 +3206,95 @@ var agentAuthContextPlugin = (config) => new Elysia2({
3206
3206
  }
3207
3207
  })).as("global");
3208
3208
 
3209
+ // src/agents/oauthGuide.ts
3210
+ var DEFAULT_GUIDE_ROUTE = "/auth.md";
3211
+ var secureUrl = (value, label) => {
3212
+ const url = new URL(value);
3213
+ const loopback = url.protocol === "http:" && (url.hostname === "localhost" || url.hostname === "127.0.0.1");
3214
+ if (url.protocol !== "https:" && !loopback)
3215
+ throw new Error(`${label} must use HTTPS outside loopback development`);
3216
+ if (url.username || url.password || url.hash)
3217
+ throw new Error(`${label} cannot contain credentials or a fragment`);
3218
+ return url.toString();
3219
+ };
3220
+ var agentOAuthGuideRoute = (config) => config.route ?? DEFAULT_GUIDE_ROUTE;
3221
+ var agentOAuthGuideUrl = (config) => new URL(agentOAuthGuideRoute(config), secureUrl(config.authorizationServer, "OAuth authorization server")).toString();
3222
+ var normalizedResources = (config) => {
3223
+ if (config.resources.length === 0)
3224
+ throw new Error("Agent OAuth guide requires at least one resource");
3225
+ const resources = config.resources.map((resource) => ({
3226
+ metadataUrl: secureUrl(resource.metadataUrl, "Protected-resource metadata URL"),
3227
+ name: resource.name.trim(),
3228
+ resource: secureUrl(resource.resource, "OAuth protected resource"),
3229
+ scopes: [
3230
+ ...new Set(resource.scopes.map((scope) => scope.trim()))
3231
+ ].filter(Boolean)
3232
+ }));
3233
+ if (resources.some(({ name, scopes }) => !name || scopes.length === 0))
3234
+ throw new Error("Every agent OAuth guide resource requires a name and at least one scope");
3235
+ if (new Set(resources.map(({ resource }) => resource)).size !== resources.length)
3236
+ throw new Error("Agent OAuth guide resources must be unique");
3237
+ return resources;
3238
+ };
3239
+ var resourceSections = (resources) => resources.map((resource) => `### ${resource.name}
3240
+
3241
+ - Protected resource: \`${resource.resource}\`
3242
+ - Metadata: ${resource.metadataUrl}
3243
+ - Allowed scopes: ${resource.scopes.map((scope) => `\`${scope}\``).join(", ")}
3244
+ `).join(`
3245
+ `);
3246
+ var generateAgentOAuthGuide = (config) => {
3247
+ if (!config.serviceName.trim())
3248
+ throw new Error("Agent OAuth guide requires a service name");
3249
+ const authorizationServer = secureUrl(config.authorizationServer, "OAuth authorization server");
3250
+ const resources = normalizedResources(config);
3251
+ const authorizationMetadata = new URL("/.well-known/oauth-authorization-server", authorizationServer).toString();
3252
+ return `# OAuth access for ${config.serviceName.trim()}
3253
+
3254
+ This guide is the agent-readable companion to the service's standards-based
3255
+ OAuth metadata. Discovery metadata is authoritative. Do not infer endpoints,
3256
+ scopes, audiences, or capabilities that are not advertised.
3257
+
3258
+ ## 1. Discover
3259
+
3260
+ Fetch the protected-resource metadata for the exact interface you intend to
3261
+ use, then fetch the authorization-server metadata at
3262
+ ${authorizationMetadata}.
3263
+
3264
+ ${resourceSections(resources)}
3265
+ ## 2. Register the OAuth client
3266
+
3267
+ Use an existing registered client or the advertised \`registration_endpoint\`.
3268
+ Request only scopes listed for the selected protected resource. Never register
3269
+ redirect URIs, grant types, or authentication methods the metadata rejects.
3270
+
3271
+ ## 3. Obtain user delegation
3272
+
3273
+ For an interactive user, use authorization code with PKCE. For a device or
3274
+ headless client, use the device authorization grant only when
3275
+ \`device_authorization_endpoint\` is advertised. Send the selected protected
3276
+ resource as the OAuth \`resource\` value and show the exact requested scopes to
3277
+ the user before consent.
3278
+
3279
+ ## 4. Call the selected interface
3280
+
3281
+ Present the access token in the Authorization header. The token must name the
3282
+ selected resource as its audience and contain the required scope. A token for
3283
+ one resource or transport is not authority for another.
3284
+
3285
+ ## Safety
3286
+
3287
+ - Never ask a user to send a password, passkey response, MFA code, device code,
3288
+ authorization code, client secret, refresh token, or access token to the
3289
+ agent or place one in model context.
3290
+ - Stop on issuer, signature, audience, expiry, delegation, or scope failure.
3291
+ - Treat consent denial, revocation, and disabled interfaces as final until the
3292
+ user explicitly starts a new authorization flow.
3293
+ - Discovery is not authorization, and this guide grants no capability by
3294
+ itself.
3295
+ `;
3296
+ };
3297
+
3209
3298
  // src/agents/registration.ts
3210
3299
  init_constants();
3211
3300
  init_crypto();
@@ -3317,7 +3406,7 @@ var AGENT_IDENTITY_ASSERTION_TYPE = "urn:ietf:params:oauth:token-type:id-jag";
3317
3406
  var DEFAULT_IDENTITY_ROUTE = "/agent/identity";
3318
3407
  var DEFAULT_CLAIM_ROUTE = "/agent/identity/claim";
3319
3408
  var DEFAULT_COMPLETE_ROUTE = "/agent/identity/claim/complete";
3320
- var DEFAULT_GUIDE_ROUTE = "/auth.md";
3409
+ var DEFAULT_GUIDE_ROUTE2 = "/auth.md";
3321
3410
  var DEFAULT_CLAIM_TTL_MS = 24 * 60 * MILLISECONDS_IN_A_MINUTE;
3322
3411
  var DEFAULT_ATTEMPT_TTL_MS = 10 * MILLISECONDS_IN_A_MINUTE;
3323
3412
  var DEFAULT_ASSERTION_TTL_MS = 60 * MILLISECONDS_IN_A_MINUTE;
@@ -3352,7 +3441,7 @@ var agentRegistrationEndpoints = (config) => {
3352
3441
  return {
3353
3442
  claimEndpoint: new URL(registration.claimRoute ?? DEFAULT_CLAIM_ROUTE, base).toString(),
3354
3443
  completeEndpoint: new URL(registration.completeRoute ?? DEFAULT_COMPLETE_ROUTE, base).toString(),
3355
- guide: new URL(registration.guideRoute ?? DEFAULT_GUIDE_ROUTE, base).toString(),
3444
+ guide: new URL(registration.guideRoute ?? DEFAULT_GUIDE_ROUTE2, base).toString(),
3356
3445
  identityEndpoint: new URL(registration.identityRoute ?? DEFAULT_IDENTITY_ROUTE, base).toString(),
3357
3446
  tokenEndpoint: new URL(`${oidcRoute}/token`, base).toString()
3358
3447
  };
@@ -3980,7 +4069,18 @@ var agentAuthRoutes = (config) => {
3980
4069
  if (config === undefined)
3981
4070
  return plugin.as("global");
3982
4071
  if (config.agentRegistration === undefined) {
3983
- return plugin.get(config.metadataRoute ?? DEFAULT_AGENT_RESOURCE_METADATA_ROUTE, () => agentProtectedResourceMetadata(config)).as("global");
4072
+ plugin.get(config.metadataRoute ?? DEFAULT_AGENT_RESOURCE_METADATA_ROUTE, () => agentProtectedResourceMetadata(config));
4073
+ const { oauthGuide } = config;
4074
+ if (oauthGuide === undefined)
4075
+ return plugin.as("global");
4076
+ const guide = generateAgentOAuthGuide(oauthGuide);
4077
+ plugin.get(agentOAuthGuideRoute(oauthGuide), () => new Response(guide, {
4078
+ headers: {
4079
+ "cache-control": "public, max-age=300",
4080
+ "content-type": "text/markdown; charset=utf-8"
4081
+ }
4082
+ }));
4083
+ return plugin.as("global");
3984
4084
  }
3985
4085
  const registration = config.agentRegistration;
3986
4086
  const identityRoute = registration.identityRoute ?? "/agent/identity";
@@ -26763,7 +26863,7 @@ var createPostgresScimTokenStore = (db) => ({
26763
26863
  });
26764
26864
  // src/agents/registrationClient.ts
26765
26865
  var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
26766
- var secureUrl = (value, allowLocalhost) => {
26866
+ var secureUrl2 = (value, allowLocalhost) => {
26767
26867
  if (typeof value !== "string")
26768
26868
  return;
26769
26869
  try {
@@ -26853,32 +26953,32 @@ var discoverAgentRegistration = async (resource, options = {}) => {
26853
26953
  const request = options.request ?? fetch;
26854
26954
  const maxBytes = options.maxResponseBytes ?? 256 * 1024;
26855
26955
  const allowLocalhost = options.allowInsecureLocalhost === true;
26856
- const resourceUrl = secureUrl(resource, allowLocalhost);
26956
+ const resourceUrl = secureUrl2(resource, allowLocalhost);
26857
26957
  if (resourceUrl === undefined)
26858
26958
  throw new Error("Resource URL must use HTTPS");
26859
26959
  const resourceMetadataUrl = new URL("/.well-known/oauth-protected-resource", resourceUrl).toString();
26860
26960
  const prm = await requestJson(request, resourceMetadataUrl, {}, maxBytes);
26861
- const advertisedResource = secureUrl(prm.body.resource, allowLocalhost);
26961
+ const advertisedResource = secureUrl2(prm.body.resource, allowLocalhost);
26862
26962
  if (advertisedResource === undefined || advertisedResource !== resourceUrl) {
26863
26963
  throw new Error("Protected resource metadata identity mismatch");
26864
26964
  }
26865
- const authorizationServer = secureUrl(stringArray(prm.body.authorization_servers)[0], allowLocalhost);
26965
+ const authorizationServer = secureUrl2(stringArray(prm.body.authorization_servers)[0], allowLocalhost);
26866
26966
  if (authorizationServer === undefined) {
26867
26967
  throw new Error("No secure authorization server is advertised");
26868
26968
  }
26869
26969
  const asUrl = new URL("/.well-known/oauth-authorization-server", authorizationServer).toString();
26870
26970
  const metadata = await requestJson(request, asUrl, {}, maxBytes);
26871
- if (secureUrl(metadata.body.issuer, allowLocalhost) !== authorizationServer) {
26971
+ if (secureUrl2(metadata.body.issuer, allowLocalhost) !== authorizationServer) {
26872
26972
  throw new Error("Authorization server issuer mismatch");
26873
26973
  }
26874
- const tokenEndpoint = secureUrl(metadata.body.token_endpoint, allowLocalhost);
26974
+ const tokenEndpoint = secureUrl2(metadata.body.token_endpoint, allowLocalhost);
26875
26975
  const agentAuth = metadata.body.agent_auth;
26876
26976
  if (tokenEndpoint === undefined || !isObject2(agentAuth)) {
26877
26977
  throw new Error("Authorization server does not advertise agent registration");
26878
26978
  }
26879
- const identityEndpoint = secureUrl(agentAuth.identity_endpoint, allowLocalhost);
26880
- const claimEndpoint = secureUrl(agentAuth.claim_endpoint, allowLocalhost);
26881
- const skill = secureUrl(agentAuth.skill, allowLocalhost);
26979
+ const identityEndpoint = secureUrl2(agentAuth.identity_endpoint, allowLocalhost);
26980
+ const claimEndpoint = secureUrl2(agentAuth.claim_endpoint, allowLocalhost);
26981
+ const skill = secureUrl2(agentAuth.skill, allowLocalhost);
26882
26982
  const assertionMetadata = agentAuth.identity_assertion;
26883
26983
  if (identityEndpoint === undefined || claimEndpoint === undefined || skill === undefined || !isObject2(assertionMetadata)) {
26884
26984
  throw new Error("Agent registration metadata is incomplete");
@@ -27638,7 +27738,7 @@ var createPostgresApiKeyStore = (db) => ({
27638
27738
  }
27639
27739
  });
27640
27740
  // src/oidc/clientIdMetadata.ts
27641
- var secureUrl2 = (value) => {
27741
+ var secureUrl3 = (value) => {
27642
27742
  try {
27643
27743
  return new URL(value).protocol === "https:";
27644
27744
  } catch {
@@ -27649,7 +27749,7 @@ var validateClientIdMetadataDocument = (document, expectedClientId) => {
27649
27749
  const errors = [];
27650
27750
  if (document.client_id !== expectedClientId)
27651
27751
  errors.push("client_id does not match the metadata document URL");
27652
- if (!secureUrl2(document.client_id))
27752
+ if (!secureUrl3(document.client_id))
27653
27753
  errors.push("client_id must use HTTPS");
27654
27754
  if (!Array.isArray(document.redirect_uris) || document.redirect_uris.length === 0)
27655
27755
  errors.push("redirect_uris is required");
@@ -27669,7 +27769,7 @@ var validateClientIdMetadataDocument = (document, expectedClientId) => {
27669
27769
  ["tos_uri", document.tos_uri],
27670
27770
  ["jwks_uri", document.jwks_uri]
27671
27771
  ]) {
27672
- if (value !== undefined && !secureUrl2(value))
27772
+ if (value !== undefined && !secureUrl3(value))
27673
27773
  errors.push(`${name2} must use HTTPS`);
27674
27774
  }
27675
27775
  return errors;
@@ -27692,7 +27792,7 @@ var createClientIdMetadataResolver = ({
27692
27792
  }) => {
27693
27793
  const cache = new Map;
27694
27794
  return async (clientId) => {
27695
- if (!secureUrl2(clientId) || !await allow(clientId))
27795
+ if (!secureUrl3(clientId) || !await allow(clientId))
27696
27796
  return;
27697
27797
  const cached = cache.get(clientId);
27698
27798
  if (cached !== undefined && cached.expiresAt > now())
@@ -30371,6 +30471,22 @@ var buildAuthApplications = async (configuration) => {
30371
30471
  tokenTtlMs: registration2.tokenTtlMs
30372
30472
  });
30373
30473
  }
30474
+ if (agentAuth?.oauthGuide !== undefined) {
30475
+ if (oidc === undefined)
30476
+ throw new Error("agentAuth.oauthGuide requires the OIDC provider");
30477
+ if (agentAuth.authorizationServer !== oidc.issuer)
30478
+ throw new Error("agentAuth.authorizationServer must equal oidc.issuer");
30479
+ if (agentAuth.oauthGuide.authorizationServer !== agentAuth.authorizationServer)
30480
+ throw new Error("agentAuth.oauthGuide.authorizationServer must equal agentAuth.authorizationServer");
30481
+ const unknownGuideScopes = agentAuth.oauthGuide.resources.flatMap(({ scopes }) => scopes).filter((scope) => !agentAuth.scopes.includes(scope));
30482
+ if (unknownGuideScopes.length > 0)
30483
+ throw new Error(`Agent OAuth guide uses undeclared scopes: ${[
30484
+ ...new Set(unknownGuideScopes)
30485
+ ].join(", ")}`);
30486
+ if (agentAuth.agentRegistration !== undefined && (agentAuth.agentRegistration.guideRoute ?? "/auth.md") === (agentAuth.oauthGuide.route ?? "/auth.md"))
30487
+ throw new Error("Agent OAuth and registration guides cannot use the same route");
30488
+ generateAgentOAuthGuide(agentAuth.oauthGuide);
30489
+ }
30374
30490
  if (tracing !== undefined)
30375
30491
  await initTracing(tracing);
30376
30492
  const clientProviders = await buildClientProviders(providersConfiguration, createOAuth2Client, customProviders);
@@ -30392,6 +30508,9 @@ var buildAuthApplications = async (configuration) => {
30392
30508
  ...oidc,
30393
30509
  additionalDiscoveryMetadata: {
30394
30510
  ...oidc.additionalDiscoveryMetadata,
30511
+ ...resolvedAgentAuth?.oauthGuide === undefined ? {} : {
30512
+ service_documentation: agentOAuthGuideUrl(resolvedAgentAuth.oauthGuide)
30513
+ },
30395
30514
  ...resolvedAgentAuth?.agentRegistration === undefined ? {} : {
30396
30515
  agent_auth: agentRegistrationDiscoveryMetadata(resolvedAgentAuth)
30397
30516
  }
@@ -30611,5 +30730,5 @@ export {
30611
30730
  auth2 as auth
30612
30731
  };
30613
30732
 
30614
- //# debugId=B037EE5900B85A3664756E2164756E21
30733
+ //# debugId=E2F70FC52266FF6C64756E2164756E21
30615
30734
  //# sourceMappingURL=server.js.map