Home / Function/ NewScopedTokenSheet() — supabase Function Reference

NewScopedTokenSheet() — supabase Function Reference

Architecture documentation for the NewScopedTokenSheet() function in NewScopedTokenSheet.tsx from the supabase codebase.

Entity Profile

Dependency Diagram

graph TD
  871e24a9_af4f_d982_ab63_afb529f14e75["NewScopedTokenSheet()"]
  93731708_c41f_3a77_3f8a_c076523beb9a["useOrgAndProjectData()"]
  871e24a9_af4f_d982_ab63_afb529f14e75 -->|calls| 93731708_c41f_3a77_3f8a_c076523beb9a
  d0619346_3801_c66f_f84d_dfd227ead1e1["getExpirationDate()"]
  871e24a9_af4f_d982_ab63_afb529f14e75 -->|calls| d0619346_3801_c66f_f84d_dfd227ead1e1
  57941846_d7e6_6a25_9582_2aaaab8b9f4d["mapPermissionToFGA()"]
  871e24a9_af4f_d982_ab63_afb529f14e75 -->|calls| 57941846_d7e6_6a25_9582_2aaaab8b9f4d
  style 871e24a9_af4f_d982_ab63_afb529f14e75 fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

apps/studio/components/interfaces/Account/AccessTokens/Scoped/NewScopedTokenSheet.tsx lines 46–320

