Files
zpan/shared/oauth.ts
T
Jasper Van 2657f82ef1 feat(auth): add contextual OAuth workspace grants (#550)
* feat(auth): add contextual OAuth workspace grants

* fix(auth): register RFC 9396 detail types

* fix(openapi): restore delegated CLI auth profile

* fix(store): recover paid capacity fulfillment

* fix(auth): close OAuth contract gaps

* fix(store): resume verified x402 settlements

* chore(deps): update zpan cloud sdk to 2.5.2

* test(store): cover verified settlement recovery

* fix(auth): localize standard consent scopes

* fix(docker): include dependency patches before install

* fix(store): harden x402 purchase responses

* test(auth): cover OAuth authorization boundaries

* docs: add PR 550 verification screenshots

* chore: remove temporary verification screenshots

* docs(openapi): document exhausted capacity response
2026-08-02 13:33:15 -04:00

41 lines
2.4 KiB
TypeScript

import { AuthorizationScope, CANONICAL_AUTHORIZATION_SCOPES } from './authorization'
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 WORKSPACE_AUTHORIZATION_DETAIL_TYPE = 'https://zpan.space/authorization-details/workspace'
export const OAUTH_STANDARD_SCOPES = ['openid', 'profile', 'email', 'offline_access'] as const
export const OAUTH_RESOURCE_SCOPES = CANONICAL_AUTHORIZATION_SCOPES.filter(
(scope) => scope !== AuthorizationScope.OBJECTS_PURGE,
)
export const OAUTH_SCOPES = [...OAUTH_STANDARD_SCOPES, ...OAUTH_RESOURCE_SCOPES] as const
const EXPLICIT_SCOPE_DESCRIPTIONS: Partial<Record<AuthorizationScope, 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',
[AuthorizationScope.OBJECTS_DELETE]: 'Soft-delete objects',
[AuthorizationScope.SHARES_READ]: 'List and inspect shares',
[AuthorizationScope.SHARES_CREATE]: 'Create public shares',
[AuthorizationScope.SHARES_DELETE]: 'Revoke shares',
[AuthorizationScope.QUOTA_READ]: 'Inspect workspace quota',
[AuthorizationScope.QUOTA_PURCHASE]: 'Purchase workspace storage capacity',
[AuthorizationScope.STORAGE_USAGE_READ]: 'Inspect workspace storage usage',
}
export const OAUTH_SCOPE_DESCRIPTIONS: Record<(typeof OAUTH_RESOURCE_SCOPES)[number], string> = Object.fromEntries(
OAUTH_RESOURCE_SCOPES.map((scope) => [scope, describeScope(scope)]),
) as Record<(typeof OAUTH_RESOURCE_SCOPES)[number], string>
function describeScope(scope: string): string {
if (scope in EXPLICIT_SCOPE_DESCRIPTIONS) return EXPLICIT_SCOPE_DESCRIPTIONS[scope as AuthorizationScope]!
const [resource, action] = scope.split(':')
return `${capitalize(action)} ${resource.replaceAll('-', ' ')}`
}
function capitalize(value: string): string {
return value.length === 0 ? value : value[0].toUpperCase() + value.slice(1)
}