@absolutejs/auth 0.56.13 → 0.56.15

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/README.md CHANGED
@@ -97,6 +97,12 @@ adds a scoped `protectAgent` guard. It can also serve a generated `/auth.md`
97
97
  registration guide and matching structured OAuth metadata. This is native to
98
98
  `@absolutejs/auth`; no WorkOS service or separate package is required.
99
99
 
100
+ Applications using ordinary OAuth dynamic client registration can publish an
101
+ agent-readable `/auth.md` without enabling the separate claim/ID-JAG profile.
102
+ Set `agentAuth.oauthGuide` to the exact enabled protected resources, metadata
103
+ URLs, and scopes. Auth serves the guide and advertises it through RFC 8414
104
+ `service_documentation`; the structured OAuth metadata remains authoritative.
105
+
100
106
  Protocol-specific credentials are normalized by verifier adapters:
101
107
 
102
108
  ```ts
@@ -1,6 +1,7 @@
1
1
  import type { RouteString } from '../types';
2
2
  import type { AgentCredentialVerifier, AgentDelegationStore, AgentRegistrationStore } from './types';
3
3
  import type { AgentRegistrationProtocolConfig } from './registration';
4
+ import type { AgentOAuthGuideConfig } from './oauthGuide';
4
5
  export declare const DEFAULT_AGENT_RESOURCE_METADATA_ROUTE: RouteString;
5
6
  export type AgentRegistrationDiscoveryMetadata = {
6
7
  claim_endpoint: string;
@@ -18,6 +19,10 @@ export type AgentAuthConfig = {
18
19
  /** Open auth.md agent-registration support. Implemented natively by Absolute
19
20
  * Auth and projected through OAuth discovery; no WorkOS service is required. */
20
21
  agentRegistration?: AgentRegistrationProtocolConfig;
22
+ /** Agent-readable OAuth onboarding derived from the same protected resources
23
+ * and scopes the application actually enables. This is separate from the
24
+ * optional claim/ID-JAG registration profile. */
25
+ oauthGuide?: AgentOAuthGuideConfig;
21
26
  authorizationServer: string;
22
27
  delegationStore: AgentDelegationStore;
23
28
  logoUri?: string;
@@ -3,10 +3,11 @@ export * from './types';
3
3
  export * from './registration';
4
4
  export * from './registrationClient';
5
5
  export * from './idJag';
6
+ export * from './oauthGuide';
6
7
  export * from '../oidc/clientIdMetadata';
7
8
  export { createOidcAgentCredentialVerifier } from './oidcAdapter';
8
9
  export { agentHasScopes, resolveAgentPrincipal } from './principal';
9
10
  export { agentAuthChallenge, agentAuthContextPlugin } from './context';
10
11
  export { agentAuthPlugin } from './routes';
11
12
  export { createInMemoryAgentDelegationStore, createInMemoryAgentIdentityRegistrationStore, createInMemoryAgentRegistrationStore } from './inMemoryStores';
12
- export { agentDelegationsTable, agentIdentityRegistrationsTable, agentRegistrationsTable, createNeonAgentDelegationStore, createNeonAgentIdentityRegistrationStore, createNeonAgentRegistrationStore, createPostgresAgentDelegationStore, createPostgresAgentIdentityRegistrationStore, createPostgresAgentRegistrationStore } from './postgresStores';
13
+ export { agentDelegationsTable, agentIdentityRegistrationsTable, agentRegistrationsTable, createNeonAgentDelegationStore, createNeonAgentIdentityRegistrationStore, createNeonAgentRegistrationStore, createDrizzleAgentDelegationStore, createPostgresAgentDelegationStore, createPostgresAgentIdentityRegistrationStore, createPostgresAgentRegistrationStore } from './postgresStores';
@@ -1165,8 +1165,96 @@ var createAgentIdentityAssertionVerifier = ({
1165
1165
  subject
1166
1166
  };
1167
1167
  };
1168
+ // src/agents/oauthGuide.ts
1169
+ var DEFAULT_GUIDE_ROUTE2 = "/auth.md";
1170
+ var secureUrl2 = (value, label) => {
1171
+ const url = new URL(value);
1172
+ const loopback = url.protocol === "http:" && (url.hostname === "localhost" || url.hostname === "127.0.0.1");
1173
+ if (url.protocol !== "https:" && !loopback)
1174
+ throw new Error(`${label} must use HTTPS outside loopback development`);
1175
+ if (url.username || url.password || url.hash)
1176
+ throw new Error(`${label} cannot contain credentials or a fragment`);
1177
+ return url.toString();
1178
+ };
1179
+ var agentOAuthGuideRoute = (config) => config.route ?? DEFAULT_GUIDE_ROUTE2;
1180
+ var agentOAuthGuideUrl = (config) => new URL(agentOAuthGuideRoute(config), secureUrl2(config.authorizationServer, "OAuth authorization server")).toString();
1181
+ var normalizedResources = (config) => {
1182
+ if (config.resources.length === 0)
1183
+ throw new Error("Agent OAuth guide requires at least one resource");
1184
+ const resources = config.resources.map((resource) => ({
1185
+ metadataUrl: secureUrl2(resource.metadataUrl, "Protected-resource metadata URL"),
1186
+ name: resource.name.trim(),
1187
+ resource: secureUrl2(resource.resource, "OAuth protected resource"),
1188
+ scopes: [
1189
+ ...new Set(resource.scopes.map((scope) => scope.trim()))
1190
+ ].filter(Boolean)
1191
+ }));
1192
+ if (resources.some(({ name, scopes }) => !name || scopes.length === 0))
1193
+ throw new Error("Every agent OAuth guide resource requires a name and at least one scope");
1194
+ if (new Set(resources.map(({ resource }) => resource)).size !== resources.length)
1195
+ throw new Error("Agent OAuth guide resources must be unique");
1196
+ return resources;
1197
+ };
1198
+ var resourceSections = (resources) => resources.map((resource) => `### ${resource.name}
1199
+
1200
+ - Protected resource: \`${resource.resource}\`
1201
+ - Metadata: ${resource.metadataUrl}
1202
+ - Allowed scopes: ${resource.scopes.map((scope) => `\`${scope}\``).join(", ")}
1203
+ `).join(`
1204
+ `);
1205
+ var generateAgentOAuthGuide = (config) => {
1206
+ if (!config.serviceName.trim())
1207
+ throw new Error("Agent OAuth guide requires a service name");
1208
+ const authorizationServer = secureUrl2(config.authorizationServer, "OAuth authorization server");
1209
+ const resources = normalizedResources(config);
1210
+ const authorizationMetadata = new URL("/.well-known/oauth-authorization-server", authorizationServer).toString();
1211
+ return `# OAuth access for ${config.serviceName.trim()}
1212
+
1213
+ This guide is the agent-readable companion to the service's standards-based
1214
+ OAuth metadata. Discovery metadata is authoritative. Do not infer endpoints,
1215
+ scopes, audiences, or capabilities that are not advertised.
1216
+
1217
+ ## 1. Discover
1218
+
1219
+ Fetch the protected-resource metadata for the exact interface you intend to
1220
+ use, then fetch the authorization-server metadata at
1221
+ ${authorizationMetadata}.
1222
+
1223
+ ${resourceSections(resources)}
1224
+ ## 2. Register the OAuth client
1225
+
1226
+ Use an existing registered client or the advertised \`registration_endpoint\`.
1227
+ Request only scopes listed for the selected protected resource. Never register
1228
+ redirect URIs, grant types, or authentication methods the metadata rejects.
1229
+
1230
+ ## 3. Obtain user delegation
1231
+
1232
+ For an interactive user, use authorization code with PKCE. For a device or
1233
+ headless client, use the device authorization grant only when
1234
+ \`device_authorization_endpoint\` is advertised. Send the selected protected
1235
+ resource as the OAuth \`resource\` value and show the exact requested scopes to
1236
+ the user before consent.
1237
+
1238
+ ## 4. Call the selected interface
1239
+
1240
+ Present the access token in the Authorization header. The token must name the
1241
+ selected resource as its audience and contain the required scope. A token for
1242
+ one resource or transport is not authority for another.
1243
+
1244
+ ## Safety
1245
+
1246
+ - Never ask a user to send a password, passkey response, MFA code, device code,
1247
+ authorization code, client secret, refresh token, or access token to the
1248
+ agent or place one in model context.
1249
+ - Stop on issuer, signature, audience, expiry, delegation, or scope failure.
1250
+ - Treat consent denial, revocation, and disabled interfaces as final until the
1251
+ user explicitly starts a new authorization flow.
1252
+ - Discovery is not authorization, and this guide grants no capability by
1253
+ itself.
1254
+ `;
1255
+ };
1168
1256
  // src/oidc/clientIdMetadata.ts
