Files
supabase/apps/studio/components/interfaces/ApiAuthorization/ApiAuthorization.Loading.tsx
Danny White 35df570342 feat(studio): move /authorize to connect interstitial (#46359)
> [!CAUTION]
> The `do-not-merge` label has been applied because this contains mocks
for easier review and testing. I'll remove those mocks before merging.

## What kind of change does this PR introduce?

Feature. Part of the shared Connect UI (interstitial) rollout. Previous
slices: #46058, #45909, #45862.

## What is the current behavior?

The `/authorize` MCP/OAuth consent screen uses the old `Card`/`Alert`
layout.

## What is the new behavior?

- Wraps all `/authorize` states in `InterstitialLayout` (the shared
full-screen centered card used across Connect flows)
- Shows a quiet footnote below the Cancel button ("Authorizing will
redirect you to \<url\>") for non-localhost redirect URIs, so users can
verify the destination before approving. No extra friction for localhost
flows (local MCP servers)

| Before | After |
| --- | --- |
| <img width="692" height="997" alt="Authorize API access
Supabase-F6C3747A-5077-43D8-A509-3E16B1DDC168"
src="https://github.com/user-attachments/assets/e86dde34-94cb-48ef-b026-66aac9122df6"
/> | <img width="692" height="997" alt="Authorize API Access
Supabase-FE6FD8B3-1159-4EA5-94D7-EA5CEA7A25F3"
src="https://github.com/user-attachments/assets/c1a94a44-51d9-40d8-8046-f3104a27b929"
/> |
| <img width="692" height="997" alt="Authorize API access
Supabase-86742351-3521-4B62-AF87-403CB7E7F4F5"
src="https://github.com/user-attachments/assets/41cff7af-b9e4-4a20-a979-7148b4220265"
/> | <img width="692" height="997" alt="Authorize Cursor
Supabase-B665B4A4-600F-462B-8C97-84B171EC3103"
src="https://github.com/user-attachments/assets/804286f2-ce51-45ab-bb3f-315f8ac62445"
/> |
| <img width="692" height="997" alt="Authorize API access
Supabase-C73DC3D0-8646-4E6E-A259-3E84AE46DAF2"
src="https://github.com/user-attachments/assets/8f285edb-438f-4262-9faa-f1133c679ed4"
/> | <img width="692" height="997" alt="Authorize Cursor
Supabase-FEA86625-27D5-4DB5-B4D4-1A2CB804E56E"
src="https://github.com/user-attachments/assets/b54f2ceb-e1cf-4c7e-be3f-8e1b0942e9a4"
/> |
| <img width="692" height="997" alt="Authorize API access
Supabase-48E0C7CB-DDDD-4305-B821-F3BEB52C4A4E"
src="https://github.com/user-attachments/assets/7d123c57-e05d-408c-8df9-d747a3afd714"
/> | <img width="692" height="997" alt="Authorize Cursor
Supabase-CE8F9905-FAE0-4C06-B77A-9F269B2100FE"
src="https://github.com/user-attachments/assets/9f403b83-5de3-43c8-a592-c3022e041243"
/> |
| <img width="692" height="997" alt="Authorize API access
Supabase-E37D2CD5-476F-4F49-A5FB-631B265025DC"
src="https://github.com/user-attachments/assets/3d235315-d7c0-4279-b23f-e8b595888511"
/> | <img width="692" height="997" alt="Authorize Cursor
Supabase-DF078AEB-BB78-4647-9FA2-5D5403CCA5D6"
src="https://github.com/user-attachments/assets/53d51718-8707-4b97-9cbe-8e523f4ce0e0"
/> |
| <img width="692" height="997" alt="Authorize API access
Supabase-D6F6817F-D8DD-4D55-85BB-A15100814AAB"
src="https://github.com/user-attachments/assets/c80c5579-772a-4dfe-a247-b0b9772b9690"
/> | <img width="692" height="997" alt="Authorize Cursor
Supabase-E457B580-9786-43AD-9CF9-FE4F5BB8E785"
src="https://github.com/user-attachments/assets/30c47b05-edf5-4380-a2f1-aedb99482540"
/> |
| <img width="692" height="997" alt="Authorize API access
Supabase-4F3D6AA4-E2E3-4526-B391-49B6E0861911"
src="https://github.com/user-attachments/assets/ffbe5b65-6eef-49d7-95f1-c29072c320b8"
/> | <img width="692" height="997" alt="Authorize Cursor
Supabase-CA9FFCC9-4CA2-4718-AD49-B02D86C6EF6A"
src="https://github.com/user-attachments/assets/8fd7ff39-19f5-4414-af13-3821290735b2"
/> |
| <img width="692" height="997" alt="Authorize API access
Supabase-E507B7A5-9AD0-4F17-8743-63A7B47D171A"
src="https://github.com/user-attachments/assets/1639b5cc-69c4-4a43-b049-6f989e2cdbb1"
/> | <img width="692" height="997" alt="Authorize Cursor
Supabase-9844BB27-2429-4BA6-BD36-1AB54099F44F"
src="https://github.com/user-attachments/assets/a94b88e2-9c2f-4941-840a-5182342bb335"
/> |
| <img width="692" height="997" alt="Authorize API access
Supabase-27684173-9DBB-4F6E-9F7F-87EFD4E10A5F"
src="https://github.com/user-attachments/assets/91794c96-8a81-4d83-9c97-01d134639676"
/> | <img width="692" height="997" alt="Authorize Cursor
Supabase-04E31F7B-D098-4814-A394-01CE3D3E5A51"
src="https://github.com/user-attachments/assets/ba0284a3-363c-4aa5-9e4a-c378aed9c42c"
/> |
| <img width="692" height="997" alt="Authorize API access
Supabase-207CBC69-4957-499C-92E8-163F2B34C8AD"
src="https://github.com/user-attachments/assets/1bafedd2-bba8-473c-ba57-637289f1c940"
/> | <img width="692" height="997" alt="Authorize API Access
Supabase-C1627071-4AE2-4012-8F7C-4E6D883618A3"
src="https://github.com/user-attachments/assets/a6fc6125-3c1e-4b8c-821a-c3c9f32f3cc0"
/> |

## To test

A mock toolbar is included for easy local testing. Navigate to
`/authorize?mock=loading` and then switch between the following
variants:

| State | What to check |
| --- | --- |
| `loading` | Shimmer skeleton inside the card |
| `ready` | Regular waiting state |
| `approving` | Authorize button shows spinner, both buttons disabled |
| `approved` | Success admonition: "Authorization approved" |
| `expired` | Warning admonition: "Authorization request expired", no
action buttons |
| `organizations-loading` | Org selector shimmer, no action buttons |
| `organizations-error` | "Unable to load organizations" admonition, no
action buttons |
| `empty` | "No organizations found" admonition, no action buttons |
| `not-member` | "Organization unavailable" admonition, no action
buttons |
| `error` | "Unable to load authorization" error screen |

Then please test the `organization_slug` prefill:
`/authorize?mock=ready&organization_slug=<your-org-name-here>`. That org
selector should be pre-selected and locked.

To test against a real OAuth app, use a registered app on
`supabase.green` — the mock states cover all edge cases but a live
round-trip confirms the approve/decline API calls.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **New Features**
* Added mock preview functionality for testing API authorization and
Connect flows
* Introduced collapsible, grouped permissions view for OAuth
authorization requests

* **Refactor**
* Redesigned API authorization screens with improved layout and
messaging
  * Restructured permissions display for better organization and clarity

* **Bug Fixes**
  * Fixed inline link underline decoration color

* **Tests**
  * Updated authorization flow test assertions to match new UI behavior

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46359?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Ali Waseem <waseema393@gmail.com>
2026-06-08 10:51:04 -06:00

41 lines
1.6 KiB
TypeScript

import type { ReactNode } from 'react'
import { Card, CardContent } from 'ui'
import { ShimmeringLoader } from 'ui-patterns'
import { InterstitialLayout, SupabaseLogo } from '@/components/layouts/InterstitialLayout'
export function ApiAuthorizationLoadingScreen(): ReactNode {
return (
<InterstitialLayout
logo={<SupabaseLogo />}
title={<ShimmeringLoader className="mx-auto h-7 w-28 max-w-full py-0" />}
description={<ShimmeringLoader className="mx-auto h-4 w-56 max-w-full py-0" />}
>
<div className="px-6 pb-6">
<span className="sr-only">Loading...</span>
<div className="flex flex-col gap-5">
<Card className="shadow-none">
<CardContent className="flex items-center gap-3 border-none px-4 py-3">
<ShimmeringLoader className="size-10 flex-shrink-0 rounded-lg py-0" />
<div className="flex min-w-0 flex-1 flex-col gap-2">
<ShimmeringLoader className="h-4 w-28 py-0" />
<ShimmeringLoader className="h-3 w-20 py-0" />
</div>
</CardContent>
</Card>
<Card className="shadow-none">
<CardContent className="space-y-3 border-none px-4 py-4">
<ShimmeringLoader className="h-4 w-full py-0" />
<ShimmeringLoader className="h-4 w-4/5 py-0" />
</CardContent>
</Card>
<div className="flex flex-col gap-2">
<ShimmeringLoader className="h-10 w-full py-0" />
<ShimmeringLoader className="h-10 w-full py-0" />
</div>
</div>
</div>
</InterstitialLayout>
)
}