> For the complete documentation index, see [llms.txt](https://gamelauncher.cloud/help/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://gamelauncher.cloud/help/add-ons/login-system/custom-auth.md).

# Custom Auth (Bring Your Own Auth)

## What Is This?

If you already have your own player database, you don't need our platform-managed Login System accounts — **Custom Auth** lets players log into the launcher with the credentials they already use, by having us call an HTTPS endpoint **you host** to validate them.

{% hint style="info" %}
Custom Auth replaces the **email/username + password** login only. It's an alternative **Authentication Provider** for the Login System add-on — you don't need to run both our accounts and yours for the same launcher.
{% endhint %}

***

## How It Works

```
Player types      Launcher sends      GLC Backend calls        Your endpoint
credentials  ───>  to GLC Backend ───>  YOUR endpoint    ───>   checks your DB
                                        (HMAC-signed)           and responds
```

1. Player enters their username/email + password in the launcher
2. GLC's backend sends them to **your endpoint**, signed with a shared secret
3. Your endpoint checks them against your own database and replies `valid: true`/`false`
4. GLC signs the player in — your database, your rules, your password hashes. We never store them.

***

## What Your Endpoint Must Do

Your endpoint must accept a `POST` request and respond within your configured timeout (default 10s, range 1–30s).

**Request you'll receive:**

```
POST https://yourgame.com/api/auth/validate
webhook-id: 3c1a...
webhook-timestamp: 1735689600
webhook-signature: v1,<base64 HMAC-SHA256>
Content-Type: application/json

{"launcherId":42,"identifier":"playerOne","password":"the-players-password","requestId":"b3f8..."}
```

The body is sent as compact JSON with camelCase keys. The example above is shown on one line on purpose — that's the exact byte layout you'll receive, and it's what the signature is computed over (see below).

**Response we expect:**

```json
{ "valid": true, "userId": "12345", "displayName": "PlayerOne", "email": "player@example.com" }
```

| Field         | Required | Meaning                                                                                                              |
| ------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| `valid`       | ✅        | `true` if the credentials are correct                                                                                |
| `userId`      | if valid | A stable, unique ID for this player in **your** system                                                               |
| `displayName` | optional | Shown in-game via `{{user_display_name}}` (see [App Launch Tokens](/help/add-ons/login-system/app-launch-tokens.md)) |
| `email`       | optional | Shown in-game via `{{user_email}}`                                                                                   |
| `banned`      | optional | `true` blocks the login with an "account banned" message                                                             |
| `error`       | optional | A custom message shown to the player when `valid` is `false`                                                         |

If the credentials are wrong, just respond `{ "valid": false }` — no need for an HTTP error code. Return `200 OK` for any answer your endpoint can give (valid, invalid, or banned); non-2xx responses are treated as the endpoint being **down**, not as a rejected login.

{% hint style="warning" %}
`identifier` is either an email or a username, depending on what you picked in the dashboard. `password` is sent in plain text over HTTPS — never log it.
{% endhint %}

***

## Verifying the Signature (Recommended)

Every request is signed with the shared secret from the dashboard using the **Standard Webhooks / Svix** scheme, so you can confirm it really came from us.

The secret is shown as `whsec_<base64>`. Before using it as the HMAC key you must **strip the `whsec_` prefix and Base64-decode the rest** — the raw bytes are the key, not the `whsec_…` string itself:

```
key_bytes   = base64_decode( secret_without_"whsec_"_prefix )
signed_data = "{webhook-id}.{webhook-timestamp}.{raw request body}"
expected    = base64( HMAC-SHA256(key_bytes, signed_data) )
```

Then compare `expected` against the value after `v1,` in the `webhook-signature` header (use a constant-time comparison). The header may contain multiple space-separated `v1,…` signatures — a match on any one is valid.

{% hint style="warning" %}
Compute the signature over the **raw request body exactly as received** (the bytes on the wire). Don't parse and re-serialize the JSON first — reformatting or reordering keys changes the bytes and the signature won't match.
{% endhint %}

Because this is the Standard Webhooks format, most existing Svix/Standard-Webhooks verification libraries will validate our requests directly if you feed them the `whsec_…` secret and the three `webhook-*` headers.

***

## Test Connection Sentinel

When you click **Test Connection** in the dashboard, we send a probe request with `identifier` and `password` both set to `__healthcheck__`. It's signed exactly like a real request.

Your endpoint should recognize this sentinel and reply with a well-formed `{ "valid": false }` **without querying your real database**. The probe passes as long as your endpoint is **reachable** and returns valid JSON with a `valid` field — the actual `true`/`false` value doesn't matter here, since the test is only proving the endpoint is up and answering. Short-circuiting on the sentinel keeps the test fast and skips a pointless database lookup.

***

## Setup

1. Go to your launcher's **Login System** add-on page
2. Under **Authentication Provider**, select **Your Own System**
3. Choose the **Login identifier** — whether players log in with **Email** or **Username** — and, optionally, a **custom field label** (e.g. *Gamertag* or *Player ID*) to rename that field on the login screen
4. Click **Save Provider Settings** — this reveals the **Custom Auth Endpoint** configuration
5. Enter your **Endpoint URL** and click **Save Endpoint Config**. A **shared secret is generated for you automatically** and shown **once** — copy it now, as it won't be displayed again. You never type or paste a secret yourself; use **Regenerate** later to rotate it (which immediately invalidates the old one).
6. *(Optional)* Adjust the **Timeout** (1–30s) and set **Player Account Links** — a **Create account** and/or **Forgot password** URL we open in the player's browser. Leave either blank to hide that link.
7. Click **Test Connection** and confirm it shows **Verified**

{% hint style="warning" %}
Custom Auth is only used at runtime once **Test Connection** passes. If the endpoint isn't verified (or later starts failing), the launcher fails safe and won't lock players out with a half-configured endpoint — so always confirm **Verified** after saving or rotating the secret.
{% endhint %}

{% hint style="success" %}
Switching to Your Own System only replaces the **email/username + password** login. **Guest login**, **social sign-in** (Google, Discord, Patreon), and **Managed Access** still work and are handled by the platform, so you can offer them alongside your own accounts. Only the platform-managed **email/password registration** form is hidden — account creation now lives in your database (point players to it with the **Create account URL**).
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://gamelauncher.cloud/help/add-ons/login-system/custom-auth.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
