> ## Documentation Index
> Fetch the complete documentation index at: https://replyke-feat-push-rich-payload-fields.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# OAuth

> Google, GitHub, Apple, and Facebook OAuth provider integration

Sublay supports OAuth 2.0 sign-in with Google, GitHub, Apple, and Facebook. The flow is redirect-based: the user is sent to the provider's authorization page and returns to a callback URL in your app with tokens embedded in the URL fragment.

<Note>
  OAuth providers must be configured in the Sublay dashboard before use. Each
  provider requires a client ID, client secret, and a list of allowed redirect
  URIs.
</Note>

## Web Integration

The `useOAuthSignIn` hook (from `@sublay/react-js`) handles the full web OAuth flow.

```tsx theme={null}
import { useOAuthSignIn } from "@sublay/react-js";
```

### Initiating Sign-In

Call `initiateOAuth` with the provider name and the URL your app will redirect back to after authentication.

```tsx theme={null}
import { useOAuthSignIn } from "@sublay/react-js";

function SignInWithGoogle() {
  const { initiateOAuth, isLoading, error } = useOAuthSignIn();

  const handleClick = async () => {
    // The user will be redirected to Google's authorization page.
    // isLoading stays true during the redirect.
    await initiateOAuth("google", "https://yourapp.com/auth/callback");
  };

  return (
    <button onClick={handleClick} disabled={isLoading}>
      {isLoading ? "Redirecting..." : "Sign in with Google"}
    </button>
  );
}
```

Supported provider values: `"google"`, `"github"`, `"apple"`, `"facebook"`.

### Handling the Callback

On the page your app redirects back to, call `handleOAuthCallback` once on mount. It reads the tokens from the URL fragment, stores them in the SDK, and cleans the URL.

```tsx theme={null}
import { useEffect } from "react";
import { useOAuthSignIn } from "@sublay/react-js";
import { useNavigate } from "react-router-dom";

function AuthCallbackPage() {
  const { handleOAuthCallback, error } = useOAuthSignIn();
  const navigate = useNavigate();

  useEffect(() => {
    const success = handleOAuthCallback();
    if (success) {
      navigate("/dashboard");
    }
  }, []);

  if (error) return <p>Authentication failed: {error}</p>;

  return <p>Authenticating...</p>;
}
```

`handleOAuthCallback` returns `true` if tokens were found in the URL fragment and `false` otherwise. If the provider returned an error (for example, the user denied access), the error is surfaced through the `error` field.

### Linking an Additional Provider

Authenticated users can link additional OAuth providers to their account using `linkOAuthProvider`. This requires the user to already be signed in.

```tsx theme={null}
import { useOAuthSignIn } from "@sublay/react-js";

function LinkGitHubButton() {
  const { linkOAuthProvider } = useOAuthSignIn();

  return (
    <button onClick={() => linkOAuthProvider("github", "https://yourapp.com/settings/callback")}>
      Connect GitHub
    </button>
  );
}
```

The callback page logic is identical for both sign-in and link flows — `handleOAuthCallback` handles both cases.

## Expo Integration

`@sublay/expo` ships its own `useOAuthSignIn` hook with the **same API** as the web hook (`initiateOAuth`, `linkOAuthProvider`, `handleOAuthCallback`, `isLoading`, `error`). Instead of a full-page redirect, it opens the Sublay-brokered consent screen in the system browser and returns to your app through a custom-scheme deep link.

<Note>
  Deep links don't work reliably in **Expo Go**. You need a [custom dev client](https://docs.expo.dev/develop/development-builds/introduction/) (`npx expo run:ios` / `npx expo run:android`) or an [EAS Build](https://docs.expo.dev/build/introduction/).
</Note>

### Setup

<Steps>
  <Step title="Install the peer dependencies">
    The Expo hook relies on `expo-web-browser` (to open the auth session) and `expo-linking` (to parse the return URL).

    ```bash theme={null}
    npx expo install expo-web-browser expo-linking
    ```
  </Step>

  <Step title="Register a URL scheme">
    Add a custom `scheme` to your `app.json` (or `app.config.js`). This is the scheme your `redirectAfterAuth` deep link uses.

    ```json app.json theme={null}
    {
      "expo": {
        "scheme": "myapp"
      }
    }
    ```
  </Step>

  <Step title="Allow the redirect URI in the dashboard">
    In your Sublay project's OAuth provider settings, add the exact deep link you'll pass as `redirectAfterAuth` (e.g. `myapp://auth/callback`) to the provider's **Allowed Redirect URIs**.
  </Step>
