s&box Auth
s&box Auth verifies that API requests come from real s&box players with valid Steam accounts. It is optional but strongly recommended for any game that stores p...
# s&box Auth
s&box Auth verifies that API requests come from real s&box players with valid Steam accounts. It is optional but strongly recommended for any game that stores per-player data like currency, XP, or inventory.
## What It Does
When enabled, every API request must include a **token** generated by the s&box engine. The token is verified against Facepunch's auth service to confirm the player's Steam ID. This prevents:
- Forged requests (someone calling your API from outside s&box)
- Identity spoofing (player A pretending to be player B)
- Replay attacks (tokens are single-use and burned on verification)
Without s&box Auth, anyone who knows your API key and a Steam ID can make requests on behalf of that player.
## How It Works
```
Game Client (s&box) sboxcool.com Backend Facepunch Auth
------------------- ---------------- --------------
1. Request token from engine
Auth.GetToken("sbox-network-storage")
------>
2. Send API request with
token + steamId + apiKey
3. Forward token + steamId
to Facepunch for verification
------>
<------
4. Facepunch confirms identity
(status: "ok", verified steamId)
5. Compare verified steamId with
the steamId in the request
6. If match: process the request
If mismatch: reject with 401
<------
7. Receive response
```
The key point: your backend never trusts the Steam ID in the request alone. It asks Facepunch "does this token belong to this Steam ID?" and only proceeds if the answer is yes.
## Enabling s&box Auth
s&box Auth is a **project-level** setting. When enabled, it applies to all collections and endpoints in that project.
1. Go to your project on the [Network Storage dashboard](/tools/network-storage)
2. Open **Project Settings**
3. Toggle **s&box Auth** on
4. Click **Save**
That's it. All API requests to this project now require a valid token.
## Implementing Auth in Your Game
### With the Network Storage Library (Recommended)
If you use the [Network Storage library](/wiki/network-storage-v3/library-setup), auth is handled automatically. No extra code needed — the library fetches and attaches tokens to every request.
**Setup (one-time, in editor):**
At runtime, your game uses the **Project ID + Public Key**. The **Secret Key** is only for the Sync Tool / Management API and stays editor-only.
1. Open the s&box editor and go to **Editor → Network Storage → Setup**
2. Enter your **Project ID**, **Public Key**, and **Secret Key**
3. Click **Save Configuration**
This writes `Assets/network-storage.credentials.json` to your project. The library auto-loads it on first use — no manual `Configure()` call required.
```csharp
// No setup code needed in your game — credentials are loaded automatically
// from Assets/network-storage.credentials.json (configured via Editor → Network Storage → Setup)
// Auth tokens are fetched and attached to every request automatically
var result = await NetworkStorage.CallEndpoint( "report-kill", new {
target = "goblin"
} );
```
The library calls `Services.Auth.GetToken("sbox-network-storage")` internally and sends the token with each request. If credentials are missing, the game logs a warning pointing to the Setup tool.
### Library Auth Flow
The library calls `Services.Auth.GetToken( "sbox-network-storage" )` internally, attaches the current Steam ID and token, and retries briefly during startup when auth is not ready yet. Use `EnsureEndpointAuthAsync` when you want to gate UI before an endpoint call:
```csharp
if ( await NetworkStorage.EnsureEndpointAuthAsync( "report-kill" ) )
{
var result = await NetworkStorage.CallEndpoint( "report-kill", new { target = "training_dummy" } );
}
```
## Auth Sessions and Encrypted Requests
Projects can opt in to auth sessions and encrypted endpoint requests. The library setup and sync tooling read these project settings and apply the runtime behavior for endpoint calls.
When auth sessions are enabled, the backend can validate a fresh s&box token, maintain a session, and require reauth for selected endpoints. Game code should keep using `NetworkStorage.CallEndpoint(...)`; the library and project configuration own the transport details.
Session requests use the normal public API key plus `x-steam-id` and `x-sbox-token` for create/reauth. Runtime endpoint calls may keep sending a fresh client auth token, but when a valid auth session is present the backend only verifies the fresh token on endpoints marked with a reauth policy.
When encrypted requests are enabled, endpoint request bodies must be signed envelopes. The signature binds `projectId`, `steamId` or `sessionId`, `endpointSlug`, `nonce`, `publicKeyFingerprint`, and the encrypted payload. Envelopes bound to `sessionId` must also include a valid auth session token on the request. After decryption the payload must include:
```yml
encryptedRequestId: "{unixSeconds}_{random6plus}"
```
The backend rejects malformed, stale, or reused encrypted request IDs before endpoint execution. Replay tracking is scoped to the project, authenticated identity, and `encryptedRequestId`, so resending the same encrypted request cannot repeat a write.
### Host-Only API Calls (Dedicated Servers)
The standard pattern — each client calls the API directly with their own token — is recommended for most games.
For **dedicated server** setups where only the host makes API calls, Network Storage supports a **proxy mode** that securely forwards requests on behalf of other players.
## Proxy Mode (Multiplayer)
Proxy mode enables the game host to call endpoints on behalf of connected clients. This is designed for dedicated servers and listen-server setups where only the host machine communicates with the API.
### How It Works
Proxy mode uses a **3-party security model**:
```
Game Client Game Host sboxcool.com Backend Facepunch Auth
----------- ---------- ----------------- --------------
1. Generate token
Auth.GetToken()
2. Send to host via RPC
(token + steamId)
3. Compute proxy signature
HMAC-SHA256(apiKey, "projectId:slug:clientSteamId:clientToken")
4. Send API request with:
- Host token + Steam ID
- Client token + Steam ID
- Proxy signature
5. Verify proxy signature
(prevents cross-server replay)
6. Verify host token via Facepunch
-------> 7. Confirm host identity
<-------
8. Return result (steamId = client)
<------ 9. Response
```
The three security checks are:
1. **Host token** — verified by Facepunch, proves the host is a real s&box client
2. **Client token** — proves the client authorized this specific request (consent proof)
3. **Proxy signature** — `HMAC-SHA256(apiKey, "projectId:endpointSlug:clientSteamId:clientToken")` — ties the request to a specific project and endpoint, preventing cross-server replay attacks
### Enabling Proxy Mode
Proxy mode is controlled per-project. Enable it from the s&box editor:
1. Open **Editor → Network Storage → Settings**
2. Toggle **Proxy Mode** on
3. Click **Save**
Or configure in code:
```csharp
NetworkStorage.ProxyEnabled = true;
```
### Using Proxy Mode in Your Game
The library provides `CallEndpointAs` and `GetDocumentAs` for host-to-client proxy calls:
```csharp
// On the HOST: call an endpoint on behalf of a connected client
var result = await NetworkStorage.CallEndpointAs(
targetSteamId: clientSteamId,
clientToken: clientToken,
slug: "report-kill",
input: new { target_type = "goblin_warrior" }
);
// Read a client's data
var data = await NetworkStorage.GetDocumentAs(
targetSteamId: clientSteamId,
clientToken: clientToken,
collectionId: "players"
);
```
The client must provide a fresh token via RPC before each call. Tokens are single-use.
**Client-side setup (on each player's machine):**
```csharp
// Client generates a token and sends it to the host
[ConCmd.Server]
public static void SendAuthToken()
{
var token = await Services.Auth.GetToken("sbox-network-storage");
// Send to host via RPC
RpcSendTokenToHost(Conversation.Caller.SteamId, token);
}
```
**Host-side proxy component:**
```csharp
public class NetworkStorageProxyComponent : Component
{
// Attach to each player via PlayerSpawner
public string ClientToken { get; set; }
public string ClientSteamId { get; set; }
public async Task CallForClient(string slug, object input)
{
if (string.IsNullOrEmpty(ClientToken))
{
Log.Warning("No client token — request fresh token via RPC");
return;
}
var result = await NetworkStorage.CallEndpointAs(
ClientSteamId, ClientToken, slug, input
);
// Token is consumed — clear it
ClientToken = null;
if (result.HasValue)
{
// Process result...
}
}
}
```
### Proxy Request Headers
When proxy mode is active, the library sends additional headers:
| Header | Purpose |
|--------|---------|
| `x-sbox-token` | Host's s&box auth token |
| `x-steam-id` | Host's Steam ID |
| `x-on-behalf-of` | Target client's Steam ID |
| `x-on-behalf-of-token` | Client's s&box auth token (consent proof) |
| `x-proxy-signature` | HMAC-SHA256 signature binding the request |
### Proxy Signature Computation
The signature ties the request to a specific project, endpoint, and client — preventing replay across different servers or endpoints:
```
HMAC-SHA256(
key = apiKey (your project's public API key),
data = "projectId:endpointSlug:clientSteamId:clientToken"
)
```
Both the client library and the backend compute this independently. If the signatures don't match, the request is rejected. The library uses a pure managed implementation (no `System.Security.Cryptography` — which is blocked by the s&box sandbox).
### IsHost Helper
```csharp
// Check if this machine is the host (authority)
if (NetworkStorage.IsHost)
{
// Safe to make API calls
}
else
{
// Send token to host via RPC instead
}
```
`IsHost` returns `true` when `Networking.IsActive` is false (single-player / self-hosted) or when `Networking.IsHost` is true (you are the host).
### When to Use Proxy Mode
| Scenario | Use Proxy? | Why |
|----------|------------|-----|
| Dedicated server, all API calls go through host | Yes | Host calls on behalf of clients securely |
| Listen server with host + clients | Yes | Same as dedicated — host proxies for clients |
| Each client calls API directly | No | Standard direct auth is simpler |
| Single-player game | No | Direct auth works — no proxy needed |
### Security Guarantees
- The host **cannot** forge requests as arbitrary players — each request requires a token generated by the target player's machine
- The host **cannot** replay client tokens on different endpoints — the signature is endpoint-specific
- A compromised host **cannot** impersonate players on other servers — the signature includes the project ID
- Client tokens are single-use — even if intercepted, they cannot be reused
### Token Lifecycle
- Tokens are **single-use**. Each token is burned after one verification attempt.
- Generate a **fresh token for every request**. Do not cache or reuse tokens.
- Token generation is fast (local engine call, no network round-trip).
- If verification fails, the backend returns HTTP 401 with error code `SBOX_AUTH_FAILED`.
## Without Auth Enabled
When s&box Auth is disabled on your project:
- Requests still need an `apiKey` and `steamId`
- The `steamId` is trusted as-is (not verified against Facepunch)
- The `token` parameter is ignored even if provided
- The writer ID defaults to `"anonymous"` if no Steam ID is provided
- **Proxy mode headers are ignored** — the `x-on-behalf-of` header is trusted directly without client token or signature verification
This is fine for prototyping or testing, but not recommended for production games with per-player data.
## Error Handling
When auth fails, the API returns:
```yml
ok: false
error:
code: SBOX_AUTH_FAILED
message: s&box auth token verification failed.
docsUrl: https://sboxcool.com/wiki/network-storage-v3/sbox-auth
```
Common causes:
| Cause | Fix |
|-------|-----|
| Not set up via editor tool | Run **Editor → Network Storage → Setup** and save your credentials. The library auto-configures from the generated credentials file. |
| Missing token (manual HTTP) | Make sure you call `Services.Auth.GetToken("sbox-network-storage")` before each request |
| Reused token | Generate a fresh token for every request. Tokens are single-use. |
| Steam ID mismatch | The `steamId` in the request must match the Steam account that generated the token |
| Too many failures | After 10 consecutive failures, the Steam ID is blocked for 60 seconds. Wait and retry. |
| Testing outside s&box | Auth tokens can only be generated inside s&box. For external testing, disable auth temporarily in project settings. |
Handle auth failures in your game code:
```csharp
var result = await NetworkStorage.CallEndpoint( "report-kill", new { target = "goblin" } );
if ( !result.HasValue )
{
// Request failed - could be auth, network, or endpoint error
Log.Warning( "Request failed. Check connection and auth settings." );
return;
}
```
## Proxy Auth Errors
When proxy auth fails, the API returns `SBOX_AUTH_FAILED` with additional context:
```yml
ok: false
error:
code: SBOX_AUTH_FAILED
message: "Proxy auth: signature mismatch — request may have been tampered with"
docsUrl: https://sboxcool.com/wiki/network-storage-v3/sbox-auth
```
Common proxy auth failures:
| Cause | Fix |
|-------|-----|
| Missing client token | Client must call `Auth.GetToken()` and send it to the host via RPC |
| Missing proxy signature | Ensure `proxyEnabled` is set in your library config |
| Signature mismatch | Token was for a different endpoint or project — generate a fresh token |
| Host token expired | Host must generate a new token for its own identity |
| Client token already used | Tokens are single-use — client must provide a new one for each request |
## When to Use Auth
| Scenario | Auth Recommended? | Why |
|----------|-------------------|-----|
| Per-player saves (XP, inventory, currency) | Yes | Prevents players from modifying each other's data |
| Leaderboards | Yes | Ensures scores come from real players |
| Global counters (total kills, server stats) | Optional | Less risk since data isn't per-player |
| Development and testing | No | Easier to test without auth. Enable before release. |
| Dedicated server with proxy mode | Yes + Proxy | Host calls on behalf of clients with cryptographic verification |
| Dedicated server only (no proxy) | Optional | If only your server has the API key, auth adds less value since clients never call the API directly |
## Security Layers
s&box Auth is one layer in a defense-in-depth approach:
```
Layer 1: API Key - Identifies your project
Layer 2: s&box Auth - Verifies player identity via Steam
Layer 3: Endpoint controlled - Blocks direct collection writes
Layer 4: Conditions - Validates game logic (enough gold, correct level)
Layer 5: Rate Limits - Caps how fast values can change
Layer 6: Ledger - Permanent audit trail of all changes
```
For maximum security, enable all layers. For a quick prototype, start with just an API key and add layers as you go.
## Testing Without Auth
During development, you can test endpoints without auth:
1. Disable **s&box Auth** in project settings
2. Use the endpoint Test panel in the Network Storage dashboard or the library's editor Test Window
3. Keep game code on `NetworkStorage.CallEndpoint(...)` so tests and runtime calls use the same response shape
Remember to re-enable auth before publishing your game.
## FAQ
**Does auth slow down requests?**
Auth adds one round-trip to Facepunch's servers (typically 10-50ms). The backend starts auth verification in parallel with request body parsing, so the overhead is minimal.
**Can I use auth on some endpoints but not others?**
No. Auth is a project-level toggle. If you need mixed auth, create two projects: one with auth for gameplay data, one without for public stats.
**What happens if Facepunch's auth service is down?**
Requests that require auth will fail with a 401 error. The backend has a 10-second timeout per verification attempt. This is extremely rare.
**Do dedicated servers need auth?**
If only your server has the API key and clients never call the API directly, auth is less critical. But it still adds a verification layer if someone extracts the key from your server.