refactor(oauth): replace agent access naming (#549)

This commit is contained in:
Jasper Van
2026-08-01 00:32:31 -04:00
committed by GitHub
parent d15ca86226
commit 1caa4e580c
66 changed files with 1356 additions and 1374 deletions
+2 -2
View File
@@ -1,5 +1,4 @@
import { describe, expect, it } from 'vitest'
import { AGENT_OAUTH_RESOURCE_SCOPES } from './agent-oauth'
import { WEBDAV_API_KEY_PERMISSIONS } from './api-key-templates'
import {
AuthorizationScope,
@@ -7,6 +6,7 @@ import {
CANONICAL_AUTHORIZATION_SCOPES,
scopePermissions,
} from './authorization'
import { OAUTH_RESOURCE_SCOPES } from './oauth'
describe('authorization scope registry', () => {
it('uses lowercase resource:action scopes without wildcard semantics', () => {
@@ -22,7 +22,7 @@ describe('authorization scope registry', () => {
it('keeps permanent object purge out of agent-grantable scopes', () => {
expect(CANONICAL_AUTHORIZATION_SCOPES).toContain(AuthorizationScope.OBJECTS_PURGE)
expect(AGENT_OAUTH_RESOURCE_SCOPES).not.toContain(AuthorizationScope.OBJECTS_PURGE)
expect(OAUTH_RESOURCE_SCOPES).not.toContain(AuthorizationScope.OBJECTS_PURGE)
expect(scopePermissions([AuthorizationScope.OBJECTS_DELETE])).toEqual({ objects: ['delete'] })
})
+3 -3
View File
@@ -15,9 +15,9 @@ export const AuthorizationScope = {
DOWNLOAD_TASKS_CREATE: 'download-tasks:create',
DOWNLOAD_TASKS_CANCEL: 'download-tasks:cancel',
SITE_ANALYTICS_READ: 'site-analytics:read',
AGENT_OAUTH_GRANTS_READ: 'agent-oauth-grants:read',
AGENT_OAUTH_GRANTS_CREATE: 'agent-oauth-grants:create',
AGENT_OAUTH_GRANTS_DELETE: 'agent-oauth-grants:delete',
OAUTH_GRANTS_READ: 'oauth-grants:read',
OAUTH_GRANTS_CREATE: 'oauth-grants:create',
OAUTH_GRANTS_DELETE: 'oauth-grants:delete',
BACKGROUND_JOBS_READ: 'background-jobs:read',
BACKGROUND_JOBS_CREATE: 'background-jobs:create',
BACKGROUND_JOBS_UPDATE: 'background-jobs:update',
+7 -7
View File
@@ -1,14 +1,14 @@
import { AuthorizationScope } from './authorization'
export const AGENT_OAUTH_ACCESS_TOKEN_SECONDS = 15 * 60
export const AGENT_OAUTH_REFRESH_TOKEN_SECONDS = 30 * 24 * 60 * 60
export const AGENT_OAUTH_ACTOR_TOKEN_SECONDS = 5 * 60
export const OAUTH_ACCESS_TOKEN_SECONDS = 15 * 60
export const OAUTH_REFRESH_TOKEN_SECONDS = 30 * 24 * 60 * 60
export const OAUTH_ACTOR_TOKEN_SECONDS = 5 * 60
export const JWT_BEARER_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:jwt-bearer'
export const TOKEN_EXCHANGE_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:token-exchange'
export const OAUTH_ACCESS_TOKEN_TYPE = 'urn:ietf:params:oauth:token-type:access_token'
export const AGENT_ACTOR_RESOURCE = 'urn:zpan:oauth:agent-actor'
export const AGENT_OAUTH_STANDARD_SCOPES = ['openid', 'profile', 'email', 'offline_access'] as const
export const AGENT_OAUTH_RESOURCE_SCOPES = [
export const OAUTH_STANDARD_SCOPES = ['openid', 'profile', 'email', 'offline_access'] as const
export const OAUTH_RESOURCE_SCOPES = [
AuthorizationScope.OBJECTS_READ,
AuthorizationScope.OBJECTS_CREATE,
AuthorizationScope.OBJECTS_UPDATE,
@@ -20,8 +20,8 @@ export const AGENT_OAUTH_RESOURCE_SCOPES = [
AuthorizationScope.QUOTA_PURCHASE,
AuthorizationScope.STORAGE_USAGE_READ,
] as const
export const AGENT_OAUTH_SCOPES = [...AGENT_OAUTH_STANDARD_SCOPES, ...AGENT_OAUTH_RESOURCE_SCOPES] as const
export const AGENT_OAUTH_SCOPE_DESCRIPTIONS: Record<(typeof AGENT_OAUTH_RESOURCE_SCOPES)[number], string> = {
export const OAUTH_SCOPES = [...OAUTH_STANDARD_SCOPES, ...OAUTH_RESOURCE_SCOPES] as const
export const OAUTH_SCOPE_DESCRIPTIONS: Record<(typeof OAUTH_RESOURCE_SCOPES)[number], string> = {
[AuthorizationScope.OBJECTS_READ]: 'List, inspect, and download objects',
[AuthorizationScope.OBJECTS_CREATE]: 'Create folders and upload objects',
[AuthorizationScope.OBJECTS_UPDATE]: 'Rename, move, and copy objects',
-64
View File
@@ -1,64 +0,0 @@
import { z } from 'zod'
import { AGENT_OAUTH_ACCESS_TOKEN_SECONDS, AGENT_OAUTH_REFRESH_TOKEN_SECONDS } from '../agent-oauth'
import { oauthResourceScopeSchema } from './oauth-resource'
export const agentOAuthGrantStatusSchema = z.enum(['active'])
export type AgentOAuthGrantStatus = z.infer<typeof agentOAuthGrantStatusSchema>
export const agentOAuthGrantSchema = z.object({
id: z.string(),
clientId: z.string(),
clientName: z.string(),
userId: z.string(),
orgId: z.string(),
workspaceName: z.string().nullable(),
scopes: z.array(oauthResourceScopeSchema),
createdAt: z.string(),
lastUsedAt: z.string().nullable(),
status: agentOAuthGrantStatusSchema,
})
export type AgentOAuthGrant = z.infer<typeof agentOAuthGrantSchema>
export const agentOAuthGrantListSchema = z.object({ items: z.array(agentOAuthGrantSchema) })
export type AgentOAuthGrantList = z.infer<typeof agentOAuthGrantListSchema>
export const agentOAuthConsentContextSchema = z.object({
clientId: z.string(),
clientName: z.string(),
instanceOrigin: z.string(),
workspace: z.object({
id: z.string(),
name: z.string().nullable(),
}),
scopes: z.array(oauthResourceScopeSchema),
standardScopes: z.array(z.string()),
redirectUri: z.string(),
grantLifetime: z.object({
accessTokenSeconds: z.number().int().default(AGENT_OAUTH_ACCESS_TOKEN_SECONDS),
refreshTokenSeconds: z.number().int().default(AGENT_OAUTH_REFRESH_TOKEN_SECONDS),
}),
})
export type AgentOAuthConsentContext = z.infer<typeof agentOAuthConsentContextSchema>
export const agentOAuthConsentContextRequestSchema = z.object({
oauthQuery: z.string().min(1),
})
export type AgentOAuthConsentContextRequest = z.infer<typeof agentOAuthConsentContextRequestSchema>
export const agentOAuthConsentSubmitSchema = z.object({
accept: z.boolean(),
oauthQuery: z.string().min(1),
})
export type AgentOAuthConsentSubmit = z.infer<typeof agentOAuthConsentSubmitSchema>
export const agentOAuthConsentResultSchema = z.object({
url: z.string(),
})
export type AgentOAuthConsentResult = z.infer<typeof agentOAuthConsentResultSchema>
export function agentOAuthGrantDTO(input: Omit<AgentOAuthGrant, 'status'>): AgentOAuthGrant {
return {
...input,
status: 'active',
}
}
+19 -19
View File
@@ -9,25 +9,6 @@ export {
adminAnalyticsTrafficSchema,
adminOverviewSchema,
} from './admin-analytics'
export type {
AgentOAuthConsentContext,
AgentOAuthConsentContextRequest,
AgentOAuthConsentResult,
AgentOAuthConsentSubmit,
AgentOAuthGrant,
AgentOAuthGrantList,
AgentOAuthGrantStatus,
} from './agent-oauth-grants'
export {
agentOAuthConsentContextRequestSchema,
agentOAuthConsentContextSchema,
agentOAuthConsentResultSchema,
agentOAuthConsentSubmitSchema,
agentOAuthGrantDTO,
agentOAuthGrantListSchema,
agentOAuthGrantSchema,
agentOAuthGrantStatusSchema,
} from './agent-oauth-grants'
export type {
AnnouncementInput,
AnnouncementStatus,
@@ -162,6 +143,25 @@ export {
} from './errors'
export type { ListNotificationsQuery } from './notification'
export { listNotificationsQuerySchema } from './notification'
export type {
OAuthConsentContext,
OAuthConsentContextRequest,
OAuthConsentResult,
OAuthConsentSubmit,
OAuthGrant,
OAuthGrantList,
OAuthGrantStatus,
} from './oauth-grants'
export {
oauthConsentContextRequestSchema,
oauthConsentContextSchema,
oauthConsentResultSchema,
oauthConsentSubmitSchema,
oauthGrantDTO,
oauthGrantListSchema,
oauthGrantSchema,
oauthGrantStatusSchema,
} from './oauth-grants'
export type { OAuthResourceScope } from './oauth-resource'
export { oauthResourceScopeLabels, oauthResourceScopeSchema } from './oauth-resource'
export type { CursorPage, CursorPageQuery, Page, PageQuery } from './pagination'
+64
View File
@@ -0,0 +1,64 @@
import { z } from 'zod'
import { OAUTH_ACCESS_TOKEN_SECONDS, OAUTH_REFRESH_TOKEN_SECONDS } from '../oauth'
import { oauthResourceScopeSchema } from './oauth-resource'
export const oauthGrantStatusSchema = z.enum(['active'])
export type OAuthGrantStatus = z.infer<typeof oauthGrantStatusSchema>
export const oauthGrantSchema = z.object({
id: z.string(),
clientId: z.string(),
clientName: z.string(),
userId: z.string(),
orgId: z.string(),
workspaceName: z.string().nullable(),
scopes: z.array(oauthResourceScopeSchema),
createdAt: z.string(),
lastUsedAt: z.string().nullable(),
status: oauthGrantStatusSchema,
})
export type OAuthGrant = z.infer<typeof oauthGrantSchema>
export const oauthGrantListSchema = z.object({ items: z.array(oauthGrantSchema) })
export type OAuthGrantList = z.infer<typeof oauthGrantListSchema>
export const oauthConsentContextSchema = z.object({
clientId: z.string(),
clientName: z.string(),
instanceOrigin: z.string(),
workspace: z.object({
id: z.string(),
name: z.string().nullable(),
}),
scopes: z.array(oauthResourceScopeSchema),
standardScopes: z.array(z.string()),
redirectUri: z.string(),
grantLifetime: z.object({
accessTokenSeconds: z.number().int().default(OAUTH_ACCESS_TOKEN_SECONDS),
refreshTokenSeconds: z.number().int().default(OAUTH_REFRESH_TOKEN_SECONDS),
}),
})
export type OAuthConsentContext = z.infer<typeof oauthConsentContextSchema>
export const oauthConsentContextRequestSchema = z.object({
oauthQuery: z.string().min(1),
})
export type OAuthConsentContextRequest = z.infer<typeof oauthConsentContextRequestSchema>
export const oauthConsentSubmitSchema = z.object({
accept: z.boolean(),
oauthQuery: z.string().min(1),
})
export type OAuthConsentSubmit = z.infer<typeof oauthConsentSubmitSchema>
export const oauthConsentResultSchema = z.object({
url: z.string(),
})
export type OAuthConsentResult = z.infer<typeof oauthConsentResultSchema>
export function oauthGrantDTO(input: Omit<OAuthGrant, 'status'>): OAuthGrant {
return {
...input,
status: 'active',
}
}
+12 -12
View File
@@ -1,19 +1,19 @@
import { z } from 'zod'
import { AGENT_OAUTH_RESOURCE_SCOPES } from '../agent-oauth'
import { AuthorizationScope } from '../authorization'
import { OAUTH_RESOURCE_SCOPES } from '../oauth'
export const oauthResourceScopeSchema = z.enum(AGENT_OAUTH_RESOURCE_SCOPES)
export const oauthResourceScopeSchema = z.enum(OAUTH_RESOURCE_SCOPES)
export type OAuthResourceScope = z.infer<typeof oauthResourceScopeSchema>
export const oauthResourceScopeLabels = {
[AuthorizationScope.OBJECTS_READ]: 'settings.agentAccess.scope.objectsRead',
[AuthorizationScope.OBJECTS_CREATE]: 'settings.agentAccess.scope.objectsCreate',
[AuthorizationScope.OBJECTS_UPDATE]: 'settings.agentAccess.scope.objectsUpdate',
[AuthorizationScope.OBJECTS_DELETE]: 'settings.agentAccess.scope.objectsDelete',
[AuthorizationScope.SHARES_READ]: 'settings.agentAccess.scope.sharesRead',
[AuthorizationScope.SHARES_CREATE]: 'settings.agentAccess.scope.sharesCreate',
[AuthorizationScope.SHARES_DELETE]: 'settings.agentAccess.scope.sharesDelete',
[AuthorizationScope.QUOTA_READ]: 'settings.agentAccess.scope.quotaRead',
[AuthorizationScope.QUOTA_PURCHASE]: 'settings.agentAccess.scope.quotaPurchase',
[AuthorizationScope.STORAGE_USAGE_READ]: 'settings.agentAccess.scope.storageUsageRead',
[AuthorizationScope.OBJECTS_READ]: 'settings.oauthApps.scope.objectsRead',
[AuthorizationScope.OBJECTS_CREATE]: 'settings.oauthApps.scope.objectsCreate',
[AuthorizationScope.OBJECTS_UPDATE]: 'settings.oauthApps.scope.objectsUpdate',
[AuthorizationScope.OBJECTS_DELETE]: 'settings.oauthApps.scope.objectsDelete',
[AuthorizationScope.SHARES_READ]: 'settings.oauthApps.scope.sharesRead',
[AuthorizationScope.SHARES_CREATE]: 'settings.oauthApps.scope.sharesCreate',
[AuthorizationScope.SHARES_DELETE]: 'settings.oauthApps.scope.sharesDelete',
[AuthorizationScope.QUOTA_READ]: 'settings.oauthApps.scope.quotaRead',
[AuthorizationScope.QUOTA_PURCHASE]: 'settings.oauthApps.scope.quotaPurchase',
[AuthorizationScope.STORAGE_USAGE_READ]: 'settings.oauthApps.scope.storageUsageRead',
} as const satisfies Record<OAuthResourceScope, string>
+1 -1
View File
@@ -625,7 +625,7 @@ export interface Announcement {
export type AuditActorType =
| 'user'
| 'api_key'
| 'agent_oauth'
| 'oauth'
| 'agent'
| 'anonymous'
| 'system'