---
title: Account linking
---

# Account linking (Google / Facebook / X / LinkedIn)

Users can link their Little X Little account to one or more upstream identity providers. Once linked, signing in with that provider logs them straight into LXL — no password.

## Supported providers

| Provider | Scope of token | Stored as |
|---|---|---|
| Google | `openid email profile` | `account_links.provider = 'google'` |
| Facebook | `email,public_profile` | `account_links.provider = 'facebook'` |
| X (Twitter) | `users.read` | `account_links.provider = 'twitter'` |
| LinkedIn | `r_liteprofile r_emailaddress` | `account_links.provider = 'linkedin'` |

## User-facing flow

The login page (`/login`) renders provider buttons automatically:

```html
<a href="/auth/google">Continue with Google</a>
<a href="/auth/facebook">Continue with Facebook</a>
<a href="/auth/twitter">Continue with X</a>
<a href="/auth/linkedin">Continue with LinkedIn</a>
```

When the user returns from the provider:

1. We look up `account_links` by `(provider, provider_subject)`.
2. **Match found:** log in that LXL account immediately.
3. **No match, email matches an existing LXL account:** prompt for OTP-to-email, then link.
4. **No match, email is new:** create a fresh LXL account and link.

## Linking from inside an existing session

```html
<a href="/auth/google?action=link">Link Google to my account</a>
```

After the round-trip, the provider identity is attached to the currently-logged-in account.

## Unlinking

```html
<form method="post" action="/auth/google/unlink">
  <button>Unlink Google</button>
</form>
```

Unlinking immediately revokes any refresh tokens issued via that provider link.

## API access

Linked accounts surface in the `lxl.links` claim of the `id_token`:

```json
{
  "sub": "Bootim",
  "email": "you@example.org",
  "lxl.links": ["google", "linkedin"]
}
```

## Anti-takeover protection

When a Google login's email matches an existing LXL account that has **never** signed in via Google, we require an OTP-to-email confirmation before linking. This prevents an attacker who compromises a Google account from automatically gaining access to the LXL account that happens to share the email.

This protection is on by default and cannot be disabled.

## Webhook events

| Event | When |
|---|---|
| `account.linked` | A new provider was attached. |
| `account.unlinked` | A provider was detached. |

See [Webhook events](../reference/webhook-events.md) for the payload shape.
