@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 +6 -0
- package/dist/agents/config.d.ts +5 -0
- package/dist/agents/index.d.ts +2 -1
- package/dist/agents/index.js +112 -8
- package/dist/agents/index.js.map +7 -6
- package/dist/agents/oauthGuide.d.ts +18 -0
- package/dist/agents/postgresStores.d.ts +2 -0
- package/dist/agents/routes.d.ts +1 -66
- package/dist/cli/migrate.js.map +1 -1
- package/dist/index.d.ts +3 -21
- package/dist/index.js +143 -19
- package/dist/index.js.map +8 -7
- package/dist/server.js +139 -19
- package/dist/server.js.map +8 -7
- package/docs/AGENT-AUTH.md +7 -0
- package/package.json +1 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { RouteString } from '../types';
|
|
2
|
+
export type AgentOAuthGuideResource = {
|
|
3
|
+
metadataUrl: string;
|
|
4
|
+
name: string;
|
|
5
|
+
resource: string;
|
|
6
|
+
scopes: readonly string[];
|
|
7
|
+
};
|
|
8
|
+
export type AgentOAuthGuideConfig = {
|
|
9
|
+
authorizationServer: string;
|
|
10
|
+
resources: readonly AgentOAuthGuideResource[];
|
|
11
|
+
route?: RouteString;
|
|
12
|
+
serviceName: string;
|
|
13
|
+
};
|
|
14
|
+
export declare const agentOAuthGuideRoute: (config: AgentOAuthGuideConfig) => `/${string}`;
|
|
15
|
+
export declare const agentOAuthGuideUrl: (config: AgentOAuthGuideConfig) => string;
|
|
16
|
+
/** Generate an agent-readable OAuth companion from exact application-owned
|
|
17
|
+
* resource metadata. OAuth discovery remains authoritative. */
|
|
18
|
+
export declare const generateAgentOAuthGuide: (config: AgentOAuthGuideConfig) => string;
|
|
@@ -584,6 +584,8 @@ export declare const agentRegistrationsTable: import("drizzle-orm/pg-core").PgTa
|
|
|
584
584
|
export declare const createNeonAgentDelegationStore: (databaseUrl: string) => AgentDelegationStore;
|
|
585
585
|
export declare const createNeonAgentIdentityRegistrationStore: (databaseUrl: string) => AgentIdentityRegistrationStore;
|
|
586
586
|
export declare const createNeonAgentRegistrationStore: (databaseUrl: string) => AgentRegistrationStore;
|
|
587
|
+
export declare const createDrizzleAgentDelegationStore: <DB extends AnyPgDatabase>(db: DB) => AgentDelegationStore;
|
|
588
|
+
/** @deprecated Use createDrizzleAgentDelegationStore. */
|
|
587
589
|
export declare const createPostgresAgentDelegationStore: <DB extends AnyPgDatabase>(db: DB) => AgentDelegationStore;
|
|
588
590
|
export declare const createPostgresAgentIdentityRegistrationStore: <DB extends AnyPgDatabase>(db: DB) => AgentIdentityRegistrationStore;
|
|
589
591
|
export declare const createPostgresAgentRegistrationStore: <DB extends AnyPgDatabase>(db: DB) => AgentRegistrationStore;
|
package/dist/agents/routes.d.ts
CHANGED
|
@@ -28,26 +28,7 @@ export declare const agentAuthPlugin: (config?: AgentAuthConfig) => Elysia<"", {
|
|
|
28
28
|
message: "Agent is not authenticated" | "Insufficient agent scopes";
|
|
29
29
|
}) => AuthFailReturn | Promise<AuthFailReturn>) => Promise<Response | AuthReturn | NonNullable<Awaited<AuthFailReturn>>>;
|
|
30
30
|
}>;
|
|
31
|
-
}, {} | {
|
|
32
|
-
[x: string]: {
|
|
33
|
-
get: {
|
|
34
|
-
body: unknown;
|
|
35
|
-
params: {};
|
|
36
|
-
query: unknown;
|
|
37
|
-
headers: unknown;
|
|
38
|
-
response: {
|
|
39
|
-
200: {
|
|
40
|
-
resource: string;
|
|
41
|
-
scopes_supported: string[];
|
|
42
|
-
resource_name?: string | undefined;
|
|
43
|
-
resource_logo_uri?: string | undefined;
|
|
44
|
-
authorization_servers: string[];
|
|
45
|
-
bearer_methods_supported: string[];
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
|
-
};
|
|
49
|
-
};
|
|
50
|
-
} | ({
|
|
31
|
+
}, {} | ({
|
|
51
32
|
[x: string]: {
|
|
52
33
|
get: {
|
|
53
34
|
body: unknown;
|
|
@@ -200,52 +181,6 @@ export declare const agentAuthRoutes: (config?: AgentAuthConfig) => Elysia<"", {
|
|
|
200
181
|
};
|
|
201
182
|
};
|
|
202
183
|
};
|
|
203
|
-
}, {
|
|
204
|
-
derive: {};
|
|
205
|
-
resolve: {};
|
|
206
|
-
schema: {};
|
|
207
|
-
standaloneSchema: {};
|
|
208
|
-
response: {};
|
|
209
|
-
}, {
|
|
210
|
-
derive: {};
|
|
211
|
-
resolve: {};
|
|
212
|
-
schema: {};
|
|
213
|
-
standaloneSchema: {};
|
|
214
|
-
response: {};
|
|
215
|
-
}> | Elysia<"", {
|
|
216
|
-
decorator: {};
|
|
217
|
-
store: {};
|
|
218
|
-
derive: {};
|
|
219
|
-
resolve: {};
|
|
220
|
-
}, {
|
|
221
|
-
typebox: {};
|
|
222
|
-
error: {};
|
|
223
|
-
}, {
|
|
224
|
-
schema: {};
|
|
225
|
-
standaloneSchema: {};
|
|
226
|
-
macro: {};
|
|
227
|
-
macroFn: {};
|
|
228
|
-
parser: {};
|
|
229
|
-
response: {};
|
|
230
|
-
}, {
|
|
231
|
-
[x: string]: {
|
|
232
|
-
get: {
|
|
233
|
-
body: unknown;
|
|
234
|
-
params: {};
|
|
235
|
-
query: unknown;
|
|
236
|
-
headers: unknown;
|
|
237
|
-
response: {
|
|
238
|
-
200: {
|
|
239
|
-
resource: string;
|
|
240
|
-
scopes_supported: string[];
|
|
241
|
-
resource_name?: string | undefined;
|
|
242
|
-
resource_logo_uri?: string | undefined;
|
|
243
|
-
authorization_servers: string[];
|
|
244
|
-
bearer_methods_supported: string[];
|
|
245
|
-
};
|
|
246
|
-
};
|
|
247
|
-
};
|
|
248
|
-
};
|
|
249
184
|
} & {
|
|
250
185
|
[x: string]: {
|
|
251
186
|
get: {
|
package/dist/cli/migrate.js.map
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"import { and, desc, eq } from 'drizzle-orm';\nimport {\n\tbigint,\n\tboolean,\n\tdoublePrecision,\n\tpgTable,\n\tprimaryKey,\n\tvarchar\n} from 'drizzle-orm/pg-core';\nimport { type AnyPgDatabase, createNeonDatabase } from '../stores/postgres';\nimport type {\n\tKnownDevice,\n\tKnownDeviceStore,\n\tLoginAttempt,\n\tLoginHistoryStore\n} from './types';\n\nconst ID_LENGTH = 255;\n\nexport const knownDevicesTable = pgTable(\n\t'auth_known_devices',\n\t{\n\t\tdevice_id: varchar('device_id', { length: ID_LENGTH }).notNull(),\n\t\tfirst_seen_at_ms: bigint('first_seen_at_ms', {\n\t\t\tmode: 'number'\n\t\t}).notNull(),\n\t\tlabel: varchar('label', { length: ID_LENGTH }),\n\t\tlast_seen_at_ms: bigint('last_seen_at_ms', {\n\t\t\tmode: 'number'\n\t\t}).notNull(),\n\t\ttrusted: boolean('trusted').notNull().default(false),\n\t\tuser_id: varchar('user_id', { length: ID_LENGTH }).notNull()\n\t},\n\t(table) => [primaryKey({ columns: [table.user_id, table.device_id] })]\n);\n\nexport const loginHistoryTable = pgTable('auth_login_history', {\n\tattempt_id: varchar('attempt_id', { length: ID_LENGTH }).primaryKey(),\n\tcountry: varchar('country', { length: ID_LENGTH }),\n\tdevice_id: varchar('device_id', { length: ID_LENGTH }).notNull(),\n\tip_address: varchar('ip_address', { length: ID_LENGTH }),\n\tlatitude: doublePrecision('latitude'),\n\tlongitude: doublePrecision('longitude'),\n\toutcome: varchar('outcome', { length: ID_LENGTH }).notNull(),\n\ttimestamp_ms: bigint('timestamp_ms', { mode: 'number' }).notNull(),\n\tuser_id: varchar('user_id', { length: ID_LENGTH }).notNull()\n});\n\ntype KnownDeviceRow = typeof knownDevicesTable.$inferSelect;\ntype LoginAttemptRow = typeof loginHistoryTable.$inferSelect;\n\nconst toRiskAction = (value: string) => {\n\tif (value === 'deny') return 'deny';\n\tif (value === 'step_up') return 'step_up';\n\n\treturn 'allow';\n};\n\nconst toDevice = (row: KnownDeviceRow): KnownDevice => ({\n\tdeviceId: row.device_id,\n\tfirstSeenAt: row.first_seen_at_ms,\n\tlabel: row.label ?? undefined,\n\tlastSeenAt: row.last_seen_at_ms,\n\ttrusted: row.trusted,\n\tuserId: row.user_id\n});\n\nconst toDeviceValues = (\n\tdevice: KnownDevice\n): typeof knownDevicesTable.$inferInsert => ({\n\tdevice_id: device.deviceId,\n\tfirst_seen_at_ms: device.firstSeenAt,\n\tlabel: device.label ?? null,\n\tlast_seen_at_ms: device.lastSeenAt,\n\ttrusted: device.trusted,\n\tuser_id: device.userId\n});\n\nconst toAttempt = (row: LoginAttemptRow): LoginAttempt => ({\n\tattemptId: row.attempt_id,\n\tcountry: row.country ?? undefined,\n\tdeviceId: row.device_id,\n\tipAddress: row.ip_address ?? undefined,\n\tlatitude: row.latitude ?? undefined,\n\tlongitude: row.longitude ?? undefined,\n\toutcome: toRiskAction(row.outcome),\n\ttimestamp: row.timestamp_ms,\n\tuserId: row.user_id\n});\n\nconst toAttemptValues = (\n\tattempt: LoginAttempt\n): typeof loginHistoryTable.$inferInsert => ({\n\tattempt_id: attempt.attemptId,\n\tcountry: attempt.country ?? null,\n\tdevice_id: attempt.deviceId,\n\tip_address: attempt.ipAddress ?? null,\n\tlatitude: attempt.latitude ?? null,\n\tlongitude: attempt.longitude ?? null,\n\toutcome: attempt.outcome,\n\ttimestamp_ms: attempt.timestamp,\n\tuser_id: attempt.userId\n});\n\nexport const createNeonKnownDeviceStore = (databaseUrl: string) =>\n\tcreatePostgresKnownDeviceStore(createNeonDatabase(databaseUrl));\nexport const createNeonLoginHistoryStore = (databaseUrl: string) =>\n\tcreatePostgresLoginHistoryStore(createNeonDatabase(databaseUrl));\nexport const createPostgresKnownDeviceStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): KnownDeviceStore => ({\n\tfindDevice: async (userId, deviceId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(knownDevicesTable)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(knownDevicesTable.user_id, userId),\n\t\t\t\t\teq(knownDevicesTable.device_id, deviceId)\n\t\t\t\t)\n\t\t\t)\n\t\t\t.limit(1);\n\n\t\treturn row ? toDevice(row) : undefined;\n\t},\n\tlistDevices: async (userId) => {\n\t\tconst rows = await db\n\t\t\t.select()\n\t\t\t.from(knownDevicesTable)\n\t\t\t.where(eq(knownDevicesTable.user_id, userId))\n\t\t\t.orderBy(desc(knownDevicesTable.last_seen_at_ms));\n\n\t\treturn rows.map(toDevice);\n\t},\n\tsaveDevice: async (device) => {\n\t\tconst values = toDeviceValues(device);\n\t\tawait db\n\t\t\t.insert(knownDevicesTable)\n\t\t\t.values(values)\n\t\t\t.onConflictDoUpdate({\n\t\t\t\tset: values,\n\t\t\t\ttarget: [knownDevicesTable.user_id, knownDevicesTable.device_id]\n\t\t\t});\n\t}\n});\nexport const createPostgresLoginHistoryStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): LoginHistoryStore => ({\n\tlistRecent: async (userId, limit) => {\n\t\tconst rows = await db\n\t\t\t.select()\n\t\t\t.from(loginHistoryTable)\n\t\t\t.where(eq(loginHistoryTable.user_id, userId))\n\t\t\t.orderBy(desc(loginHistoryTable.timestamp_ms))\n\t\t\t.limit(limit);\n\n\t\treturn rows.map(toAttempt);\n\t},\n\trecordAttempt: async (attempt) => {\n\t\tawait db.insert(loginHistoryTable).values(toAttemptValues(attempt));\n\t}\n});\n",
|
|
12
12
|
"import { neon } from '@neondatabase/serverless';\nimport { drizzle } from 'drizzle-orm/neon-http';\nimport type { PgAsyncDatabase } from 'drizzle-orm/pg-core';\n\n// Shared scaffolding for the enterprise stores. Every new store ships an\n// in-memory implementation (dev/test) plus a Postgres implementation that accepts\n// a Drizzle `PgDatabase` — so it runs on Neon (neon-http) AND node-postgres without\n// the package bundling a second driver. `createNeonDatabase` is the convenience\n// wrapper consumers reach for when they just want a Neon connection string.\n\n// PgAsyncDatabase (drizzle 1.0; was PgDatabase pre-1.0) is effectively invariant\n// over its type parameters (query-result HKT, relational config), and TS can't\n// reliably re-infer those base params from a concrete driver *subclass*\n// (PostgresJsDatabase, NeonHttpDatabase, …). So the only way to accept any\n// driver with NO caller-side cast is a generic store constructor whose db\n// parameter is bound by `AnyPgDatabase`: `<DB extends AnyPgDatabase>(db: DB)`.\n// The `any`s live ONLY in this bound — `DB` is inferred as the caller's exact\n// database type, so store bodies stay fully typed (this is not `db: any`).\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- constraint bound only; DB infers the caller's exact db type\nexport type AnyPgDatabase = PgAsyncDatabase<any, any>;\n\nexport const createNeonDatabase = (databaseUrl: string) =>\n\tdrizzle({ client: neon(databaseUrl) });\n",
|
|
13
13
|
"import { desc, eq, lt } from 'drizzle-orm';\nimport { bigint, pgTable, text, varchar } from 'drizzle-orm/pg-core';\nimport { type AnyPgDatabase, createNeonDatabase } from '../stores/postgres';\nimport type {\n\tAccessToken,\n\tAccessTokenStore,\n\tApiClient,\n\tApiClientStore,\n\tApiKey,\n\tApiKeyStore\n} from './types';\n\nconst ID_LENGTH = 255;\n\nexport const accessTokensTable = pgTable('auth_access_tokens', {\n\tclient_id: varchar('client_id', { length: ID_LENGTH }).notNull(),\n\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }).notNull(),\n\thashed_token: varchar('hashed_token', { length: ID_LENGTH }).notNull(),\n\towner_id: varchar('owner_id', { length: ID_LENGTH }),\n\tscopes: text('scopes').array().notNull(),\n\ttoken_id: varchar('token_id', { length: ID_LENGTH }).primaryKey()\n});\nexport const apiClientsTable = pgTable('auth_api_clients', {\n\tclient_id: varchar('client_id', { length: ID_LENGTH }).primaryKey(),\n\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\thashed_secret: varchar('hashed_secret', { length: ID_LENGTH }).notNull(),\n\tname: varchar('name', { length: ID_LENGTH }).notNull(),\n\towner_id: varchar('owner_id', { length: ID_LENGTH }),\n\tscopes: text('scopes').array().notNull()\n});\nexport const apiKeysTable = pgTable('auth_api_keys', {\n\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }),\n\thashed_key: varchar('hashed_key', { length: ID_LENGTH }).notNull(),\n\tkey_id: varchar('key_id', { length: ID_LENGTH }).primaryKey(),\n\tlast_used_at_ms: bigint('last_used_at_ms', { mode: 'number' }),\n\tname: varchar('name', { length: ID_LENGTH }).notNull(),\n\towner_id: varchar('owner_id', { length: ID_LENGTH }),\n\tprefix: varchar('prefix', { length: ID_LENGTH }).notNull(),\n\tscopes: text('scopes').array().notNull()\n});\n\ntype ApiKeyRow = typeof apiKeysTable.$inferSelect;\ntype ApiClientRow = typeof apiClientsTable.$inferSelect;\ntype AccessTokenRow = typeof accessTokensTable.$inferSelect;\n\nconst toKey = (row: ApiKeyRow): ApiKey => ({\n\tcreatedAt: row.created_at_ms,\n\texpiresAt: row.expires_at_ms ?? undefined,\n\thashedKey: row.hashed_key,\n\tkeyId: row.key_id,\n\tlastUsedAt: row.last_used_at_ms ?? undefined,\n\tname: row.name,\n\townerId: row.owner_id ?? undefined,\n\tprefix: row.prefix,\n\tscopes: row.scopes\n});\n\nconst toKeyValues = (key: ApiKey): typeof apiKeysTable.$inferInsert => ({\n\tcreated_at_ms: key.createdAt,\n\texpires_at_ms: key.expiresAt ?? null,\n\thashed_key: key.hashedKey,\n\tkey_id: key.keyId,\n\tlast_used_at_ms: key.lastUsedAt ?? null,\n\tname: key.name,\n\towner_id: key.ownerId ?? null,\n\tprefix: key.prefix,\n\tscopes: key.scopes\n});\n\nconst toClient = (row: ApiClientRow): ApiClient => ({\n\tclientId: row.client_id,\n\tcreatedAt: row.created_at_ms,\n\thashedSecret: row.hashed_secret,\n\tname: row.name,\n\townerId: row.owner_id ?? undefined,\n\tscopes: row.scopes\n});\n\nconst toClientValues = (\n\tclient: ApiClient\n): typeof apiClientsTable.$inferInsert => ({\n\tclient_id: client.clientId,\n\tcreated_at_ms: client.createdAt,\n\thashed_secret: client.hashedSecret,\n\tname: client.name,\n\towner_id: client.ownerId ?? null,\n\tscopes: client.scopes\n});\n\nconst toAccessToken = (row: AccessTokenRow): AccessToken => ({\n\tclientId: row.client_id,\n\tcreatedAt: row.created_at_ms,\n\texpiresAt: row.expires_at_ms,\n\thashedToken: row.hashed_token,\n\townerId: row.owner_id ?? undefined,\n\tscopes: row.scopes,\n\ttokenId: row.token_id\n});\n\nconst toAccessTokenValues = (\n\ttoken: AccessToken\n): typeof accessTokensTable.$inferInsert => ({\n\tclient_id: token.clientId,\n\tcreated_at_ms: token.createdAt,\n\texpires_at_ms: token.expiresAt,\n\thashed_token: token.hashedToken,\n\towner_id: token.ownerId ?? null,\n\tscopes: token.scopes,\n\ttoken_id: token.tokenId\n});\n\nexport const createNeonAccessTokenStore = (databaseUrl: string) =>\n\tcreatePostgresAccessTokenStore(createNeonDatabase(databaseUrl));\nexport const createNeonApiClientStore = (databaseUrl: string) =>\n\tcreatePostgresApiClientStore(createNeonDatabase(databaseUrl));\nexport const createNeonApiKeyStore = (databaseUrl: string) =>\n\tcreatePostgresApiKeyStore(createNeonDatabase(databaseUrl));\nexport const createPostgresAccessTokenStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): AccessTokenStore => ({\n\tdeleteExpired: async (now) => {\n\t\tawait db\n\t\t\t.delete(accessTokensTable)\n\t\t\t.where(lt(accessTokensTable.expires_at_ms, now));\n\t},\n\tdeleteToken: async (tokenId) => {\n\t\tawait db\n\t\t\t.delete(accessTokensTable)\n\t\t\t.where(eq(accessTokensTable.token_id, tokenId));\n\t},\n\tfindByHashedToken: async (hashedToken) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(accessTokensTable)\n\t\t\t.where(eq(accessTokensTable.hashed_token, hashedToken))\n\t\t\t.limit(1);\n\n\t\treturn row ? toAccessToken(row) : undefined;\n\t},\n\tsaveToken: async (token) => {\n\t\tconst values = toAccessTokenValues(token);\n\t\tawait db.insert(accessTokensTable).values(values).onConflictDoUpdate({\n\t\t\tset: values,\n\t\t\ttarget: accessTokensTable.token_id\n\t\t});\n\t}\n});\nexport const createPostgresApiClientStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): ApiClientStore => ({\n\tdeleteClient: async (clientId) => {\n\t\tawait db\n\t\t\t.delete(apiClientsTable)\n\t\t\t.where(eq(apiClientsTable.client_id, clientId));\n\t},\n\tfindClient: async (clientId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(apiClientsTable)\n\t\t\t.where(eq(apiClientsTable.client_id, clientId))\n\t\t\t.limit(1);\n\n\t\treturn row ? toClient(row) : undefined;\n\t},\n\tlistClients: async (ownerId) => {\n\t\tconst rows =\n\t\t\townerId === undefined\n\t\t\t\t? await db\n\t\t\t\t\t\t.select()\n\t\t\t\t\t\t.from(apiClientsTable)\n\t\t\t\t\t\t.orderBy(desc(apiClientsTable.created_at_ms))\n\t\t\t\t: await db\n\t\t\t\t\t\t.select()\n\t\t\t\t\t\t.from(apiClientsTable)\n\t\t\t\t\t\t.where(eq(apiClientsTable.owner_id, ownerId))\n\t\t\t\t\t\t.orderBy(desc(apiClientsTable.created_at_ms));\n\n\t\treturn rows.map(toClient);\n\t},\n\tsaveClient: async (client) => {\n\t\tconst values = toClientValues(client);\n\t\tawait db.insert(apiClientsTable).values(values).onConflictDoUpdate({\n\t\t\tset: values,\n\t\t\ttarget: apiClientsTable.client_id\n\t\t});\n\t}\n});\nexport const createPostgresApiKeyStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): ApiKeyStore => ({\n\tdeleteKey: async (keyId) => {\n\t\tawait db.delete(apiKeysTable).where(eq(apiKeysTable.key_id, keyId));\n\t},\n\tfindByHashedKey: async (hashedKey) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(apiKeysTable)\n\t\t\t.where(eq(apiKeysTable.hashed_key, hashedKey))\n\t\t\t.limit(1);\n\n\t\treturn row ? toKey(row) : undefined;\n\t},\n\tlistKeys: async (ownerId) => {\n\t\tconst rows =\n\t\t\townerId === undefined\n\t\t\t\t? await db\n\t\t\t\t\t\t.select()\n\t\t\t\t\t\t.from(apiKeysTable)\n\t\t\t\t\t\t.orderBy(desc(apiKeysTable.created_at_ms))\n\t\t\t\t: await db\n\t\t\t\t\t\t.select()\n\t\t\t\t\t\t.from(apiKeysTable)\n\t\t\t\t\t\t.where(eq(apiKeysTable.owner_id, ownerId))\n\t\t\t\t\t\t.orderBy(desc(apiKeysTable.created_at_ms));\n\n\t\treturn rows.map(toKey);\n\t},\n\tsaveKey: async (key) => {\n\t\tconst values = toKeyValues(key);\n\t\tawait db.insert(apiKeysTable).values(values).onConflictDoUpdate({\n\t\t\tset: values,\n\t\t\ttarget: apiKeysTable.key_id\n\t\t});\n\t},\n\ttouchKey: async (keyId, lastUsedAt) => {\n\t\tawait db\n\t\t\t.update(apiKeysTable)\n\t\t\t.set({ last_used_at_ms: lastUsedAt })\n\t\t\t.where(eq(apiKeysTable.key_id, keyId));\n\t}\n});\n",
|
|
14
|
-
"import { and, desc, eq, gt, isNull, or } from 'drizzle-orm';\nimport {\n\tbigint,\n\tcustomType,\n\tinteger,\n\tpgTable,\n\tuniqueIndex,\n\tvarchar\n} from 'drizzle-orm/pg-core';\nimport { type AnyPgDatabase, createNeonDatabase } from '../stores/postgres';\nimport type {\n\tAgentDelegation,\n\tAgentDelegationStatus,\n\tAgentDelegationStore,\n\tAgentIdentityRegistration,\n\tAgentIdentityRegistrationKind,\n\tAgentIdentityRegistrationStatus,\n\tAgentIdentityRegistrationStore,\n\tAgentRegistration,\n\tAgentRegistrationStatus,\n\tAgentRegistrationStore\n} from './types';\n\nconst ID_LENGTH = 255;\nconst NAME_LENGTH = 255;\nconst STATUS_LENGTH = 16;\n\n// Drizzle 1.0's built-in JSONB codec is not yet serialized by Bun SQL. This\n// package-owned custom type preserves one portable boundary for Bun SQL,\n// postgres.js, and Neon while `$type<T>()` retains each column's exact shape.\nconst portableJsonb = customType<{ data: unknown; driverData: unknown }>({\n\tdataType: () => 'jsonb',\n\tfromDriver: (value) =>\n\t\ttypeof value === 'string' ? JSON.parse(value) : value,\n\ttoDriver: (value) => JSON.stringify(value)\n});\n\nexport const agentDelegationsTable = pgTable('auth_agent_delegations', {\n\tagent_id: varchar('agent_id', { length: ID_LENGTH }).notNull(),\n\tauthorization_details: portableJsonb('authorization_details').$type<\n\t\tRecord<string, unknown>[]\n\t>(),\n\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\tdelegation_id: varchar('delegation_id', {\n\t\tlength: ID_LENGTH\n\t}).primaryKey(),\n\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }),\n\torganization_id: varchar('organization_id', { length: ID_LENGTH }),\n\tscopes: portableJsonb('scopes').$type<string[]>().notNull().default([]),\n\tstatus: varchar('status', { length: STATUS_LENGTH })\n\t\t.$type<AgentDelegationStatus>()\n\t\t.notNull(),\n\tupdated_at_ms: bigint('updated_at_ms', { mode: 'number' }).notNull(),\n\tuser_id: varchar('user_id', { length: ID_LENGTH }).notNull()\n});\nexport const agentIdentityRegistrationsTable = pgTable(\n\t'auth_agent_identity_registrations',\n\t{\n\t\tagent_id: varchar('agent_id', { length: ID_LENGTH }).notNull().unique(),\n\t\tclaim_attempt:\n\t\t\tportableJsonb('claim_attempt').$type<\n\t\t\t\tAgentIdentityRegistration['claimAttempt']\n\t\t\t>(),\n\t\tclaim_attempt_token_hash: varchar('claim_attempt_token_hash', {\n\t\t\tlength: ID_LENGTH\n\t\t}).unique(),\n\t\tclaim_expires_at_ms: bigint('claim_expires_at_ms', {\n\t\t\tmode: 'number'\n\t\t}).notNull(),\n\t\tclaim_token_hash: varchar('claim_token_hash', {\n\t\t\tlength: ID_LENGTH\n\t\t})\n\t\t\t.notNull()\n\t\t\t.unique(),\n\t\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\t\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }).notNull(),\n\t\tkind: varchar('kind', { length: 32 })\n\t\t\t.$type<AgentIdentityRegistrationKind>()\n\t\t\t.notNull(),\n\t\tlast_polled_at_ms: bigint('last_polled_at_ms', { mode: 'number' }),\n\t\tlogin_hint: varchar('login_hint', { length: ID_LENGTH }),\n\t\tregistration_id: varchar('registration_id', {\n\t\t\tlength: ID_LENGTH\n\t\t}).primaryKey(),\n\t\tstatus: varchar('status', { length: STATUS_LENGTH })\n\t\t\t.$type<AgentIdentityRegistrationStatus>()\n\t\t\t.notNull(),\n\t\tupdated_at_ms: bigint('updated_at_ms', { mode: 'number' }).notNull(),\n\t\tupstream_client_id: varchar('upstream_client_id', {\n\t\t\tlength: ID_LENGTH\n\t\t}),\n\t\tupstream_issuer: varchar('upstream_issuer', { length: ID_LENGTH }),\n\t\tupstream_subject: varchar('upstream_subject', { length: ID_LENGTH }),\n\t\tuser_id: varchar('user_id', { length: ID_LENGTH }),\n\t\tversion: integer('version').notNull()\n\t},\n\t(table) => [\n\t\tuniqueIndex('auth_agent_identity_upstream_unique').on(\n\t\t\ttable.upstream_issuer,\n\t\t\ttable.upstream_subject,\n\t\t\ttable.upstream_client_id\n\t\t)\n\t]\n);\nexport const agentRegistrationsTable = pgTable('auth_agent_registrations', {\n\tagent_id: varchar('agent_id', { length: ID_LENGTH }).primaryKey(),\n\tallowed_scopes: portableJsonb('allowed_scopes')\n\t\t.$type<string[]>()\n\t\t.notNull()\n\t\t.default([]),\n\tclient_id: varchar('client_id', { length: ID_LENGTH }).unique(),\n\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\tmetadata: portableJsonb('metadata').$type<Record<string, unknown>>(),\n\tname: varchar('name', { length: NAME_LENGTH }).notNull(),\n\tstatus: varchar('status', { length: STATUS_LENGTH })\n\t\t.$type<AgentRegistrationStatus>()\n\t\t.notNull(),\n\tupdated_at_ms: bigint('updated_at_ms', { mode: 'number' }).notNull()\n});\n\ntype RegistrationRow = typeof agentRegistrationsTable.$inferSelect;\ntype RegistrationInsert = typeof agentRegistrationsTable.$inferInsert;\ntype DelegationRow = typeof agentDelegationsTable.$inferSelect;\ntype DelegationInsert = typeof agentDelegationsTable.$inferInsert;\ntype IdentityRegistrationRow =\n\ttypeof agentIdentityRegistrationsTable.$inferSelect;\ntype IdentityRegistrationInsert =\n\ttypeof agentIdentityRegistrationsTable.$inferInsert;\n\nconst toRegistration = (row: RegistrationRow): AgentRegistration => ({\n\tagentId: row.agent_id,\n\tallowedScopes: row.allowed_scopes,\n\tclientId: row.client_id ?? undefined,\n\tcreatedAt: row.created_at_ms,\n\tmetadata: row.metadata ?? undefined,\n\tname: row.name,\n\tstatus: row.status,\n\tupdatedAt: row.updated_at_ms\n});\n\nconst toDelegation = (row: DelegationRow): AgentDelegation => ({\n\tagentId: row.agent_id,\n\tauthorizationDetails: row.authorization_details ?? undefined,\n\tcreatedAt: row.created_at_ms,\n\tdelegationId: row.delegation_id,\n\texpiresAt: row.expires_at_ms ?? undefined,\n\torganizationId: row.organization_id ?? undefined,\n\tscopes: row.scopes,\n\tstatus: row.status,\n\tupdatedAt: row.updated_at_ms,\n\tuserId: row.user_id\n});\n\nconst toIdentityRegistration = (\n\trow: IdentityRegistrationRow\n): AgentIdentityRegistration => ({\n\tagentId: row.agent_id,\n\tclaimAttempt: row.claim_attempt ?? undefined,\n\tclaimExpiresAt: row.claim_expires_at_ms,\n\tclaimTokenHash: row.claim_token_hash,\n\tcreatedAt: row.created_at_ms,\n\texpiresAt: row.expires_at_ms,\n\tkind: row.kind,\n\tlastPolledAt: row.last_polled_at_ms ?? undefined,\n\tloginHint: row.login_hint ?? undefined,\n\tregistrationId: row.registration_id,\n\tstatus: row.status,\n\tupdatedAt: row.updated_at_ms,\n\tupstream:\n\t\trow.upstream_client_id === null ||\n\t\trow.upstream_issuer === null ||\n\t\trow.upstream_subject === null\n\t\t\t? undefined\n\t\t\t: {\n\t\t\t\t\tclientId: row.upstream_client_id,\n\t\t\t\t\tissuer: row.upstream_issuer,\n\t\t\t\t\tsubject: row.upstream_subject\n\t\t\t\t},\n\tuserId: row.user_id ?? undefined,\n\tversion: row.version\n});\n\nconst identityRegistrationValues = (\n\tregistration: AgentIdentityRegistration\n): IdentityRegistrationInsert => ({\n\tagent_id: registration.agentId,\n\tclaim_attempt: registration.claimAttempt ?? null,\n\tclaim_attempt_token_hash: registration.claimAttempt?.tokenHash ?? null,\n\tclaim_expires_at_ms: registration.claimExpiresAt,\n\tclaim_token_hash: registration.claimTokenHash,\n\tcreated_at_ms: registration.createdAt,\n\texpires_at_ms: registration.expiresAt,\n\tkind: registration.kind,\n\tlast_polled_at_ms: registration.lastPolledAt ?? null,\n\tlogin_hint: registration.loginHint ?? null,\n\tregistration_id: registration.registrationId,\n\tstatus: registration.status,\n\tupdated_at_ms: registration.updatedAt,\n\tupstream_client_id: registration.upstream?.clientId ?? null,\n\tupstream_issuer: registration.upstream?.issuer ?? null,\n\tupstream_subject: registration.upstream?.subject ?? null,\n\tuser_id: registration.userId ?? null,\n\tversion: registration.version\n});\n\nexport const createNeonAgentDelegationStore = (databaseUrl: string) =>\n\tcreatePostgresAgentDelegationStore(createNeonDatabase(databaseUrl));\nexport const createNeonAgentIdentityRegistrationStore = (databaseUrl: string) =>\n\tcreatePostgresAgentIdentityRegistrationStore(\n\t\tcreateNeonDatabase(databaseUrl)\n\t);\nexport const createNeonAgentRegistrationStore = (databaseUrl: string) =>\n\tcreatePostgresAgentRegistrationStore(createNeonDatabase(databaseUrl));\nexport const createPostgresAgentDelegationStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): AgentDelegationStore => ({\n\tfindActiveDelegation: async ({\n\t\tagentId,\n\t\tnow = Date.now(),\n\t\torganizationId,\n\t\tuserId\n\t}) => {\n\t\tconst organizationCondition =\n\t\t\torganizationId === undefined\n\t\t\t\t? isNull(agentDelegationsTable.organization_id)\n\t\t\t\t: eq(agentDelegationsTable.organization_id, organizationId);\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(agentDelegationsTable)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(agentDelegationsTable.agent_id, agentId),\n\t\t\t\t\teq(agentDelegationsTable.user_id, userId),\n\t\t\t\t\torganizationCondition,\n\t\t\t\t\teq(agentDelegationsTable.status, 'active'),\n\t\t\t\t\tor(\n\t\t\t\t\t\tisNull(agentDelegationsTable.expires_at_ms),\n\t\t\t\t\t\tgt(agentDelegationsTable.expires_at_ms, now)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t\t.orderBy(desc(agentDelegationsTable.updated_at_ms))\n\t\t\t.limit(1);\n\n\t\treturn row === undefined ? undefined : toDelegation(row);\n\t},\n\tfindByDelegationId: async (delegationId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(agentDelegationsTable)\n\t\t\t.where(eq(agentDelegationsTable.delegation_id, delegationId))\n\t\t\t.limit(1);\n\n\t\treturn row === undefined ? undefined : toDelegation(row);\n\t},\n\tlistDelegations: async (agentId) => {\n\t\tconst base = db.select().from(agentDelegationsTable);\n\t\tconst rows = await (agentId === undefined\n\t\t\t? base.orderBy(desc(agentDelegationsTable.created_at_ms))\n\t\t\t: base\n\t\t\t\t\t.where(eq(agentDelegationsTable.agent_id, agentId))\n\t\t\t\t\t.orderBy(desc(agentDelegationsTable.created_at_ms)));\n\n\t\treturn rows.map(toDelegation);\n\t},\n\tsaveDelegation: async (delegation) => {\n\t\tconst values: DelegationInsert = {\n\t\t\tagent_id: delegation.agentId,\n\t\t\tauthorization_details: delegation.authorizationDetails ?? null,\n\t\t\tcreated_at_ms: delegation.createdAt,\n\t\t\tdelegation_id: delegation.delegationId,\n\t\t\texpires_at_ms: delegation.expiresAt ?? null,\n\t\t\torganization_id: delegation.organizationId ?? null,\n\t\t\tscopes: delegation.scopes,\n\t\t\tstatus: delegation.status,\n\t\t\tupdated_at_ms: delegation.updatedAt,\n\t\t\tuser_id: delegation.userId\n\t\t};\n\t\tawait db\n\t\t\t.insert(agentDelegationsTable)\n\t\t\t.values(values)\n\t\t\t.onConflictDoUpdate({\n\t\t\t\tset: values,\n\t\t\t\ttarget: agentDelegationsTable.delegation_id\n\t\t\t});\n\t}\n});\nexport const createPostgresAgentIdentityRegistrationStore = <\n\tDB extends AnyPgDatabase\n>(\n\tdb: DB\n): AgentIdentityRegistrationStore => ({\n\tcreate: async (registration) => {\n\t\tconst rows = await db\n\t\t\t.insert(agentIdentityRegistrationsTable)\n\t\t\t.values(identityRegistrationValues(registration))\n\t\t\t.onConflictDoNothing()\n\t\t\t.returning({ id: agentIdentityRegistrationsTable.registration_id });\n\n\t\treturn rows.length === 1;\n\t},\n\tfindByAgentId: async (agentId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(agentIdentityRegistrationsTable)\n\t\t\t.where(eq(agentIdentityRegistrationsTable.agent_id, agentId))\n\t\t\t.limit(1);\n\n\t\treturn row === undefined ? undefined : toIdentityRegistration(row);\n\t},\n\tfindByAttemptTokenHash: async (attemptTokenHash) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(agentIdentityRegistrationsTable)\n\t\t\t.where(\n\t\t\t\teq(\n\t\t\t\t\tagentIdentityRegistrationsTable.claim_attempt_token_hash,\n\t\t\t\t\tattemptTokenHash\n\t\t\t\t)\n\t\t\t)\n\t\t\t.limit(1);\n\n\t\treturn row === undefined ? undefined : toIdentityRegistration(row);\n\t},\n\tfindByClaimTokenHash: async (claimTokenHash) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(agentIdentityRegistrationsTable)\n\t\t\t.where(\n\t\t\t\teq(\n\t\t\t\t\tagentIdentityRegistrationsTable.claim_token_hash,\n\t\t\t\t\tclaimTokenHash\n\t\t\t\t)\n\t\t\t)\n\t\t\t.limit(1);\n\n\t\treturn row === undefined ? undefined : toIdentityRegistration(row);\n\t},\n\tfindByRegistrationId: async (registrationId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(agentIdentityRegistrationsTable)\n\t\t\t.where(\n\t\t\t\teq(\n\t\t\t\t\tagentIdentityRegistrationsTable.registration_id,\n\t\t\t\t\tregistrationId\n\t\t\t\t)\n\t\t\t)\n\t\t\t.limit(1);\n\n\t\treturn row === undefined ? undefined : toIdentityRegistration(row);\n\t},\n\tfindByUpstreamIdentity: async ({ clientId, issuer, subject }) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(agentIdentityRegistrationsTable)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(\n\t\t\t\t\t\tagentIdentityRegistrationsTable.upstream_client_id,\n\t\t\t\t\t\tclientId\n\t\t\t\t\t),\n\t\t\t\t\teq(agentIdentityRegistrationsTable.upstream_issuer, issuer),\n\t\t\t\t\teq(\n\t\t\t\t\t\tagentIdentityRegistrationsTable.upstream_subject,\n\t\t\t\t\t\tsubject\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t\t.limit(1);\n\n\t\treturn row === undefined ? undefined : toIdentityRegistration(row);\n\t},\n\treplace: async (registration, expectedVersion) => {\n\t\tconst next: AgentIdentityRegistration = {\n\t\t\t...registration,\n\t\t\tversion: expectedVersion + 1\n\t\t};\n\t\tconst values = identityRegistrationValues(next);\n\t\tconst rows = await db\n\t\t\t.update(agentIdentityRegistrationsTable)\n\t\t\t.set(values)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(\n\t\t\t\t\t\tagentIdentityRegistrationsTable.registration_id,\n\t\t\t\t\t\tregistration.registrationId\n\t\t\t\t\t),\n\t\t\t\t\teq(agentIdentityRegistrationsTable.version, expectedVersion)\n\t\t\t\t)\n\t\t\t)\n\t\t\t.returning({ id: agentIdentityRegistrationsTable.registration_id });\n\n\t\treturn rows.length === 1;\n\t}\n});\nexport const createPostgresAgentRegistrationStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): AgentRegistrationStore => ({\n\tfindByAgentId: async (agentId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(agentRegistrationsTable)\n\t\t\t.where(eq(agentRegistrationsTable.agent_id, agentId))\n\t\t\t.limit(1);\n\n\t\treturn row === undefined ? undefined : toRegistration(row);\n\t},\n\tfindByClientId: async (clientId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(agentRegistrationsTable)\n\t\t\t.where(eq(agentRegistrationsTable.client_id, clientId))\n\t\t\t.limit(1);\n\n\t\treturn row === undefined ? undefined : toRegistration(row);\n\t},\n\tlistRegistrations: async () => {\n\t\tconst rows = await db\n\t\t\t.select()\n\t\t\t.from(agentRegistrationsTable)\n\t\t\t.orderBy(desc(agentRegistrationsTable.created_at_ms));\n\n\t\treturn rows.map(toRegistration);\n\t},\n\tsaveRegistration: async (registration) => {\n\t\tconst values: RegistrationInsert = {\n\t\t\tagent_id: registration.agentId,\n\t\t\tallowed_scopes: registration.allowedScopes,\n\t\t\tclient_id: registration.clientId ?? null,\n\t\t\tcreated_at_ms: registration.createdAt,\n\t\t\tmetadata: registration.metadata ?? null,\n\t\t\tname: registration.name,\n\t\t\tstatus: registration.status,\n\t\t\tupdated_at_ms: registration.updatedAt\n\t\t};\n\t\tawait db\n\t\t\t.insert(agentRegistrationsTable)\n\t\t\t.values(values)\n\t\t\t.onConflictDoUpdate({\n\t\t\t\tset: values,\n\t\t\t\ttarget: agentRegistrationsTable.agent_id\n\t\t\t});\n\t}\n});\n",
|
|
14
|
+
"import { and, desc, eq, gt, isNull, or } from 'drizzle-orm';\nimport {\n\tbigint,\n\tcustomType,\n\tinteger,\n\tpgTable,\n\tuniqueIndex,\n\tvarchar\n} from 'drizzle-orm/pg-core';\nimport { type AnyPgDatabase, createNeonDatabase } from '../stores/postgres';\nimport type {\n\tAgentDelegation,\n\tAgentDelegationStatus,\n\tAgentDelegationStore,\n\tAgentIdentityRegistration,\n\tAgentIdentityRegistrationKind,\n\tAgentIdentityRegistrationStatus,\n\tAgentIdentityRegistrationStore,\n\tAgentRegistration,\n\tAgentRegistrationStatus,\n\tAgentRegistrationStore\n} from './types';\n\nconst ID_LENGTH = 255;\nconst NAME_LENGTH = 255;\nconst STATUS_LENGTH = 16;\n\n// Drizzle 1.0's built-in JSONB codec is not yet serialized by Bun SQL. This\n// package-owned custom type preserves one portable boundary for Bun SQL,\n// postgres.js, and Neon while `$type<T>()` retains each column's exact shape.\nconst portableJsonb = customType<{ data: unknown; driverData: unknown }>({\n\tdataType: () => 'jsonb',\n\tfromDriver: (value) =>\n\t\ttypeof value === 'string' ? JSON.parse(value) : value,\n\ttoDriver: (value) => JSON.stringify(value)\n});\n\nexport const agentDelegationsTable = pgTable('auth_agent_delegations', {\n\tagent_id: varchar('agent_id', { length: ID_LENGTH }).notNull(),\n\tauthorization_details: portableJsonb('authorization_details').$type<\n\t\tRecord<string, unknown>[]\n\t>(),\n\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\tdelegation_id: varchar('delegation_id', {\n\t\tlength: ID_LENGTH\n\t}).primaryKey(),\n\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }),\n\torganization_id: varchar('organization_id', { length: ID_LENGTH }),\n\tscopes: portableJsonb('scopes').$type<string[]>().notNull().default([]),\n\tstatus: varchar('status', { length: STATUS_LENGTH })\n\t\t.$type<AgentDelegationStatus>()\n\t\t.notNull(),\n\tupdated_at_ms: bigint('updated_at_ms', { mode: 'number' }).notNull(),\n\tuser_id: varchar('user_id', { length: ID_LENGTH }).notNull()\n});\nexport const agentIdentityRegistrationsTable = pgTable(\n\t'auth_agent_identity_registrations',\n\t{\n\t\tagent_id: varchar('agent_id', { length: ID_LENGTH }).notNull().unique(),\n\t\tclaim_attempt:\n\t\t\tportableJsonb('claim_attempt').$type<\n\t\t\t\tAgentIdentityRegistration['claimAttempt']\n\t\t\t>(),\n\t\tclaim_attempt_token_hash: varchar('claim_attempt_token_hash', {\n\t\t\tlength: ID_LENGTH\n\t\t}).unique(),\n\t\tclaim_expires_at_ms: bigint('claim_expires_at_ms', {\n\t\t\tmode: 'number'\n\t\t}).notNull(),\n\t\tclaim_token_hash: varchar('claim_token_hash', {\n\t\t\tlength: ID_LENGTH\n\t\t})\n\t\t\t.notNull()\n\t\t\t.unique(),\n\t\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\t\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }).notNull(),\n\t\tkind: varchar('kind', { length: 32 })\n\t\t\t.$type<AgentIdentityRegistrationKind>()\n\t\t\t.notNull(),\n\t\tlast_polled_at_ms: bigint('last_polled_at_ms', { mode: 'number' }),\n\t\tlogin_hint: varchar('login_hint', { length: ID_LENGTH }),\n\t\tregistration_id: varchar('registration_id', {\n\t\t\tlength: ID_LENGTH\n\t\t}).primaryKey(),\n\t\tstatus: varchar('status', { length: STATUS_LENGTH })\n\t\t\t.$type<AgentIdentityRegistrationStatus>()\n\t\t\t.notNull(),\n\t\tupdated_at_ms: bigint('updated_at_ms', { mode: 'number' }).notNull(),\n\t\tupstream_client_id: varchar('upstream_client_id', {\n\t\t\tlength: ID_LENGTH\n\t\t}),\n\t\tupstream_issuer: varchar('upstream_issuer', { length: ID_LENGTH }),\n\t\tupstream_subject: varchar('upstream_subject', { length: ID_LENGTH }),\n\t\tuser_id: varchar('user_id', { length: ID_LENGTH }),\n\t\tversion: integer('version').notNull()\n\t},\n\t(table) => [\n\t\tuniqueIndex('auth_agent_identity_upstream_unique').on(\n\t\t\ttable.upstream_issuer,\n\t\t\ttable.upstream_subject,\n\t\t\ttable.upstream_client_id\n\t\t)\n\t]\n);\nexport const agentRegistrationsTable = pgTable('auth_agent_registrations', {\n\tagent_id: varchar('agent_id', { length: ID_LENGTH }).primaryKey(),\n\tallowed_scopes: portableJsonb('allowed_scopes')\n\t\t.$type<string[]>()\n\t\t.notNull()\n\t\t.default([]),\n\tclient_id: varchar('client_id', { length: ID_LENGTH }).unique(),\n\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\tmetadata: portableJsonb('metadata').$type<Record<string, unknown>>(),\n\tname: varchar('name', { length: NAME_LENGTH }).notNull(),\n\tstatus: varchar('status', { length: STATUS_LENGTH })\n\t\t.$type<AgentRegistrationStatus>()\n\t\t.notNull(),\n\tupdated_at_ms: bigint('updated_at_ms', { mode: 'number' }).notNull()\n});\n\ntype RegistrationRow = typeof agentRegistrationsTable.$inferSelect;\ntype RegistrationInsert = typeof agentRegistrationsTable.$inferInsert;\ntype DelegationRow = typeof agentDelegationsTable.$inferSelect;\ntype DelegationInsert = typeof agentDelegationsTable.$inferInsert;\ntype IdentityRegistrationRow =\n\ttypeof agentIdentityRegistrationsTable.$inferSelect;\ntype IdentityRegistrationInsert =\n\ttypeof agentIdentityRegistrationsTable.$inferInsert;\n\nconst toRegistration = (row: RegistrationRow): AgentRegistration => ({\n\tagentId: row.agent_id,\n\tallowedScopes: row.allowed_scopes,\n\tclientId: row.client_id ?? undefined,\n\tcreatedAt: row.created_at_ms,\n\tmetadata: row.metadata ?? undefined,\n\tname: row.name,\n\tstatus: row.status,\n\tupdatedAt: row.updated_at_ms\n});\n\nconst toDelegation = (row: DelegationRow): AgentDelegation => ({\n\tagentId: row.agent_id,\n\tauthorizationDetails: row.authorization_details ?? undefined,\n\tcreatedAt: row.created_at_ms,\n\tdelegationId: row.delegation_id,\n\texpiresAt: row.expires_at_ms ?? undefined,\n\torganizationId: row.organization_id ?? undefined,\n\tscopes: row.scopes,\n\tstatus: row.status,\n\tupdatedAt: row.updated_at_ms,\n\tuserId: row.user_id\n});\n\nconst toIdentityRegistration = (\n\trow: IdentityRegistrationRow\n): AgentIdentityRegistration => ({\n\tagentId: row.agent_id,\n\tclaimAttempt: row.claim_attempt ?? undefined,\n\tclaimExpiresAt: row.claim_expires_at_ms,\n\tclaimTokenHash: row.claim_token_hash,\n\tcreatedAt: row.created_at_ms,\n\texpiresAt: row.expires_at_ms,\n\tkind: row.kind,\n\tlastPolledAt: row.last_polled_at_ms ?? undefined,\n\tloginHint: row.login_hint ?? undefined,\n\tregistrationId: row.registration_id,\n\tstatus: row.status,\n\tupdatedAt: row.updated_at_ms,\n\tupstream:\n\t\trow.upstream_client_id === null ||\n\t\trow.upstream_issuer === null ||\n\t\trow.upstream_subject === null\n\t\t\t? undefined\n\t\t\t: {\n\t\t\t\t\tclientId: row.upstream_client_id,\n\t\t\t\t\tissuer: row.upstream_issuer,\n\t\t\t\t\tsubject: row.upstream_subject\n\t\t\t\t},\n\tuserId: row.user_id ?? undefined,\n\tversion: row.version\n});\n\nconst identityRegistrationValues = (\n\tregistration: AgentIdentityRegistration\n): IdentityRegistrationInsert => ({\n\tagent_id: registration.agentId,\n\tclaim_attempt: registration.claimAttempt ?? null,\n\tclaim_attempt_token_hash: registration.claimAttempt?.tokenHash ?? null,\n\tclaim_expires_at_ms: registration.claimExpiresAt,\n\tclaim_token_hash: registration.claimTokenHash,\n\tcreated_at_ms: registration.createdAt,\n\texpires_at_ms: registration.expiresAt,\n\tkind: registration.kind,\n\tlast_polled_at_ms: registration.lastPolledAt ?? null,\n\tlogin_hint: registration.loginHint ?? null,\n\tregistration_id: registration.registrationId,\n\tstatus: registration.status,\n\tupdated_at_ms: registration.updatedAt,\n\tupstream_client_id: registration.upstream?.clientId ?? null,\n\tupstream_issuer: registration.upstream?.issuer ?? null,\n\tupstream_subject: registration.upstream?.subject ?? null,\n\tuser_id: registration.userId ?? null,\n\tversion: registration.version\n});\n\nexport const createNeonAgentDelegationStore = (databaseUrl: string) =>\n\tcreateDrizzleAgentDelegationStore(createNeonDatabase(databaseUrl));\nexport const createNeonAgentIdentityRegistrationStore = (databaseUrl: string) =>\n\tcreatePostgresAgentIdentityRegistrationStore(\n\t\tcreateNeonDatabase(databaseUrl)\n\t);\nexport const createNeonAgentRegistrationStore = (databaseUrl: string) =>\n\tcreatePostgresAgentRegistrationStore(createNeonDatabase(databaseUrl));\nexport const createDrizzleAgentDelegationStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): AgentDelegationStore => ({\n\tfindActiveDelegation: async ({\n\t\tagentId,\n\t\tnow = Date.now(),\n\t\torganizationId,\n\t\tuserId\n\t}) => {\n\t\tconst organizationCondition =\n\t\t\torganizationId === undefined\n\t\t\t\t? isNull(agentDelegationsTable.organization_id)\n\t\t\t\t: eq(agentDelegationsTable.organization_id, organizationId);\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(agentDelegationsTable)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(agentDelegationsTable.agent_id, agentId),\n\t\t\t\t\teq(agentDelegationsTable.user_id, userId),\n\t\t\t\t\torganizationCondition,\n\t\t\t\t\teq(agentDelegationsTable.status, 'active'),\n\t\t\t\t\tor(\n\t\t\t\t\t\tisNull(agentDelegationsTable.expires_at_ms),\n\t\t\t\t\t\tgt(agentDelegationsTable.expires_at_ms, now)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t\t.orderBy(desc(agentDelegationsTable.updated_at_ms))\n\t\t\t.limit(1);\n\n\t\treturn row === undefined ? undefined : toDelegation(row);\n\t},\n\tfindByDelegationId: async (delegationId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(agentDelegationsTable)\n\t\t\t.where(eq(agentDelegationsTable.delegation_id, delegationId))\n\t\t\t.limit(1);\n\n\t\treturn row === undefined ? undefined : toDelegation(row);\n\t},\n\tlistDelegations: async (agentId) => {\n\t\tconst base = db.select().from(agentDelegationsTable);\n\t\tconst rows = await (agentId === undefined\n\t\t\t? base.orderBy(desc(agentDelegationsTable.created_at_ms))\n\t\t\t: base\n\t\t\t\t\t.where(eq(agentDelegationsTable.agent_id, agentId))\n\t\t\t\t\t.orderBy(desc(agentDelegationsTable.created_at_ms)));\n\n\t\treturn rows.map(toDelegation);\n\t},\n\tsaveDelegation: async (delegation) => {\n\t\tconst values: DelegationInsert = {\n\t\t\tagent_id: delegation.agentId,\n\t\t\tauthorization_details: delegation.authorizationDetails ?? null,\n\t\t\tcreated_at_ms: delegation.createdAt,\n\t\t\tdelegation_id: delegation.delegationId,\n\t\t\texpires_at_ms: delegation.expiresAt ?? null,\n\t\t\torganization_id: delegation.organizationId ?? null,\n\t\t\tscopes: delegation.scopes,\n\t\t\tstatus: delegation.status,\n\t\t\tupdated_at_ms: delegation.updatedAt,\n\t\t\tuser_id: delegation.userId\n\t\t};\n\t\tawait db\n\t\t\t.insert(agentDelegationsTable)\n\t\t\t.values(values)\n\t\t\t.onConflictDoUpdate({\n\t\t\t\tset: values,\n\t\t\t\ttarget: agentDelegationsTable.delegation_id\n\t\t\t});\n\t}\n});\n/** @deprecated Use createDrizzleAgentDelegationStore. */\nexport const createPostgresAgentDelegationStore =\n\tcreateDrizzleAgentDelegationStore;\nexport const createPostgresAgentIdentityRegistrationStore = <\n\tDB extends AnyPgDatabase\n>(\n\tdb: DB\n): AgentIdentityRegistrationStore => ({\n\tcreate: async (registration) => {\n\t\tconst rows = await db\n\t\t\t.insert(agentIdentityRegistrationsTable)\n\t\t\t.values(identityRegistrationValues(registration))\n\t\t\t.onConflictDoNothing()\n\t\t\t.returning({ id: agentIdentityRegistrationsTable.registration_id });\n\n\t\treturn rows.length === 1;\n\t},\n\tfindByAgentId: async (agentId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(agentIdentityRegistrationsTable)\n\t\t\t.where(eq(agentIdentityRegistrationsTable.agent_id, agentId))\n\t\t\t.limit(1);\n\n\t\treturn row === undefined ? undefined : toIdentityRegistration(row);\n\t},\n\tfindByAttemptTokenHash: async (attemptTokenHash) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(agentIdentityRegistrationsTable)\n\t\t\t.where(\n\t\t\t\teq(\n\t\t\t\t\tagentIdentityRegistrationsTable.claim_attempt_token_hash,\n\t\t\t\t\tattemptTokenHash\n\t\t\t\t)\n\t\t\t)\n\t\t\t.limit(1);\n\n\t\treturn row === undefined ? undefined : toIdentityRegistration(row);\n\t},\n\tfindByClaimTokenHash: async (claimTokenHash) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(agentIdentityRegistrationsTable)\n\t\t\t.where(\n\t\t\t\teq(\n\t\t\t\t\tagentIdentityRegistrationsTable.claim_token_hash,\n\t\t\t\t\tclaimTokenHash\n\t\t\t\t)\n\t\t\t)\n\t\t\t.limit(1);\n\n\t\treturn row === undefined ? undefined : toIdentityRegistration(row);\n\t},\n\tfindByRegistrationId: async (registrationId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(agentIdentityRegistrationsTable)\n\t\t\t.where(\n\t\t\t\teq(\n\t\t\t\t\tagentIdentityRegistrationsTable.registration_id,\n\t\t\t\t\tregistrationId\n\t\t\t\t)\n\t\t\t)\n\t\t\t.limit(1);\n\n\t\treturn row === undefined ? undefined : toIdentityRegistration(row);\n\t},\n\tfindByUpstreamIdentity: async ({ clientId, issuer, subject }) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(agentIdentityRegistrationsTable)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(\n\t\t\t\t\t\tagentIdentityRegistrationsTable.upstream_client_id,\n\t\t\t\t\t\tclientId\n\t\t\t\t\t),\n\t\t\t\t\teq(agentIdentityRegistrationsTable.upstream_issuer, issuer),\n\t\t\t\t\teq(\n\t\t\t\t\t\tagentIdentityRegistrationsTable.upstream_subject,\n\t\t\t\t\t\tsubject\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t\t.limit(1);\n\n\t\treturn row === undefined ? undefined : toIdentityRegistration(row);\n\t},\n\treplace: async (registration, expectedVersion) => {\n\t\tconst next: AgentIdentityRegistration = {\n\t\t\t...registration,\n\t\t\tversion: expectedVersion + 1\n\t\t};\n\t\tconst values = identityRegistrationValues(next);\n\t\tconst rows = await db\n\t\t\t.update(agentIdentityRegistrationsTable)\n\t\t\t.set(values)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(\n\t\t\t\t\t\tagentIdentityRegistrationsTable.registration_id,\n\t\t\t\t\t\tregistration.registrationId\n\t\t\t\t\t),\n\t\t\t\t\teq(agentIdentityRegistrationsTable.version, expectedVersion)\n\t\t\t\t)\n\t\t\t)\n\t\t\t.returning({ id: agentIdentityRegistrationsTable.registration_id });\n\n\t\treturn rows.length === 1;\n\t}\n});\nexport const createPostgresAgentRegistrationStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): AgentRegistrationStore => ({\n\tfindByAgentId: async (agentId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(agentRegistrationsTable)\n\t\t\t.where(eq(agentRegistrationsTable.agent_id, agentId))\n\t\t\t.limit(1);\n\n\t\treturn row === undefined ? undefined : toRegistration(row);\n\t},\n\tfindByClientId: async (clientId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(agentRegistrationsTable)\n\t\t\t.where(eq(agentRegistrationsTable.client_id, clientId))\n\t\t\t.limit(1);\n\n\t\treturn row === undefined ? undefined : toRegistration(row);\n\t},\n\tlistRegistrations: async () => {\n\t\tconst rows = await db\n\t\t\t.select()\n\t\t\t.from(agentRegistrationsTable)\n\t\t\t.orderBy(desc(agentRegistrationsTable.created_at_ms));\n\n\t\treturn rows.map(toRegistration);\n\t},\n\tsaveRegistration: async (registration) => {\n\t\tconst values: RegistrationInsert = {\n\t\t\tagent_id: registration.agentId,\n\t\t\tallowed_scopes: registration.allowedScopes,\n\t\t\tclient_id: registration.clientId ?? null,\n\t\t\tcreated_at_ms: registration.createdAt,\n\t\t\tmetadata: registration.metadata ?? null,\n\t\t\tname: registration.name,\n\t\t\tstatus: registration.status,\n\t\t\tupdated_at_ms: registration.updatedAt\n\t\t};\n\t\tawait db\n\t\t\t.insert(agentRegistrationsTable)\n\t\t\t.values(values)\n\t\t\t.onConflictDoUpdate({\n\t\t\t\tset: values,\n\t\t\t\ttarget: agentRegistrationsTable.agent_id\n\t\t\t});\n\t}\n});\n",
|
|
15
15
|
"import { desc, eq, lt } from 'drizzle-orm';\nimport { bigint, jsonb, pgTable, varchar } from 'drizzle-orm/pg-core';\nimport { type AnyPgDatabase, createNeonDatabase } from '../stores/postgres';\nimport type { AuditEvent, AuditEventType, AuditSink } from './types';\n\nconst ID_LENGTH = 255;\nconst IP_LENGTH = 64;\nconst TYPE_LENGTH = 64;\nconst DEFAULT_AUDIT_LIMIT = 100;\n\nexport const auditEventsTable = pgTable('auth_audit_events', {\n\tat_ms: bigint('at_ms', { mode: 'number' }).notNull(),\n\tid: varchar('id', { length: ID_LENGTH }).primaryKey(),\n\tip: varchar('ip', { length: IP_LENGTH }),\n\tmetadata_json: jsonb('metadata_json').$type<Record<string, unknown>>(),\n\torganization_id: varchar('organization_id', { length: ID_LENGTH }),\n\ttype: varchar('type', { length: TYPE_LENGTH }).notNull(),\n\tuser_id: varchar('user_id', { length: ID_LENGTH })\n});\n\ntype AuditRow = typeof auditEventsTable.$inferSelect;\n\nconst toEvent = (row: AuditRow): AuditEvent => ({\n\tat: row.at_ms,\n\tip: row.ip ?? undefined,\n\tmetadata: row.metadata_json ?? undefined,\n\torganizationId: row.organization_id ?? undefined,\n\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- deserialization boundary: `type` was persisted from AuditEventType, so reading it back is sound\n\ttype: row.type as AuditEventType,\n\tuserId: row.user_id ?? undefined\n});\n\nexport const createNeonAuditSink = (databaseUrl: string) =>\n\tcreatePostgresAuditSink(createNeonDatabase(databaseUrl));\nexport const createPostgresAuditSink = <DB extends AnyPgDatabase>(\n\tdb: DB\n): AuditSink => ({\n\tappend: async (event) => {\n\t\tawait db.insert(auditEventsTable).values({\n\t\t\tat_ms: event.at,\n\t\t\tid: crypto.randomUUID(),\n\t\t\tip: event.ip ?? null,\n\t\t\tmetadata_json: event.metadata ?? null,\n\t\t\torganization_id: event.organizationId ?? null,\n\t\t\ttype: event.type,\n\t\t\tuser_id: event.userId ?? null\n\t\t});\n\t},\n\tlist: async (filter) => {\n\t\tconst rows = await db\n\t\t\t.select()\n\t\t\t.from(auditEventsTable)\n\t\t\t.where(\n\t\t\t\tfilter?.userId\n\t\t\t\t\t? eq(auditEventsTable.user_id, filter.userId)\n\t\t\t\t\t: undefined\n\t\t\t)\n\t\t\t.orderBy(desc(auditEventsTable.at_ms))\n\t\t\t.limit(filter?.limit ?? DEFAULT_AUDIT_LIMIT);\n\n\t\treturn rows.map(toEvent);\n\t},\n\tprune: async (before) => {\n\t\tconst deleted = await db\n\t\t\t.delete(auditEventsTable)\n\t\t\t.where(lt(auditEventsTable.at_ms, before))\n\t\t\t.returning({ id: auditEventsTable.id });\n\n\t\treturn deleted.length;\n\t}\n});\n",
|
|
16
16
|
"import { eq } from 'drizzle-orm';\nimport { bigint, boolean, pgTable, text, varchar } from 'drizzle-orm/pg-core';\nimport { type AnyPgDatabase, createNeonDatabase } from '../stores/postgres';\nimport type {\n\tCredentialRecord,\n\tCredentialStatus,\n\tCredentialStore,\n\tCredentialToken\n} from './types';\n\nconst EMAIL_LENGTH = 320;\nconst ID_LENGTH = 255;\nconst STATUS_LENGTH = 32;\nconst TOKEN_HASH_LENGTH = 255;\n\nexport const credentialsTable = pgTable('auth_credentials', {\n\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\temail: varchar('email', { length: EMAIL_LENGTH }).primaryKey(),\n\temail_verified: boolean('email_verified').notNull().default(false),\n\torganization_id: varchar('organization_id', { length: ID_LENGTH }),\n\tpassword_hash: text('password_hash').notNull(),\n\tstatus: varchar('status', { length: STATUS_LENGTH })\n\t\t.notNull()\n\t\t.default('active'),\n\tupdated_at_ms: bigint('updated_at_ms', { mode: 'number' }).notNull(),\n\tuser_id: varchar('user_id', { length: ID_LENGTH })\n});\n\nconst createTokenTable = (name: string) =>\n\tpgTable(name, {\n\t\temail: varchar('email', { length: EMAIL_LENGTH }).notNull(),\n\t\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }).notNull(),\n\t\ttoken_hash: varchar('token_hash', {\n\t\t\tlength: TOKEN_HASH_LENGTH\n\t\t}).primaryKey()\n\t});\n\nexport const credentialResetTokensTable = createTokenTable(\n\t'auth_credential_reset_tokens'\n);\nexport const credentialVerificationTokensTable = createTokenTable(\n\t'auth_credential_verification_tokens'\n);\n\ntype CredentialRow = typeof credentialsTable.$inferSelect;\ntype CredentialInsert = typeof credentialsTable.$inferInsert;\ntype TokenTable = ReturnType<typeof createTokenTable>;\ntype TokenRow = TokenTable['$inferSelect'];\ntype TokenInsert = TokenTable['$inferInsert'];\n\nconst CREDENTIAL_STATUSES: CredentialStatus[] = ['active', 'disabled'];\n\nconst isCredentialStatus = (value: string): value is CredentialStatus =>\n\tCREDENTIAL_STATUSES.some((status) => status === value);\n\nconst toCredentialRecord = (row: CredentialRow): CredentialRecord => ({\n\tcreatedAt: row.created_at_ms,\n\temail: row.email,\n\temailVerified: row.email_verified,\n\torganizationId: row.organization_id ?? undefined,\n\tpasswordHash: row.password_hash,\n\tstatus: isCredentialStatus(row.status) ? row.status : 'active',\n\tupdatedAt: row.updated_at_ms,\n\tuserId: row.user_id ?? undefined\n});\n\nconst toToken = (row: TokenRow): CredentialToken => ({\n\temail: row.email,\n\texpiresAt: row.expires_at_ms,\n\ttokenHash: row.token_hash\n});\n\nconst saveToken = async <DB extends AnyPgDatabase>(\n\tdb: DB,\n\ttable: TokenTable,\n\ttoken: CredentialToken\n) => {\n\tconst values: TokenInsert = {\n\t\temail: token.email.toLowerCase(),\n\t\texpires_at_ms: token.expiresAt,\n\t\ttoken_hash: token.tokenHash\n\t};\n\tawait db\n\t\t.insert(table)\n\t\t.values(values)\n\t\t.onConflictDoUpdate({ set: values, target: table.token_hash });\n};\n\nconst consumeToken = async <DB extends AnyPgDatabase>(\n\tdb: DB,\n\ttable: TokenTable,\n\ttokenHash: string\n) => {\n\tconst [row] = await db\n\t\t.select()\n\t\t.from(table)\n\t\t.where(eq(table.token_hash, tokenHash))\n\t\t.limit(1);\n\tif (!row) return undefined;\n\n\tawait db.delete(table).where(eq(table.token_hash, tokenHash));\n\tif (row.expires_at_ms < Date.now()) return undefined;\n\n\treturn toToken(row);\n};\n\nexport const createNeonCredentialStore = (databaseUrl: string) =>\n\tcreatePostgresCredentialStore(createNeonDatabase(databaseUrl));\nexport const createPostgresCredentialStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): CredentialStore => ({\n\tconsumeResetToken: (tokenHash) =>\n\t\tconsumeToken(db, credentialResetTokensTable, tokenHash),\n\tconsumeVerificationToken: (tokenHash) =>\n\t\tconsumeToken(db, credentialVerificationTokensTable, tokenHash),\n\tgetCredentialByEmail: async (email) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(credentialsTable)\n\t\t\t.where(eq(credentialsTable.email, email.toLowerCase()))\n\t\t\t.limit(1);\n\n\t\treturn row ? toCredentialRecord(row) : undefined;\n\t},\n\tsaveCredential: async (credential) => {\n\t\tconst values: CredentialInsert = {\n\t\t\tcreated_at_ms: credential.createdAt,\n\t\t\temail: credential.email.toLowerCase(),\n\t\t\temail_verified: credential.emailVerified,\n\t\t\torganization_id: credential.organizationId ?? null,\n\t\t\tpassword_hash: credential.passwordHash,\n\t\t\tstatus: credential.status,\n\t\t\tupdated_at_ms: credential.updatedAt,\n\t\t\tuser_id: credential.userId ?? null\n\t\t};\n\t\tawait db.insert(credentialsTable).values(values).onConflictDoUpdate({\n\t\t\tset: values,\n\t\t\ttarget: credentialsTable.email\n\t\t});\n\t},\n\tsaveResetToken: (token) => saveToken(db, credentialResetTokensTable, token),\n\tsaveVerificationToken: (token) =>\n\t\tsaveToken(db, credentialVerificationTokensTable, token),\n\tsetEmailVerified: async (email) => {\n\t\tawait db\n\t\t\t.update(credentialsTable)\n\t\t\t.set({ email_verified: true, updated_at_ms: Date.now() })\n\t\t\t.where(eq(credentialsTable.email, email.toLowerCase()));\n\t}\n});\n",
|
|
17
17
|
"import { and, eq } from 'drizzle-orm';\nimport { pgTable, varchar } from 'drizzle-orm/pg-core';\nimport { type AnyPgDatabase, createNeonDatabase } from '../stores/postgres';\nimport { warrantKey } from './inMemoryStores';\nimport type { Warrant, WarrantStore } from './types';\n\nconst ID_LENGTH = 255;\n\nexport const warrantsTable = pgTable('auth_fga_warrants', {\n\tid: varchar('id', { length: ID_LENGTH }).primaryKey(),\n\trelation: varchar('relation', { length: ID_LENGTH }).notNull(),\n\tresource_id: varchar('resource_id', { length: ID_LENGTH }).notNull(),\n\tresource_type: varchar('resource_type', { length: ID_LENGTH }).notNull(),\n\tsubject_id: varchar('subject_id', { length: ID_LENGTH }).notNull(),\n\tsubject_relation: varchar('subject_relation', { length: ID_LENGTH }),\n\tsubject_type: varchar('subject_type', { length: ID_LENGTH }).notNull()\n});\n\ntype WarrantRow = typeof warrantsTable.$inferSelect;\n\nconst toWarrant = (row: WarrantRow): Warrant => ({\n\trelation: row.relation,\n\tresourceId: row.resource_id,\n\tresourceType: row.resource_type,\n\tsubjectId: row.subject_id,\n\tsubjectRelation: row.subject_relation ?? undefined,\n\tsubjectType: row.subject_type\n});\n\nexport const createNeonWarrantStore = (databaseUrl: string) =>\n\tcreatePostgresWarrantStore(createNeonDatabase(databaseUrl));\nexport const createPostgresWarrantStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): WarrantStore => ({\n\tdeleteWarrant: async (warrant) => {\n\t\tawait db\n\t\t\t.delete(warrantsTable)\n\t\t\t.where(eq(warrantsTable.id, warrantKey(warrant)));\n\t},\n\tlistForResource: async (resourceType, resourceId, relation) => {\n\t\tconst rows = await db\n\t\t\t.select()\n\t\t\t.from(warrantsTable)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(warrantsTable.resource_type, resourceType),\n\t\t\t\t\teq(warrantsTable.resource_id, resourceId),\n\t\t\t\t\teq(warrantsTable.relation, relation)\n\t\t\t\t)\n\t\t\t);\n\n\t\treturn rows.map(toWarrant);\n\t},\n\tlistResourceIds: async (resourceType) => {\n\t\tconst rows = await db\n\t\t\t.selectDistinct({ resourceId: warrantsTable.resource_id })\n\t\t\t.from(warrantsTable)\n\t\t\t.where(eq(warrantsTable.resource_type, resourceType));\n\n\t\treturn rows.map((row) => row.resourceId);\n\t},\n\tsaveWarrant: async (warrant) => {\n\t\tawait db\n\t\t\t.insert(warrantsTable)\n\t\t\t.values({\n\t\t\t\tid: warrantKey(warrant),\n\t\t\t\trelation: warrant.relation,\n\t\t\t\tresource_id: warrant.resourceId,\n\t\t\t\tresource_type: warrant.resourceType,\n\t\t\t\tsubject_id: warrant.subjectId,\n\t\t\t\tsubject_relation: warrant.subjectRelation ?? null,\n\t\t\t\tsubject_type: warrant.subjectType\n\t\t\t})\n\t\t\t.onConflictDoNothing({ target: warrantsTable.id });\n\t}\n});\n",
|
package/dist/index.d.ts
CHANGED
|
@@ -3317,26 +3317,7 @@ export declare const createAuthApplications: <UserType>(configuration: AuthConfi
|
|
|
3317
3317
|
};
|
|
3318
3318
|
};
|
|
3319
3319
|
};
|
|
3320
|
-
}))) & ({} | {
|
|
3321
|
-
[x: string]: {
|
|
3322
|
-
get: {
|
|
3323
|
-
body: unknown;
|
|
3324
|
-
params: {};
|
|
3325
|
-
query: unknown;
|
|
3326
|
-
headers: unknown;
|
|
3327
|
-
response: {
|
|
3328
|
-
200: {
|
|
3329
|
-
resource: string;
|
|
3330
|
-
scopes_supported: string[];
|
|
3331
|
-
resource_name?: string | undefined;
|
|
3332
|
-
resource_logo_uri?: string | undefined;
|
|
3333
|
-
authorization_servers: string[];
|
|
3334
|
-
bearer_methods_supported: string[];
|
|
3335
|
-
};
|
|
3336
|
-
};
|
|
3337
|
-
};
|
|
3338
|
-
};
|
|
3339
|
-
} | ({
|
|
3320
|
+
}))) & ({} | ({
|
|
3340
3321
|
[x: string]: {
|
|
3341
3322
|
get: {
|
|
3342
3323
|
body: unknown;
|
|
@@ -3568,11 +3549,12 @@ export * from './agents/types';
|
|
|
3568
3549
|
export * from './agents/registration';
|
|
3569
3550
|
export * from './agents/registrationClient';
|
|
3570
3551
|
export * from './agents/idJag';
|
|
3552
|
+
export * from './agents/oauthGuide';
|
|
3571
3553
|
export { agentAuthPlugin, agentAuthChallenge } from './agents/routes';
|
|
3572
3554
|
export { resolveAgentPrincipal, agentHasScopes } from './agents/principal';
|
|
3573
3555
|
export { createOidcAgentCredentialVerifier } from './agents/oidcAdapter';
|
|
3574
3556
|
export { createInMemoryAgentDelegationStore, createInMemoryAgentIdentityRegistrationStore, createInMemoryAgentRegistrationStore } from './agents/inMemoryStores';
|
|
3575
|
-
export { agentDelegationsTable, agentIdentityRegistrationsTable, agentRegistrationsTable, createNeonAgentDelegationStore, createNeonAgentIdentityRegistrationStore, createNeonAgentRegistrationStore, createPostgresAgentDelegationStore, createPostgresAgentIdentityRegistrationStore, createPostgresAgentRegistrationStore } from './agents/postgresStores';
|
|
3557
|
+
export { agentDelegationsTable, agentIdentityRegistrationsTable, agentRegistrationsTable, createNeonAgentDelegationStore, createNeonAgentIdentityRegistrationStore, createNeonAgentRegistrationStore, createDrizzleAgentDelegationStore, createPostgresAgentDelegationStore, createPostgresAgentIdentityRegistrationStore, createPostgresAgentRegistrationStore } from './agents/postgresStores';
|
|
3576
3558
|
export { createInMemoryAccessTokenStore, createInMemoryApiClientStore, createInMemoryApiKeyStore } from './apikeys/inMemoryStores';
|
|
3577
3559
|
export { accessTokensTable, apiClientsTable, apiKeysTable, createNeonAccessTokenStore, createNeonApiClientStore, createNeonApiKeyStore, createPostgresAccessTokenStore, createPostgresApiClientStore, createPostgresApiKeyStore } from './apikeys/postgresStores';
|
|
3578
3560
|
export * from './oidc/config';
|
package/dist/index.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
|
|
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 ??
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 (
|
|
26971
|
+
if (secureUrl2(metadata.body.issuer, allowLocalhost) !== authorizationServer) {
|
|
26872
26972
|
throw new Error("Authorization server issuer mismatch");
|
|
26873
26973
|
}
|
|
26874
|
-
const tokenEndpoint =
|
|
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 =
|
|
26880
|
-
const claimEndpoint =
|
|
26881
|
-
const skill =
|
|
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");
|
|
@@ -27328,10 +27428,10 @@ var identityRegistrationValues = (registration) => ({
|
|
|
27328
27428
|
user_id: registration.userId ?? null,
|
|
27329
27429
|
version: registration.version
|
|
27330
27430
|
});
|
|
27331
|
-
var createNeonAgentDelegationStore = (databaseUrl) =>
|
|
27431
|
+
var createNeonAgentDelegationStore = (databaseUrl) => createDrizzleAgentDelegationStore(createNeonDatabase(databaseUrl));
|
|
27332
27432
|
var createNeonAgentIdentityRegistrationStore = (databaseUrl) => createPostgresAgentIdentityRegistrationStore(createNeonDatabase(databaseUrl));
|
|
27333
27433
|
var createNeonAgentRegistrationStore = (databaseUrl) => createPostgresAgentRegistrationStore(createNeonDatabase(databaseUrl));
|
|
27334
|
-
var
|
|
27434
|
+
var createDrizzleAgentDelegationStore = (db) => ({
|
|
27335
27435
|
findActiveDelegation: async ({
|
|
27336
27436
|
agentId,
|
|
27337
27437
|
now = Date.now(),
|
|
@@ -27370,6 +27470,7 @@ var createPostgresAgentDelegationStore = (db) => ({
|
|
|
27370
27470
|
});
|
|
27371
27471
|
}
|
|
27372
27472
|
});
|
|
27473
|
+
var createPostgresAgentDelegationStore = createDrizzleAgentDelegationStore;
|
|
27373
27474
|
var createPostgresAgentIdentityRegistrationStore = (db) => ({
|
|
27374
27475
|
create: async (registration) => {
|
|
27375
27476
|
const rows = await db.insert(agentIdentityRegistrationsTable).values(identityRegistrationValues(registration)).onConflictDoNothing().returning({ id: agentIdentityRegistrationsTable.registration_id });
|
|
@@ -27638,7 +27739,7 @@ var createPostgresApiKeyStore = (db) => ({
|
|
|
27638
27739
|
}
|
|
27639
27740
|
});
|
|
27640
27741
|
// src/oidc/clientIdMetadata.ts
|
|
27641
|
-
var
|
|
27742
|
+
var secureUrl3 = (value) => {
|
|
27642
27743
|
try {
|
|
27643
27744
|
return new URL(value).protocol === "https:";
|
|
27644
27745
|
} catch {
|
|
@@ -27649,7 +27750,7 @@ var validateClientIdMetadataDocument = (document, expectedClientId) => {
|
|
|
27649
27750
|
const errors = [];
|
|
27650
27751
|
if (document.client_id !== expectedClientId)
|
|
27651
27752
|
errors.push("client_id does not match the metadata document URL");
|
|
27652
|
-
if (!
|
|
27753
|
+
if (!secureUrl3(document.client_id))
|
|
27653
27754
|
errors.push("client_id must use HTTPS");
|
|
27654
27755
|
if (!Array.isArray(document.redirect_uris) || document.redirect_uris.length === 0)
|
|
27655
27756
|
errors.push("redirect_uris is required");
|
|
@@ -27669,7 +27770,7 @@ var validateClientIdMetadataDocument = (document, expectedClientId) => {
|
|
|
27669
27770
|
["tos_uri", document.tos_uri],
|
|
27670
27771
|
["jwks_uri", document.jwks_uri]
|
|
27671
27772
|
]) {
|
|
27672
|
-
if (value !== undefined && !
|
|
27773
|
+
if (value !== undefined && !secureUrl3(value))
|
|
27673
27774
|
errors.push(`${name2} must use HTTPS`);
|
|
27674
27775
|
}
|
|
27675
27776
|
return errors;
|
|
@@ -27692,7 +27793,7 @@ var createClientIdMetadataResolver = ({
|
|
|
27692
27793
|
}) => {
|
|
27693
27794
|
const cache = new Map;
|
|
27694
27795
|
return async (clientId) => {
|
|
27695
|
-
if (!
|
|
27796
|
+
if (!secureUrl3(clientId) || !await allow(clientId))
|
|
27696
27797
|
return;
|
|
27697
27798
|
const cached = cache.get(clientId);
|
|
27698
27799
|
if (cached !== undefined && cached.expiresAt > now())
|
|
@@ -30371,6 +30472,22 @@ var buildAuthApplications = async (configuration) => {
|
|
|
30371
30472
|
tokenTtlMs: registration2.tokenTtlMs
|
|
30372
30473
|
});
|
|
30373
30474
|
}
|
|
30475
|
+
if (agentAuth?.oauthGuide !== undefined) {
|
|
30476
|
+
if (oidc === undefined)
|
|
30477
|
+
throw new Error("agentAuth.oauthGuide requires the OIDC provider");
|
|
30478
|
+
if (agentAuth.authorizationServer !== oidc.issuer)
|
|
30479
|
+
throw new Error("agentAuth.authorizationServer must equal oidc.issuer");
|
|
30480
|
+
if (agentAuth.oauthGuide.authorizationServer !== agentAuth.authorizationServer)
|
|
30481
|
+
throw new Error("agentAuth.oauthGuide.authorizationServer must equal agentAuth.authorizationServer");
|
|
30482
|
+
const unknownGuideScopes = agentAuth.oauthGuide.resources.flatMap(({ scopes }) => scopes).filter((scope) => !agentAuth.scopes.includes(scope));
|
|
30483
|
+
if (unknownGuideScopes.length > 0)
|
|
30484
|
+
throw new Error(`Agent OAuth guide uses undeclared scopes: ${[
|
|
30485
|
+
...new Set(unknownGuideScopes)
|
|
30486
|
+
].join(", ")}`);
|
|
30487
|
+
if (agentAuth.agentRegistration !== undefined && (agentAuth.agentRegistration.guideRoute ?? "/auth.md") === (agentAuth.oauthGuide.route ?? "/auth.md"))
|
|
30488
|
+
throw new Error("Agent OAuth and registration guides cannot use the same route");
|
|
30489
|
+
generateAgentOAuthGuide(agentAuth.oauthGuide);
|
|
30490
|
+
}
|
|
30374
30491
|
if (tracing !== undefined)
|
|
30375
30492
|
await initTracing(tracing);
|
|
30376
30493
|
const clientProviders = await buildClientProviders(providersConfiguration, createOAuth2Client, customProviders);
|
|
@@ -30392,6 +30509,9 @@ var buildAuthApplications = async (configuration) => {
|
|
|
30392
30509
|
...oidc,
|
|
30393
30510
|
additionalDiscoveryMetadata: {
|
|
30394
30511
|
...oidc.additionalDiscoveryMetadata,
|
|
30512
|
+
...resolvedAgentAuth?.oauthGuide === undefined ? {} : {
|
|
30513
|
+
service_documentation: agentOAuthGuideUrl(resolvedAgentAuth.oauthGuide)
|
|
30514
|
+
},
|
|
30395
30515
|
...resolvedAgentAuth?.agentRegistration === undefined ? {} : {
|
|
30396
30516
|
agent_auth: agentRegistrationDiscoveryMetadata(resolvedAgentAuth)
|
|
30397
30517
|
}
|
|
@@ -30797,6 +30917,7 @@ export {
|
|
|
30797
30917
|
generateEncryptionKey,
|
|
30798
30918
|
generateBackupCodes,
|
|
30799
30919
|
generateAgentRegistrationGuide,
|
|
30920
|
+
generateAgentOAuthGuide,
|
|
30800
30921
|
fromBase64Url2 as fromBase64Url,
|
|
30801
30922
|
fingerprintDevice,
|
|
30802
30923
|
fetchUserInfo,
|
|
@@ -30978,6 +31099,7 @@ export {
|
|
|
30978
31099
|
createInMemoryAccessTokenStore,
|
|
30979
31100
|
createFgaEngine,
|
|
30980
31101
|
createFederatedTokenStore,
|
|
31102
|
+
createDrizzleAgentDelegationStore,
|
|
30981
31103
|
createCustomOAuth2Client,
|
|
30982
31104
|
createCredentialOffer,
|
|
30983
31105
|
createClientIdMetadataResolver,
|
|
@@ -31026,6 +31148,8 @@ export {
|
|
|
31026
31148
|
agentRegistrationEndpoints,
|
|
31027
31149
|
agentRegistrationDiscoveryMetadata,
|
|
31028
31150
|
agentProtectedResourceMetadata,
|
|
31151
|
+
agentOAuthGuideUrl,
|
|
31152
|
+
agentOAuthGuideRoute,
|
|
31029
31153
|
agentIdentityRegistrationsTable,
|
|
31030
31154
|
agentHasScopes,
|
|
31031
31155
|
agentDelegationsTable,
|
|
@@ -31083,5 +31207,5 @@ export {
|
|
|
31083
31207
|
AGENT_CLAIM_GRANT_TYPE
|
|
31084
31208
|
};
|
|
31085
31209
|
|
|
31086
|
-
//# debugId=
|
|
31210
|
+
//# debugId=C85526782954EAFA64756E2164756E21
|
|
31087
31211
|
//# sourceMappingURL=index.js.map
|