</Steps>

### Initiating Sign-In

`redirectAfterAuth` is **required** on mobile — there is no `window.location` to fall back to. Pass the deep link you registered above.

```tsx theme={null}
import { useOAuthSignIn } from "@sublay/expo";

function SignInWithGoogle() {
  const { initiateOAuth, isLoading, error } = useOAuthSignIn();

  const handlePress = () =>
    initiateOAuth({
      provider: "google",
      redirectAfterAuth: "myapp://auth/callback",
    });

  return (
    <Button
      title={isLoading ? "Signing in..." : "Sign in with Google"}
      onPress={handlePress}
      disabled={isLoading}
    />
  );
}
```

On a successful return, tokens are parsed from the deep link and stored automatically. There is **no separate callback page** — the result is resolved inline. `handleOAuthCallback` exists for API parity but is a no-op that always returns `false`, so you never need to call it on mobile.

If the user cancels or dismisses the browser, the flow resolves quietly: `isLoading` resets to `false` and `error` stays `null`. `error` is only set on a server failure, a provider `error` in the redirect, or when no tokens are returned.

### Linking an Additional Provider

`linkOAuthProvider` works exactly like the web flow and requires the user to already be signed in.

```tsx theme={null}
import { useOAuthSignIn } from "@sublay/expo";

function LinkGitHubButton() {
  const { linkOAuthProvider } = useOAuthSignIn();

  return (
    <Button
      title="Connect GitHub"
      onPress={() =>
        linkOAuthProvider({
          provider: "github",
          redirectAfterAuth: "myapp://settings",
        })
      }
    />
  );
}
```

### Persistence

Tokens dispatched by the hook are picked up by the `SublayProvider`'s account manager and written to `SecureStore`, so the session survives app relaunches automatically. You don't need to add any storage code.

### Web vs. Expo at a glance

|                     | Web (`@sublay/react-js`)                        | Expo (`@sublay/expo`)                             |
| ------------------- | ----------------------------------------------- | ------------------------------------------------- |
| Browser handoff     | Full-page redirect via `window.location`        | System browser via `expo-web-browser`             |
| `redirectAfterAuth` | Optional (defaults to current URL)              | **Required** (a custom-scheme deep link)          |
| Callback handling   | Call `handleOAuthCallback` on the callback page | Resolved inline; `handleOAuthCallback` is a no-op |
| Token persistence   | `localStorage`                                  | `SecureStore`                                     |

## How Tokens Are Returned

After the provider callback, Sublay redirects to your `redirectAfterAuth` URL with tokens in the **URL fragment** (not query parameters):

```
https://yourapp.com/auth/callback#accessToken=...&refreshToken=...
```

Fragments are not sent to servers and do not appear in server access logs, which prevents token leakage. `handleOAuthCallback` extracts them automatically.

## Managing Linked Identities

Use `useOAuthIdentities` to list and unlink OAuth identities on the current user's account.

```tsx theme={null}
import { useOAuthIdentities } from "@sublay/react-js";

function LinkedAccounts() {
  const { identities, fetchIdentities, unlinkIdentity, isLoading } = useOAuthIdentities();

  useEffect(() => {
    fetchIdentities();
  }, []);

  return (
    <ul>
      {identities.map((identity) => (
        <li key={identity.id}>
          {identity.provider} — {identity.email}
          <button onClick={() => unlinkIdentity(identity.id)}>Unlink</button>
        </li>
      ))}
    </ul>
  );
}
```

<Warning>
  Unlinking an identity is blocked if it is the last identity on the account and
  the user has no password set. This prevents the user from being locked out.
</Warning>

## See Also

* [`useOAuthIdentities` hook reference](/hooks/auth/use-oauth-identities)
* [OAuth Authorize API reference](/api-reference/oauth/authorize)
* [OAuth Link API reference](/api-reference/oauth/link)
* [List Identities API reference](/api-reference/oauth/list-identities)
* [Unlink Identity API reference](/api-reference/oauth/unlink-identity)
