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

# Player Authentication

> Configure player identity verification for lobbies and matchmaking.

PlayFlow supports authenticating players who connect through the Lobby and Matchmaking system. You configure an auth provider for your project via the engine API, and PlayFlow validates player identity tokens before allowing lobby and matchmaking operations.

## Prerequisites

* A PlayFlow project with an API key. Player-token verification only runs for **client keys** (`pfclient_*`, the key you ship in your game build). See [Sending the Player Token](#sending-the-player-token) for why the key type matters.
* A **server key** (`pf_*`) to configure the provider — updating project settings requires a server key (a client key returns `403`).
* If you use a verified provider, a way for your game client to obtain a token (a JWT, PlayFab entity token, or Steam session ticket).

## How It Works

When a player connects to a lobby or enters matchmaking with a client key, their game client sends an identity token in the `x-player-token` header. PlayFlow validates that token against your configured provider, creates or updates a player record, and grants access to the session. When your project uses the default `none` provider, PlayFlow instead trusts the `x-player-id` header verbatim.

```mermaid theme={null}
sequenceDiagram
    participant Client as Game Client
    participant Provider as Auth Provider
    participant PF as PlayFlow
    participant DB as Players Table

    Client->>Provider: Authenticate (login, session, ticket)
    Provider-->>Client: Identity token
    Client->>PF: Lobby/matchmaking request<br/>api-key + x-player-token
    PF->>Provider: Verify token (JWKS / Steam API)
    Provider-->>PF: Verified identity
    PF->>DB: Upsert player record
    DB-->>PF: Player ID
    PF-->>Client: Access granted
```

<Steps>
  <Step title="Configure your auth provider">
    Set the provider on your project with a server key via `POST /api/v3/projects/settings`. See [API Configuration](#api-configuration) below.
  </Step>

  <Step title="Get a token from your auth provider">
    Your game client authenticates with your chosen provider and receives a token -- a JWT, PlayFab entity token, or Steam session ticket.
  </Step>

  <Step title="Send the token with requests">
    Include the token in the `x-player-token` header when your client connects to lobbies or matchmaking.
  </Step>

  <Step title="PlayFlow validates and tracks the player">
    PlayFlow verifies the token, creates or updates a player record with the provider's user ID, and grants access. You can view authenticated players in the **Players** tab on the dashboard.
  </Step>
</Steps>

## Auth Providers

PlayFlow supports four authentication providers. Choose the one that matches your game's identity system.

<Tabs>
  <Tab title="None (Default)">
    No server-side verification. In `none` mode, the player's identity comes from the `x-player-id` header, which PlayFlow trusts verbatim. The `x-player-token` header is not consulted in this mode.

    This is the default for all projects. It is suitable for development and testing but should not be used in production.

    ### Configuration

    No configuration is needed. All new projects start with this provider.

    ```json theme={null}
    {
      "provider": "none"
    }
    ```

    ### Behavior

    * Send the player's unique identifier in the `x-player-id` header on every player-scoped lobby and matchmaking request.
    * The `x-player-id` value is used directly as the player identity — there is no verification.
    * Player-scoped requests without an `x-player-id` header fail with `400 Missing x-player-id header`.
    * `x-player-token` is ignored in `none` mode.

    <Warning>
      None mode performs no verification. Any client can impersonate any player by sending an arbitrary `x-player-id` value. Use a verified provider (Custom JWT, PlayFab, or Steam) before shipping to production.
    </Warning>
  </Tab>

  <Tab title="Custom JWT">
    Validate JWTs from any OIDC-compliant provider using a JWKS (JSON Web Key Set) endpoint. This works with Auth0, Firebase Auth, Supabase Auth, Clerk, and any other provider that publishes a JWKS URL.

    PlayFlow fetches your public keys from the JWKS endpoint, verifies the JWT signature and expiry, and extracts the `sub` claim as the player identifier.

    ### Configuration

    ```json theme={null}
    {
      "provider": "custom_jwt",
      "jwks_url": "https://your-auth-provider.com/.well-known/jwks.json",
      "issuer": "https://your-auth-provider.com",
      "audience": "your-app-id"
    }
    ```

    | Field      | Required | Description                                                              |
    | ---------- | -------- | ------------------------------------------------------------------------ |
    | `jwks_url` | Yes      | The URL where your auth provider publishes its public keys.              |
    | `issuer`   | No       | Expected `iss` claim in the JWT. Rejects tokens from other issuers.      |
    | `audience` | No       | Expected `aud` claim in the JWT. Rejects tokens intended for other apps. |

    ### Common JWKS URLs

    | Provider      | JWKS URL                                                                                    |
    | ------------- | ------------------------------------------------------------------------------------------- |
    | Auth0         | `https://YOUR_DOMAIN.auth0.com/.well-known/jwks.json`                                       |
    | Firebase Auth | `https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com` |
    | Supabase Auth | `https://YOUR_PROJECT.supabase.co/auth/v1/.well-known/jwks`                                 |
    | Clerk         | `https://YOUR_DOMAIN.clerk.accounts.dev/.well-known/jwks.json`                              |

    <Info>
      JWKS keys are cached in memory and rotated automatically when your provider publishes new keys. You do not need to restart or redeploy after a key rotation.
    </Info>
  </Tab>

  <Tab title="PlayFab">
    Native integration with Microsoft PlayFab. PlayFlow verifies PlayFab entity tokens (JWTs) against PlayFab's published JWKS endpoint and extracts the `eid` (entity ID) claim as the player identifier.

    ### Configuration

    ```json theme={null}
    {
      "provider": "playfab",
      "title_id": "YOUR_TITLE_ID"
    }
    ```

    | Field      | Required | Description                                                |
    | ---------- | -------- | ---------------------------------------------------------- |
    | `title_id` | Yes      | Your PlayFab Title ID (found in the PlayFab Game Manager). |

    PlayFlow automatically constructs the JWKS URL from your Title ID:

    ```
    https://{title_id}.playfabapi.com/jwks
    ```

    ### Getting a PlayFab Entity Token

    In your game client, call `GetEntityToken` after the player logs in:

    ```csharp theme={null}
    PlayFabAuthenticationAPI.GetEntityToken(
        new GetEntityTokenRequest(),
        result => {
            string entityToken = result.EntityToken;
            // Pass this as x-player-token when connecting to PlayFlow
        },
        error => Debug.LogError(error.GenerateErrorReport())
    );
    ```
  </Tab>

  <Tab title="Steam">
    Validates Steam authentication tickets via the Steamworks Web API. Unlike the JWT-based providers, this makes an HTTP call to Steam's servers for each authentication.

    ### Configuration

    ```json theme={null}
    {
      "provider": "steam",
      "app_id": "YOUR_APP_ID",
      "web_api_key": "YOUR_WEB_API_KEY"
    }
    ```

    | Field         | Required | Description                                                                                                           |
    | ------------- | -------- | --------------------------------------------------------------------------------------------------------------------- |
    | `app_id`      | Yes      | Your Steam Application ID.                                                                                            |
    | `web_api_key` | Yes      | Your Steam Web API Key (from the [Steamworks partner site](https://partner.steamgames.com/doc/webapi_overview/auth)). |

    PlayFlow calls the `ISteamUserAuth/AuthenticateUserTicket/v1/` endpoint and extracts the `steamid` as the player identifier.

    ### Getting a Steam Auth Ticket

    In your game client, use the Steamworks API to get a session ticket:

    ```csharp theme={null}
    // Using Steamworks.NET
    byte[] ticketData = new byte[1024];
    uint ticketLength;
    HAuthTicket ticket = SteamUser.GetAuthSessionTicket(ticketData, 1024, out ticketLength);
    string hexTicket = BitConverter.ToString(ticketData, 0, (int)ticketLength).Replace("-", "");
    // Pass hexTicket as x-player-token when connecting to PlayFlow
    ```

    <Warning>
      Your Steam Web API Key is stored in your project's `auth_config`. Set it only with a server key, treat it as a secret, and never expose it in a client build.
    </Warning>
  </Tab>
</Tabs>

## API Configuration

Configure player authentication by writing an `auth_config` to your project settings via the engine API. This is the only supported way to set the provider.

<Warning>
  Updating project settings requires a **server key** (`pf_*`). A client key (`pfclient_*`) returns `403`. Keep your server key server-side — never ship it in a game build.
</Warning>

<CodeGroup>
  ```bash None theme={null}
  curl -X POST https://api.computeflow.cloud/api/v3/projects/settings \
    -H "api-key: YOUR_SERVER_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "auth_config": {
        "provider": "none"
      }
    }'
  ```

  ```bash Custom JWT theme={null}
  curl -X POST https://api.computeflow.cloud/api/v3/projects/settings \
    -H "api-key: YOUR_SERVER_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "auth_config": {
        "provider": "custom_jwt",
        "jwks_url": "https://your-auth.com/.well-known/jwks.json",
        "issuer": "https://your-auth.com",
        "audience": "your-app"
      }
    }'
  ```

  ```bash PlayFab theme={null}
  curl -X POST https://api.computeflow.cloud/api/v3/projects/settings \
    -H "api-key: YOUR_SERVER_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "auth_config": {
        "provider": "playfab",
        "title_id": "YOUR_TITLE_ID"
      }
    }'
  ```

  ```bash Steam theme={null}
  curl -X POST https://api.computeflow.cloud/api/v3/projects/settings \
    -H "api-key: YOUR_SERVER_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "auth_config": {
        "provider": "steam",
        "app_id": "YOUR_APP_ID",
        "web_api_key": "YOUR_WEB_API_KEY"
      }
    }'
  ```
</CodeGroup>

You can verify the current configuration by reading your project settings:

```bash theme={null}
curl https://api.computeflow.cloud/api/v3/projects/settings \
  -H "api-key: YOUR_SERVER_KEY"
```

The response includes your current `auth_config` alongside other project settings. Changes take effect immediately for all new player connections.

## Sending the Player Token

<Info>
  **Token verification only runs for client keys (`pfclient_*`).** Ship a client key in your game build so PlayFlow verifies each player's `x-player-token` against your provider and derives identity from the verified token.

  A **server key (`pf_*`) bypasses verification** even when a provider is configured — it is trusted to assert identity directly via `x-player-id` (for trusted server-side matchmakers and admin tooling). If you test a verified provider with a server key, verification silently passes through and you may wrongly conclude it works.
</Info>

Once your provider is configured, your game client (using a client key) must include the player's identity token in the `x-player-token` header on every player-scoped lobby and matchmaking request. In `none` mode, send `x-player-id` instead (no token).

<Tabs>
  <Tab title="Unity">
    The PlayFlow SDK handles the token header automatically when you set the player token during initialization:

    ```csharp theme={null}
    // After authenticating with your provider, pass the token to PlayFlow
    string playerToken = GetTokenFromYourAuthProvider();

    PlayFlowLobbyManagerV2.Instance.Initialize(playerToken, () => {
        Debug.Log("PlayFlow SDK initialized with authenticated player.");
    });
    ```
  </Tab>

  <Tab title="REST API (verified provider)">
    With a verified provider and a client key, send the token on a player-scoped request. PlayFlow verifies it and derives the player identity — no `x-player-id` needed:

    ```bash theme={null}
    curl -X POST https://api.computeflow.cloud/api/v3/lobbies/default \
      -H "api-key: YOUR_CLIENT_KEY" \
      -H "x-player-token: PLAYER_IDENTITY_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"name": "My Lobby", "maxPlayers": 4}'
    ```
  </Tab>

  <Tab title="REST API (None mode)">
    In `none` mode, identify the player with the `x-player-id` header:

    ```bash theme={null}
    curl -X POST https://api.computeflow.cloud/api/v3/lobbies/default \
      -H "api-key: YOUR_API_KEY" \
      -H "x-player-id: player-123" \
      -H "Content-Type: application/json" \
      -d '{"name": "My Lobby", "maxPlayers": 4}'
    ```
  </Tab>
</Tabs>

## Players Table

Players verified by a provider are automatically tracked in a per-project players table. Each time a player is verified against **Custom JWT, PlayFab, or Steam**, PlayFlow upserts a record with the following fields:

| Field          | Description                                                                                       |
| -------------- | ------------------------------------------------------------------------------------------------- |
| `id`           | Unique player ID (UUID), auto-generated by PlayFlow.                                              |
| `provider`     | The auth provider that verified this player (`custom_jwt`, `playfab`, `steam`).                   |
| `provider_uid` | The player's identifier from the auth provider (e.g., JWT `sub`, PlayFab `eid`, Steam `steamid`). |
| `display_name` | Display name extracted from the token, if available.                                              |
| `metadata`     | Provider-specific metadata (e.g., `owner_steamid` for Steam, `title_id` for PlayFab).             |
| `last_seen_at` | Timestamp of the player's most recent authentication.                                             |

Players are uniquely identified by the combination of `project_id` and `provider_uid`. If the same player authenticates again, their existing record is updated rather than duplicated.

<Info>
  `none`-mode players are **not** written to the players table — no persisted record is created for them. They may appear transiently in a live lobby or match roster, but only verified-provider players are persisted.
</Info>

You can view your authenticated players in the **Players** tab on the project dashboard.

## Choosing a Provider

<CardGroup cols={2}>
  <Card title="Development / Testing" icon="flask">
    Use **None** to get started quickly without any auth infrastructure. Switch to a verified provider before launching.
  </Card>

  <Card title="Existing Auth System" icon="key">
    Use **Custom JWT** if you already have an auth provider (Auth0, Firebase, Supabase, Clerk, or any OIDC-compliant service).
  </Card>

  <Card title="PlayFab Games" icon="gamepad">
    Use **PlayFab** if your game uses Microsoft PlayFab for player management and you want native integration.
  </Card>

  <Card title="Steam Games" icon="steam">
    Use **Steam** if your game is distributed on Steam and you want to verify player identity through Steam session tickets.
  </Card>
</CardGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Lobby Quick Start" icon="rocket" href="/unity/lobbies">
    Set up lobbies and start using player authentication with the lobby system.
  </Card>

  <Card title="Matchmaking Guide" icon="swords" href="/unity/matchmaking">
    Add skill-based matchmaking with authenticated players.
  </Card>
</CardGroup>
