> Let users log in with Google or Microsoft through your own OAuth client

# Social Login

Users log in with Google or Microsoft and see your consent screen, with your name and logo. You
create the OAuth client with the provider, register it in Pylo, and connect it to an app. Pylo runs
the OAuth flow and hands your app a session, so your app never touches a client secret.

Logging in never creates a user. Invite or provision everyone before they try to log in.

## 1. Create the OAuth client

In the admin panel, go to **Settings → Login Clients**, click **Add client**, and pick the provider.
Keep the dialog open: it shows the **Redirect URI** you register with the provider. That URI is
always Pylo's own callback, never your app's URL.

### Google

1. In the [Google Cloud Console](https://console.cloud.google.com/), set up the OAuth consent screen
   with your app's name and logo.
2. Under **APIs & Services → Credentials**, create an **OAuth client ID** of type **Web
   application**. Add Pylo's redirect URI under **Authorized redirect URIs** and leave **Authorized
   JavaScript origins** empty.
3. Copy the client ID and secret into the Pylo dialog.

### Microsoft

1. In the [Azure portal](https://portal.azure.com/), go to **App registrations → New registration**.
   Pick the supported account types that match **Who can log in** in the Pylo dialog: **this
   organizational directory only** for "Only accounts in my organization", or **any organizational
   directory and personal Microsoft accounts** for "Any Microsoft account".
2. Add Pylo's redirect URI on the **Web** platform. Under **Mobile and desktop applications** it
   makes the client public, and every login fails.
3. Under **Certificates & secrets**, create a client secret and copy its **Value**, not the Secret
   ID.
4. In the Pylo dialog, enter the **Application (client) ID**, the **Directory (tenant) ID** if you
   picked "Only accounts in my organization", the secret, and its expiry date.

## 2. Connect it to your app

Go to **Settings → Apps**, open the app, and switch to the **Login** tab. For each provider, pick
your client and list every **Return URL** your app receives logins on, one per line, exactly as your
app sends it. Save.

## 3. Add the buttons

### Next.js

`@pylo/auth-nextjs` ships both route handlers. The return URL is your site's origin plus
`/api/auth/callback`, so register `https://app.example.com/api/auth/callback` and, for local
development, `http://localhost:3000/api/auth/callback`.

```typescript filename="app/api/auth/[provider]/start/route.ts"
import { createSocialStartRoute } from "@pylo/auth-nextjs";

export const GET = createSocialStartRoute();
```

```typescript filename="app/api/auth/callback/route.ts"
import { createSocialCallbackRoute } from "@pylo/auth-nextjs";

export const GET = createSocialCallbackRoute();
```

```tsx filename="app/auth/login/page.tsx"
<a href="/api/auth/google/start?redirect=/dashboard">Continue with Google</a>
<a href="/api/auth/microsoft/start?redirect=/dashboard">Continue with Microsoft</a>
```

Use a plain `<a>`, not `<Link>`: the route redirects to the provider. After login the callback sets
the auth cookies and redirects to `redirect`, or `/`. A failed login lands on
`/auth/login?error=...`, so show `searchParams.error` there.

### React

Pass your app's ID to `PyloProvider` as `appId`, for example `<PyloProvider appId="your-app-id">`.
Then call `loginWith` with a registered return URL. It redirects the page:

```tsx filename="google-button.tsx"
import { usePyloAuth } from "@pylo/react";

export function GoogleButton() {
  const { loginWith } = usePyloAuth();

  return (
    <button onClick={() => loginWith("google", { returnUrl: "https://app.example.com/auth" })}>
      Continue with Google
    </button>
  );
}
```

On the return URL's page, `completeLogin` redeems the code and starts the session. It never throws:

```tsx filename="auth-callback.tsx"
import { useEffect } from "react";
import { usePyloAuth } from "@pylo/react";

export function AuthCallback() {
  const { completeLogin } = usePyloAuth();

  useEffect(() => {
    completeLogin().then((result) => {
      if (result.success) window.location.replace(result.redirect ?? "/");
      else showLoginError(result.error?.message);
    });
  }, []);

  return null;
}
```

### Expo

Set `appId` on `PyloProvider` as in React, install the browser and crypto modules, and register your
scheme's URL, for example `myapp://auth`. `loginWith` opens a system browser tab and resolves with
the result:

```bash
# expo
npx expo install expo-web-browser expo-crypto expo-linking
```

```tsx filename="google-button.tsx"
import { Button } from "react-native";
import * as Linking from "expo-linking";
import { usePyloAuth } from "@pylo/expo";

export function GoogleButton() {
  const { loginWith } = usePyloAuth();

  async function onPress() {
    const result = await loginWith("google", { returnUrl: Linking.createURL("auth") });
    if (!result.success && result.error?.code !== "CANCELLED") {
      showLoginError(result.error?.message);
    }
  }

  return <Button title="Continue with Google" onPress={onPress} />;
}
```

With `appId` in `pylo.config.ts`, `pylo generate` types `loginWith` to the providers and return URLs
the app allows, so a typo fails type-checking. Without an SDK, redirect to `/auth/<provider>/start`
with `app_id`, `return_url` and a PKCE `handoff_challenge`, then redeem the returned `code` with the
`redeemLoginHandoff` mutation.

## Troubleshooting

| You see                                     | Check                                                                                    |
| ------------------------------------------- | ---------------------------------------------------------------------------------------- |
| "This login page is not set up for … login" | The return URL is registered on the app exactly as your app sends it.                    |
| "Login failed. Please try again."           | A user with that email exists in the app. For Microsoft, the redirect URI is on **Web**. |
| The provider rejects the redirect URI       | You registered Pylo's redirect URI from the **Add client** dialog, not your app's URL.   |

Microsoft identifies users by their login name (UPN). To match on their email address instead, add
the optional `xms_edov` claim to the ID token under **Token configuration**.
