---
title: Silent sign-in
---

# Silent sign-in

Silent sign-in fires your callback with a fresh `id_token` for users who **previously consented** to your client — without showing any UI at all. Use it to keep users logged in across page loads or to refresh stale tokens in the background.

## How it works

1. SDK opens a hidden iframe at `https://id.littlexlittle.org/oidc/iframe/check_session?client_id=...`.
2. Iframe `postMessage`s back: `logged_in`, `logged_out`, or `unchanged`.
3. If `logged_in`, SDK opens an invisible popup to `/oidc/authorize?prompt=none&response_mode=web_message`.
4. Tokens are returned via `postMessage` to your origin.
5. Your callback runs — total time typically < 500ms, no visual change.

```mermaid
sequenceDiagram
  participant U as User browser
  participant N as NGO site
  participant L as id.littlexlittle.org
  N->>L: hidden iframe /check_session
  L-->>N: postMessage {status:'logged_in'}
  N->>L: invisible /authorize?prompt=none
  L-->>N: postMessage {credential:'eyJ...'}
  N->>N: callback(credential)
```

## Enable it

```js
lxl.accounts.id.initialize({
  client_id: 'YOUR_CLIENT_ID',
  callback: onSignIn,
  auto_select: true,         // required for fully silent flow
  itp_support: true,         // Safari ITP work-arounds
});
```

## When silent fails

The IdP returns `error=login_required` (per OIDC spec) when:

- The user has no session at `id.littlexlittle.org`.
- The user has not consented to your client before.
- The user opted out of auto-select.
- The session has expired (default 30 days).

Your callback receives `{ credential: undefined, error: 'login_required' }` — fall back to the visible button or One-Tap prompt.

## Server-side equivalent (PHP)

```php
$url = $client->createAuthUrl([
    'prompt'   => 'none',
    'id_token_hint' => $previousIdToken,
]);
header('Location: ' . $url);
```

If the user is no longer logged in, they're redirected back with `?error=login_required` instead of the consent screen.

## Best practices

- **Don't poll.** Run silent sign-in once on page load and once when the user returns to the tab (`visibilitychange`).
- **Cache the JWT.** Keep `id_token` in memory and only re-attempt silent flow when it's about to expire.
- **Combine with refresh tokens.** Silent sign-in is for *re-establishing* the session; refresh tokens are for *extending* it. Use both.
