> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bbrands.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Social Login & Internal SSO

> The end-user social login and the cross-app session handshake — two systems distinct from the OAuth IdP for external clients.

<Warning>
  This page covers **two systems that are different from the OAuth IdP** for
  external clients. Do not confuse them:

  | System               | Purpose                                                   | Key token                           | Entry point                           |
  | -------------------- | --------------------------------------------------------- | ----------------------------------- | ------------------------------------- |
  | **OAuth IdP (OIDC)** | External OAuth clients                                    | code → ES256 → internal exchange    | `/api/v3/oauth/*`                     |
  | **Social login**     | B Brands end users sign in with a social provider         | internal HS256 JWT                  | `/api/v3/auth/login/oauth/<provider>` |
  | **Internal SSO**     | Carry a session across B Brands apps on different domains | short-lived signed token in the URL | `/api/v3/auth/sso/exchange`           |
</Warning>

## 1. Social login

End users sign into B Brands apps with a social provider. This uses the managed
engine's social sign-in directly, then mints the internal B Brands JWT — it does
**not** use the OAuth IdP, ES256 tokens, or the consent screen.

### Routes

| Step                      | Method | Path                                                                 |
| ------------------------- | ------ | -------------------------------------------------------------------- |
| 1. Start                  | GET    | `/api/v3/auth/login/oauth/<provider>?redirect_url=<url>&format=json` |
| 2. Callback (HTML bridge) | GET    | `/api/v3/auth/login/oauth/<provider>/callback`                       |
| 3. Processing             | GET    | `/api/v3/auth/login/oauth/<provider>/callback-redirect`              |

```mermaid theme={null}
sequenceDiagram
    actor U as User
    participant A as App (frontend)
    participant G as api.bbrands.io
    participant S as OIDC engine
    participant GG as Social provider

    U->>A: Click "Continue with provider"
    A->>G: GET /api/v3/auth/login/oauth/<provider>?redirect_url=…
    G->>S: signInWithOAuth(provider, redirectTo=callback)
    alt Web
        G->>U: 302 to provider OAuth URL
    else Mobile / SPA (Accept: application/json or format=json)
        G->>U: 200 { data: { redirectUrl } }
    end
    GG->>S: User approves
    S->>G: /callback#access_token&refresh_token&expires_in
    G->>G: HTML page reads fragment → /callback-redirect?access_token=…
    G->>S: setSession(access_token, refresh_token)
    G->>G: ensure registration (deferred), login
    alt Deep link (redirect_url without http/https)
        G->>U: 302 with tokens in query
    else Web / API
        G->>U: 200 login payload + is_new_user + redirect_url
    end
```

* The start route requires `redirect_url`; `format=json` (or `Accept: application/json`) returns JSON instead of a 302.
* The `/callback` page is an HTML bridge that reads the URL fragment (`#access_token…`) or `?code=` and forwards it to `/callback-redirect`.
* `/callback-redirect` sets the engine session, auto-registers a profile if needed, and returns the **internal B Brands JWT** (HS256) plus account data.

<Note>
  Legacy routes exist for additional providers under an earlier API version. The
  current flow adds a parametrizable `redirect_url` and JSON/deep-link support.
</Note>

## 2. Internal SSO — the cross-app handshake

When a user logs in via the authentication app and must land on a sibling
B Brands app on a **different domain**, the session is carried with a short-lived
signed token in the URL. A small shared internal package handles this.

```mermaid theme={null}
sequenceDiagram
    actor U as User
    participant AU as Authentication app (issuer)
    participant APP as Target app (consumer)
    participant G as api.bbrands.io

    U->>AU: Login (email/password or social)
    AU->>AU: Sign a short-lived handshake token (HS256, 60s)
    AU->>U: Redirect target?<handshake-token>
    U->>APP: GET target?<handshake-token>
    APP->>G: POST /api/v3/auth/sso/exchange { token }
    G->>G: Verify token (HMAC), re-verify inner jwt, mint new session JWT
    G->>APP: { jwt }
    APP->>U: Set HttpOnly session cookie, redirect to clean URL
```

### Shared internal SSO package

A small package for **consumer** apps, with helpers for:

| Helper        | Purpose                                                                                                     |
| ------------- | ----------------------------------------------------------------------------------------------------------- |
| Sign / verify | Create and validate the short-lived HS256 handshake token (`{ jwt, nonce, next? }` claims, default TTL 60s) |
| Exchange      | Call `POST /api/v3/auth/sso/exchange` and receive the session `{ jwt }`                                     |
| Proxy         | A middleware that turns the handshake token into a session cookie                                           |

```typescript theme={null}
// Consumer app proxy (pseudocode)
const sso = createSsoProxy({
  backendUrl: config.routeApiUrl,
  cookieDomain: config.sessionCookieDomain,
  cookieName: config.sessionCookieName,
  hmacSecret: config.ssoHmacSecret,
  isProduction: config.isProduction,
});

export default function proxy(request) {
  if (hasHandshakeToken(request)) return sso.proxy(request);
  // ...session guard
}
```

### The SSO issuer

The authentication app is the centralized login/register/2FA + consent UI. It is
the **issuer**: it signs the handshake token and writes the session cookie
directly in server actions (it does not mount the consumer proxy). Same-domain
targets redirect without a handshake token; cross-domain targets receive one.

<Note>
  The internal SSO exchange (`/api/v3/auth/sso/exchange`) is for cross-app session
  transfer. It is **not** the external OAuth client exchange
  (`/api/v3/oauth/exchange`). See [Token exchange](/oauth/token-exchange).
</Note>

### Security model

| Aspect        | Detail                                                           |
| ------------- | ---------------------------------------------------------------- |
| Algorithm     | HS256                                                            |
| Secret        | A shared HMAC secret between issuer and backend                  |
| TTL           | 60s — only survives one redirect                                 |
| Claims        | `jwt`, `nonce`, `next?` + `iss`/`aud`/`iat`/`exp`                |
| Target cookie | `HttpOnly`, `SameSite=Lax`, `Secure` in prod                     |
| Failure       | Proxy redirects to a clean URL with `?sso_error=exchange_failed` |
