Files
zpan/server/usecases/ports/api-keys.ts
T
agent-kanban[bot] 1b1b1db772 feat: add workspace agent API keys (#538)
Enforce owner/admin management, terminal expired/revoked lifecycle, explicit workspace scopes, current membership rechecks, and authenticated management UI.
2026-07-29 10:08:44 -04:00

59 lines
2.0 KiB
TypeScript

import type { ApiKeyScope } from '@shared/api-key-templates'
import type { ApiKeyPermissions, AuthorizationScope } from '@shared/authorization'
import type { AgentApiKey, AgentApiKeyCreated, AgentGrantableScope } from '@shared/schemas'
import type { Database } from '../../platform/interface'
export interface VerifiedApiKey {
id: string
configId: string
referenceId: string
scope: ApiKeyScope
permissions: ApiKeyPermissions | null
}
// Structural view of better-auth used by the gateway. Keeps the port free of the
// better-auth framework type while letting callers pass the real `Auth`.
export interface ApiKeyAuth {
api: Record<string, unknown>
}
// Thrown by the gateway when better-auth reports the key is rate limited. The
// http layer (business routes + WebDAV) maps it to 429 with Retry-After.
export class ApiKeyRateLimitError extends Error {
constructor(
message: string,
public readonly retryAfterMs?: number,
) {
super(message)
this.name = 'ApiKeyRateLimitError'
}
}
export interface ApiKeyGateway {
verifyApiKey(auth: ApiKeyAuth, db: Database, key: string, configId?: string): Promise<VerifiedApiKey | null>
verifyApiKeyForPermission(
auth: ApiKeyAuth,
db: Database,
key: string,
resource: string,
action: string,
configId?: string,
): Promise<VerifiedApiKey | null>
hasApiKeyPermission(permissions: ApiKeyPermissions | null | undefined, resource: string, action: string): boolean
hasApiKeyScope(permissions: ApiKeyPermissions | null | undefined, scope: AuthorizationScope): boolean
listAgentApiKeys(db: Database, userId: string, orgId: string, now: Date): Promise<AgentApiKey[]>
getAgentApiKey(db: Database, userId: string, orgId: string, keyId: string, now: Date): Promise<AgentApiKey | null>
issueAgentApiKey(
db: Database,
input: {
name: string
userId: string
orgId: string
scopes: AgentGrantableScope[]
expiresAt: Date
revokeKeyId?: string
},
): Promise<AgentApiKeyCreated>
revokeAgentApiKey(db: Database, keyId: string): Promise<void>
}