From e96efe695f43fd4c9d17bc8b5d48ff050360ff5a Mon Sep 17 00:00:00 2001 From: Hieu Date: Wed, 4 Feb 2026 03:56:45 +0700 Subject: [PATCH] feat: show required permissions in mgmt api docs (#41151) ## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Show required FGA permissions for each route in the management API docs. Permissions are pulled from the `fga_permissions` security field in the OpenAPI spec. ## Summary by CodeRabbit * **New Features** * API reference docs now extract and display fine-grained access-control permissions per endpoint. When present, a dedicated block lists required permissions with a clear header explaining the token requirements. * The permissions block appears alongside existing OAuth scope information and only shows for endpoints that declare such permissions. --------- Co-authored-by: Chris Chinchilla --- .../docs/features/docs/Reference.api.utils.ts | 6 +++- .../docs/features/docs/Reference.sections.tsx | 32 ++++++++++++++----- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/apps/docs/features/docs/Reference.api.utils.ts b/apps/docs/features/docs/Reference.api.utils.ts index a8a16e5f3ce..7461d79a9ce 100644 --- a/apps/docs/features/docs/Reference.api.utils.ts +++ b/apps/docs/features/docs/Reference.api.utils.ts @@ -118,7 +118,7 @@ interface IApiFormUrlEncodedDTO { } } -type ISecurityOption = IBearerSecurity | IOAuth2Security +type ISecurityOption = IBearerSecurity | IOAuth2Security | IFgaSecurity interface IBearerSecurity { bearer: [] @@ -128,6 +128,10 @@ interface IOAuth2Security { oauth2: Array<'read' | 'write'> } +interface IFgaSecurity { + fga_permissions: string[] +} + export function getTypeDisplayFromSchema(schema: ISchema) { if ('allOf' in schema) { if (schema.allOf.length === 1) { diff --git a/apps/docs/features/docs/Reference.sections.tsx b/apps/docs/features/docs/Reference.sections.tsx index c4952e6f0d6..a3f17c83513 100644 --- a/apps/docs/features/docs/Reference.sections.tsx +++ b/apps/docs/features/docs/Reference.sections.tsx @@ -1,17 +1,20 @@ +import { isFeatureEnabled } from 'common' import { Fragment } from 'react' import ReactMarkdown from 'react-markdown' import { Badge, - cn, - Tabs_Shadcn_, TabsContent_Shadcn_, TabsList_Shadcn_, TabsTrigger_Shadcn_, + Tabs_Shadcn_, + cn, } from 'ui' -import { isFeatureEnabled } from 'common' +import { type IApiEndPoint } from './Reference.api.utils' +import { RefInternalLink } from './Reference.navigation.client' +import { ApiOperationBodySchemeSelector } from './Reference.ui.client' import ApiSchema from '~/components/ApiSchema' -import { clientSdkIds, REFERENCES } from '~/content/navigation.references' +import { REFERENCES, clientSdkIds } from '~/content/navigation.references' import { getApiEndpointById, getCliSpec, @@ -20,7 +23,7 @@ import { getSelfHostedApiEndpointById, getTypeSpec, } from '~/features/docs/Reference.generated.singleton' -import { getRefMarkdown, MDXRemoteRefs } from '~/features/docs/Reference.mdx' +import { MDXRemoteRefs, getRefMarkdown } from '~/features/docs/Reference.mdx' import type { MethodTypes } from '~/features/docs/Reference.typeSpec' import { formatMethodSignature } from '~/features/docs/Reference.typeSpec' import { @@ -35,9 +38,6 @@ import { import type { AbbrevApiReferenceSection } from '~/features/docs/Reference.utils' import { normalizeMarkdown } from '~/features/docs/Reference.utils' import { CodeBlock } from '~/features/ui/CodeBlock/CodeBlock' -import { type IApiEndPoint } from './Reference.api.utils' -import { RefInternalLink } from './Reference.navigation.client' -import { ApiOperationBodySchemeSelector } from './Reference.ui.client' type RefSectionsProps = { libraryId: string @@ -286,6 +286,8 @@ async function ApiEndpointSection({ link, section, servicePath }: ApiEndpointSec : await getApiEndpointById(section.id) if (!endpointDetails) return null + const endpointFgaPermissions = + endpointDetails.security?.find((sec) => 'fga_permissions' in sec)?.fga_permissions ?? [] const pathParameters = (endpointDetails.parameters ?? []).filter((param) => param.in === 'path') const queryParameters = (endpointDetails.parameters ?? []).filter((param) => param.in === 'query') const bodyParameters = @@ -358,6 +360,20 @@ async function ApiEndpointSection({ link, section, servicePath }: ApiEndpointSec )} + {endpointFgaPermissions.length > 0 && ( +
+

+ The fine-grained token must include the following permissions to access this endpoint: +

+
    + {endpointFgaPermissions.map((perm) => ( +
  • + {perm} +
  • + ))} +
+
+ )} {pathParameters.length > 0 && (

Path parameters