export const NewScopedTokenSheet = ({
  visible,
  onOpenChange,
  tokenScope,
  onCreateToken,
}: NewScopedTokenSheetProps) => {
  const [resourceSearchOpen, setResourceSearchOpen] = useState(false)
  const { organizations, projects } = useOrgAndProjectData()

  const form = useForm<TokenFormValues>({
    resolver: zodResolver(TokenSchema),
    defaultValues: {
      tokenName: '',
      expiresAt: EXPIRES_AT_OPTIONS['month'].value,
      customExpiryDate: undefined,
      resourceAccess: 'all-orgs',
      selectedOrganizations: [],
      selectedProjects: [],
      permissionRows: [],
    },
    mode: 'onChange',
  })
  const { mutate: createAccessToken, isPending } = useAccessTokenCreateMutation()

  const resourceAccess = form.watch('resourceAccess')
  const expiresAt = form.watch('expiresAt')
  const permissionRows = form.watch('permissionRows') || []

  const onSubmit: SubmitHandler<TokenFormValues> = async (values) => {
    if (!permissionRows || permissionRows.length === 0) {
      toast.error('Please configure at least one permission.')
      return
    }

    const hasValidPermissions = permissionRows.every(
      (row) => row.resource && row.actions && row.actions.length > 0
    )
    if (!hasValidPermissions) {
      toast.error('Please ensure all permissions have both resource and action selected.')
      return
    }

    if (values.resourceAccess === 'selected-orgs') {
      const selectedOrgs = values.selectedOrganizations || []

      if (selectedOrgs.length === 0) {
        toast.error('Please select at least one organization.')
        return
      }

      const availableOrgSlugs = organizations.map((org) => org.slug)
      const invalidOrgs = selectedOrgs.filter((slug) => !availableOrgSlugs.includes(slug))

      if (invalidOrgs.length > 0) {
        toast.error(
          `You don't have access to the following organization(s): ${invalidOrgs.join(', ')}`
        )
        return
      }
    }

    if (values.resourceAccess === 'selected-projects') {
      const selectedProjects = values.selectedProjects || []

      if (selectedProjects.length === 0) {
        toast.error('Please select at least one project.')
        return
      }

      const availableProjectRefs = projects.map((project) => project.ref)
      const invalidProjects = selectedProjects.filter((ref) => !availableProjectRefs.includes(ref))

      if (invalidProjects.length > 0) {
        toast.error(
          `You don't have access to the following project(s): ${invalidProjects.join(', ')}`
        )
        return
      }
    }

    const finalExpiresAt =
      values.expiresAt === CUSTOM_EXPIRY_VALUE
        ? values.customExpiryDate
        : getExpirationDate(values.expiresAt || '')

    const permissions = permissionRows
      .flatMap((row) => {
        const { resource, actions } = row
        return actions.flatMap((action) => mapPermissionToFGA(resource, action))
      })
      .filter(Boolean) as ScopedAccessTokenPermission[]

    if (!permissions || permissions.length === 0) {
      toast.error('Please configure at least one valid permission.')
      return
    }

    const finalPayload: ScopedAccessTokenCreateVariables = {
      name: values.tokenName,
      permissions,
    }

    if (finalExpiresAt) {
      finalPayload.expires_at = finalExpiresAt
    }

    if (
      values.resourceAccess === 'selected-orgs' &&
      values.selectedOrganizations &&
      values.selectedOrganizations.length > 0
    ) {
      finalPayload.organization_slugs = values.selectedOrganizations
    } else if (
      values.resourceAccess === 'selected-projects' &&
      values.selectedProjects &&
      values.selectedProjects.length > 0
    ) {
      finalPayload.project_refs = values.selectedProjects
    }

    if (!finalPayload.name || finalPayload.name.trim() === '') {
      toast.error('Please enter a token name.')
      return
    }

    if (!finalPayload.permissions || finalPayload.permissions.length === 0) {
      toast.error('Please configure at least one permission.')
      return
    }

    createAccessToken(finalPayload, {
      onSuccess: (data) => {
        toast.success('Access token created successfully')
        onCreateToken(data)
        handleClose()
      },
      onError: (error) => {
        if (error.message && error.message.includes("don't have access")) {
          toast.error(
            `Access Error: ${error.message}. Please verify you have access to the selected resources.`
          )
        } else {
          toast.error(`Failed to create access token: ${error.message}`)
        }
      },
    })
  }

  const handleClose = () => {
    form.reset({
      tokenName: '',
      expiresAt: EXPIRES_AT_OPTIONS['month'].value,
      customExpiryDate: undefined,
      resourceAccess: 'all-orgs',
      selectedOrganizations: [],
      selectedProjects: [],
      permissionRows: [],
    })
    onOpenChange(false)
  }

  const handleCustomDateChange = useCallback(
    (date: { date: string } | undefined) => {
      form.setValue('customExpiryDate', date?.date, { shouldValidate: true })
    },
    [form]
  )

  const handleCustomExpiryChange = useCallback(
    (isCustom: boolean) => {
      if (isCustom && !form.getValues('customExpiryDate')) {
        form.setValue('customExpiryDate', dayjs().endOf('day').toISOString(), {
          shouldValidate: true,
        })
      }
      if (!isCustom) {
        form.setValue('customExpiryDate', undefined, { shouldValidate: true })
      }
    },
    [form]
  )

  return (
    <Sheet
      open={visible}
      onOpenChange={(open) => {
        if (!open) {
          handleClose()
        } else {
          onOpenChange(open)
        }
      }}
    >
      <SheetContent
        showClose={false}
        size="default"
        className="!min-w-[600px] flex flex-col h-full gap-0"
      >
        <SheetHeader>
          <SheetTitle>
            {tokenScope === 'V0' ? 'Generate token for experimental API' : 'Generate New Token'}
          </SheetTitle>
          <SheetDescription className="sr-only">
            A form to generate a new scoped access token.
          </SheetDescription>
        </SheetHeader>
        <ScrollArea className="flex-1 max-h-[calc(100vh-116px)]">
          <div className="flex flex-col overflow-visible">
            {tokenScope === 'V0' && (
              <div className="px-4 sm:px-5 py-4 pb-4">
                <Admonition
                  type="warning"
                  title="The experimental API provides additional endpoints which allows you to manage your organizations and projects."
                  description={
                    <>
                      <p>
                        These include deleting organizations and projects which cannot be undone. As
                        such, be very careful when using this API.
                      </p>
                      <div className="mt-4">
                        <Button asChild type="default" icon={<ExternalLink />}>
                          <Link
                            href="https://api.supabase.com/api/v0"
                            target="_blank"
                            rel="noreferrer"
                          >
                            Experimental API documentation
                          </Link>
                        </Button>
                      </div>
                    </>
                  }
                />
              </div>
            )}

            <Form_Shadcn_ {...form}>
              <div className="flex flex-col gap-0 overflow-visible">
                <BasicInfo
                  control={form.control}
                  expirationDate={expiresAt || ''}
                  onCustomDateChange={handleCustomDateChange}
                  onCustomExpiryChange={handleCustomExpiryChange}
                />
                <Separator />
                <ResourceAccess
                  control={form.control}
                  resourceAccess={resourceAccess}
                  setValue={form.setValue}
                />
                <Separator />
                <Permissions
                  setValue={form.setValue}
                  watch={form.watch}
                  resourceSearchOpen={resourceSearchOpen}
                  setResourceSearchOpen={setResourceSearchOpen}
                />
              </div>
            </Form_Shadcn_>
          </div>
        </ScrollArea>
        <SheetFooter className="!justify-end w-full mt-auto py-4 border-t">
          <div className="flex gap-2">
            <Button type="default" disabled={isPending} onClick={handleClose}>
              Cancel
            </Button>
            <Button onClick={form.handleSubmit(onSubmit)} loading={isPending}>
              Generate token
            </Button>
          </div>
        </SheetFooter>
      </SheetContent>
    </Sheet>
  )
}

Subdomains

Frequently Asked Questions

What does NewScopedTokenSheet() do?
NewScopedTokenSheet() is a function in the supabase codebase.
What does NewScopedTokenSheet() call?
NewScopedTokenSheet() calls 3 function(s): getExpirationDate, mapPermissionToFGA, useOrgAndProjectData.

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free