1169
- var secureUrl2 = (value) => {
1257
+ var secureUrl3 = (value) => {
1170
1258
  try {
1171
1259
  return new URL(value).protocol === "https:";
1172
1260
  } catch {
@@ -1177,7 +1265,7 @@ var validateClientIdMetadataDocument = (document, expectedClientId) => {
1177
1265
  const errors = [];
1178
1266
  if (document.client_id !== expectedClientId)
1179
1267
  errors.push("client_id does not match the metadata document URL");
1180
- if (!secureUrl2(document.client_id))
1268
+ if (!secureUrl3(document.client_id))
1181
1269
  errors.push("client_id must use HTTPS");
1182
1270
  if (!Array.isArray(document.redirect_uris) || document.redirect_uris.length === 0)
1183
1271
  errors.push("redirect_uris is required");
@@ -1197,7 +1285,7 @@ var validateClientIdMetadataDocument = (document, expectedClientId) => {
1197
1285
  ["tos_uri", document.tos_uri],
1198
1286
  ["jwks_uri", document.jwks_uri]
1199
1287
  ]) {
1200
- if (value !== undefined && !secureUrl2(value))
1288
+ if (value !== undefined && !secureUrl3(value))
1201
1289
  errors.push(`${name} must use HTTPS`);
1202
1290
  }
1203
1291
  return errors;
@@ -1220,7 +1308,7 @@ var createClientIdMetadataResolver = ({
1220
1308
  }) => {
1221
1309
  const cache = new Map;
1222
1310
  return async (clientId) => {
1223
- if (!secureUrl2(clientId) || !await allow(clientId))
1311
+ if (!secureUrl3(clientId) || !await allow(clientId))
1224
1312
  return;
1225
1313
  const cached = cache.get(clientId);
1226
1314
  if (cached !== undefined && cached.expiresAt > now())
@@ -1626,7 +1714,18 @@ var agentAuthRoutes = (config) => {
1626
1714
  if (config === undefined)
1627
1715
  return plugin.as("global");
1628
1716
  if (config.agentRegistration === undefined) {
1629
- return plugin.get(config.metadataRoute ?? DEFAULT_AGENT_RESOURCE_METADATA_ROUTE, () => agentProtectedResourceMetadata(config)).as("global");
1717
+ plugin.get(config.metadataRoute ?? DEFAULT_AGENT_RESOURCE_METADATA_ROUTE, () => agentProtectedResourceMetadata(config));
1718
+ const { oauthGuide } = config;
1719
+ if (oauthGuide === undefined)
1720
+ return plugin.as("global");
1721
+ const guide = generateAgentOAuthGuide(oauthGuide);
1722
+ plugin.get(agentOAuthGuideRoute(oauthGuide), () => new Response(guide, {
1723
+ headers: {
1724
+ "cache-control": "public, max-age=300",
1725
+ "content-type": "text/markdown; charset=utf-8"
1726
+ }
1727
+ }));
1728
+ return plugin.as("global");
1630
1729
  }
1631
1730
  const registration = config.agentRegistration;
1632
1731
  const identityRoute = registration.identityRoute ?? "/agent/identity";
@@ -13469,10 +13568,10 @@ var identityRegistrationValues = (registration) => ({
13469
13568
  user_id: registration.userId ?? null,
13470
13569
  version: registration.version
13471
13570
  });
13472
- var createNeonAgentDelegationStore = (databaseUrl) => createPostgresAgentDelegationStore(createNeonDatabase(databaseUrl));
13571
+ var createNeonAgentDelegationStore = (databaseUrl) => createDrizzleAgentDelegationStore(createNeonDatabase(databaseUrl));
13473
13572
  var createNeonAgentIdentityRegistrationStore = (databaseUrl) => createPostgresAgentIdentityRegistrationStore(createNeonDatabase(databaseUrl));
13474
13573
  var createNeonAgentRegistrationStore = (databaseUrl) => createPostgresAgentRegistrationStore(createNeonDatabase(databaseUrl));
13475
- var createPostgresAgentDelegationStore = (db) => ({
13574
+ var createDrizzleAgentDelegationStore = (db) => ({
13476
13575
  findActiveDelegation: async ({
13477
13576
  agentId,
13478
13577
  now = Date.now(),
@@ -13511,6 +13610,7 @@ var createPostgresAgentDelegationStore = (db) => ({
13511
13610
  });
13512
13611
  }
13513
13612
  });
13613
+ var createPostgresAgentDelegationStore = createDrizzleAgentDelegationStore;
13514
13614
  var createPostgresAgentIdentityRegistrationStore = (db) => ({
13515
13615
  create: async (registration) => {
13516
13616
  const rows = await db.insert(agentIdentityRegistrationsTable).values(identityRegistrationValues(registration)).onConflictDoNothing().returning({ id: agentIdentityRegistrationsTable.registration_id });
@@ -13585,6 +13685,7 @@ export {
13585
13685
  issueAgentIdentityAssertion,
13586
13686
  handleAgentTokenGrant,
13587
13687
  generateAgentRegistrationGuide,
13688
+ generateAgentOAuthGuide,
13588
13689
  discoverAgentRegistration,
13589
13690
  createPostgresAgentRegistrationStore,
13590
13691
  createPostgresAgentIdentityRegistrationStore,
@@ -13597,6 +13698,7 @@ export {
13597
13698
  createInMemoryAgentIdentityRegistrationStore,
13598
13699
  createInMemoryAgentIdentityAssertionJtiStore,
13599
13700
  createInMemoryAgentDelegationStore,
13701
+ createDrizzleAgentDelegationStore,
13600
13702
  createClientIdMetadataResolver,
13601
13703
  createAgentRegistrationCredentialVerifier,
13602
13704
  createAgentRegistrationClient,
@@ -13608,6 +13710,8 @@ export {
13608
13710
  agentRegistrationEndpoints,
13609
13711
  agentRegistrationDiscoveryMetadata,
13610
13712
  agentProtectedResourceMetadata,
13713
+ agentOAuthGuideUrl,
13714
+ agentOAuthGuideRoute,
13611
13715
  agentIdentityRegistrationsTable,
13612
13716
  agentHasScopes,
13613
13717
  agentDelegationsTable,
@@ -13620,5 +13724,5 @@ export {
13620
13724
  AGENT_CLAIM_GRANT_TYPE
13621
13725
  };
13622
13726
 
13623
- //# debugId=3257C9C351E3F2FA64756E2164756E21
13727
+ //# debugId=44573BF0288F41C864756E2164756E21
13624
13728
  //# sourceMappingURL=index.js.map