Skip to main content
A desktop app has no web server for a provider to redirect to, and Google and GitHub both reject custom URI schemes as redirect targets. The plugin uses the provider-sanctioned native pattern instead: open the system browser, listen on a loopback port, exchange the code with PKCE.

The round-trip

The listener is one-shot and state-checked: it accepts a single callback matching the state parameter it generated, then shuts down.

Redirect allow-list

The plugin binds the first free port from oauth.callbackPorts and asks GoTrue to redirect to http://127.0.0.1:<port>/callback. Supabase matches additional_redirect_urls exactly, so every port you might bind has to be listed.
supabase/config.toml
A missing redirect URL fails after the consent screen, inside GoTrue. It does not come back as a structured AuthError, so it surfaces as an unstructured failure — check this list first when a round-trip dies at the redirect.
The provider’s own callback URL is different: it points at GoTrue, not at the plugin. See Provider setup.

In the browser

The web bindings hand the whole redirect to GoTrue instead of a loopback listener, so the landing page is yours to choose: pass redirectTo to signInWithOAuth / linkIdentity (e.g. /auth/callback?next=… in a multi-environment SPA where localhost dev and the production domain need different callbacks). Omitted, GoTrue returns the browser to the project’s Site URL; provided, the URL must appear in the same Redirect URLs allow-list above or GoTrue falls back to the Site URL. On Tauri the option is accepted for signature parity and ignored — the loopback redirect is the plugin’s.

Cancelling

While a round-trip is in flight, the plugin holds the loopback listener until the browser returns or oauth.flowTimeoutSecs elapses. If the user backs out in your UI, cancel explicitly so the listener closes immediately:
The pending signInWithOAuth() call rejects with oauthFlowInterrupted. useIdentities() exposes cancelLink() for the linking side; wire a Cancel action to it (and to cancelOAuthFlow() for sign-in) so a user is never stuck waiting on a browser tab they closed.

Linking versus signing in

signInWithOAuth() establishes a new session. linkIdentity() runs the same round-trip authenticated as the current user, so the provider identity is attached to the existing account rather than creating a second one. If the identity already belongs to another account, the call rejects with identityAlreadyLinked and the current account is left untouched. Linking needs enable_manual_linking = true on the project and the allow-link-identity permission.