From 095daf8b30bad2684eb676f7898131d7bde1d16b Mon Sep 17 00:00:00 2001 From: saltbo Date: Fri, 7 Aug 2026 13:32:27 -0400 Subject: [PATCH] fix(openapi): align OAuth security metadata --- server/app.ts | 28 ++++++------------------ server/openapi.integration.test.ts | 34 ++++++------------------------ 2 files changed, 12 insertions(+), 50 deletions(-) diff --git a/server/app.ts b/server/app.ts index 8925151f..1a1e8d36 100644 --- a/server/app.ts +++ b/server/app.ts @@ -198,9 +198,10 @@ export function createApp(platform: Platform, auth: Auth, deps: Deps = createDep return c.json({ resource: `${origin}/api`, authorization_servers: [authorizationServer], - bearer_methods_supported: ['header'], + bearer_methods_supported: [], scopes_supported: OAUTH_RESOURCE_SCOPES, dpop_signing_alg_values_supported: ['ES256', 'EdDSA'], + dpop_bound_access_tokens_required: true, resource_name: 'ZPan API', }) }) @@ -284,26 +285,6 @@ export function createApp(platform: Platform, auth: Auth, deps: Deps = createDep oauthAuthorizationServer: '/.well-known/oauth-authorization-server/api/auth', oauthProtectedResource: '/.well-known/oauth-protected-resource/api', }, - 'x-cli-config': { - profiles: { - default: { - credentials: { - oauth2: { - auth: { - type: 'api-key', - params: { - in: 'header', - name: 'Authorization', - value: 'DPoP', - provider: 'realmroot-target', - scopes: OAUTH_RESOURCE_SCOPES.join(' '), - }, - }, - }, - }, - }, - }, - }, }) addRequestIdOpenApi(doc) @@ -471,7 +452,10 @@ function getCorsOrigins(platform: Platform): Set { } function agentScopeDescriptions(): Record { - return { ...OAUTH_SCOPE_DESCRIPTIONS } + return { + ...OAUTH_SCOPE_DESCRIPTIONS, + [AuthorizationScope.WORKSPACES_DISCOVER]: 'Discover workspaces available to the connected account', + } } export type AppType = ReturnType diff --git a/server/openapi.integration.test.ts b/server/openapi.integration.test.ts index 80f7bdef..67a16ad7 100644 --- a/server/openapi.integration.test.ts +++ b/server/openapi.integration.test.ts @@ -240,7 +240,7 @@ describe('global OpenAPI document', () => { expect(await headResponse.text()).toBe('') }) - it('publishes the external OAuth scope catalog with delegated CLI authentication', async () => { + it('publishes the external OAuth scope catalog without client-owned credential configuration', async () => { const { app } = await createTestApp({ DOWNLOAD_TOKEN_SECRET: 'test-download-token-secret' }) const res = await app.request('/api/openapi.json') const doc = (await res.json()) as { @@ -250,17 +250,7 @@ describe('global OpenAPI document', () => { { type?: string; scheme?: string; flows?: { authorizationCode?: { scopes?: Record } } } > } - 'x-cli-config'?: { - profiles?: Record< - string, - { - credentials?: Record< - string, - { auth?: { params?: Record }; params?: Record } - > - } - > - } + 'x-cli-config'?: unknown } expect(doc.components?.securitySchemes?.oauth2).toMatchObject({ @@ -271,6 +261,7 @@ describe('global OpenAPI document', () => { tokenUrl: '/api/auth/oauth2/token', refreshUrl: '/api/auth/oauth2/token', scopes: expect.objectContaining({ + [AuthorizationScope.WORKSPACES_DISCOVER]: 'Discover workspaces available to the connected account', [AuthorizationScope.OBJECTS_READ]: 'List, inspect, and download objects', [AuthorizationScope.OBJECTS_CREATE]: 'Create folders and upload objects', [AuthorizationScope.SHARES_CREATE]: 'Create public shares', @@ -280,22 +271,7 @@ describe('global OpenAPI document', () => { }, }) expect(doc.components?.securitySchemes?.agentApiKey).toBeUndefined() - expect(doc['x-cli-config']?.profiles?.default?.credentials?.oauth2).toEqual({ - auth: { - type: 'api-key', - params: { - in: 'header', - name: 'Authorization', - value: 'DPoP', - provider: 'realmroot-target', - scopes: expect.stringContaining(AuthorizationScope.OBJECTS_CREATE), - }, - }, - }) - expect(doc['x-cli-config']?.profiles?.default?.credentials?.oauth2.auth?.params?.scopes).toContain( - AuthorizationScope.OBJECTS_PURGE, - ) - expect(Object.keys(doc['x-cli-config']?.profiles ?? {})).toEqual(['default']) + expect(doc['x-cli-config']).toBeUndefined() }) it('publishes scopes through authorization-server metadata without a duplicate catalog endpoint', async () => { @@ -345,7 +321,9 @@ describe('global OpenAPI document', () => { expect(await protectedResource.json()).toMatchObject({ resource: 'http://localhost/api', authorization_servers: ['http://localhost:3000/api/auth'], + bearer_methods_supported: [], scopes_supported: expect.arrayContaining([AuthorizationScope.OBJECTS_READ]), + dpop_bound_access_tokens_required: true, }) const protectedHead = await app.request('/.well-known/oauth-protected-resource/api', { method: 'HEAD' })