Docs / Authentication

Authentication

Asobi supports username/password, OAuth/OIDC social login (Google, Apple, Microsoft, Discord), Steam, and device auth. Players can link multiple providers to a single account.

Windows. Run the curl examples in Git Bash or WSL, or use PowerShell's Invoke-RestMethod with the same URL and a JSON -Body; it parses the response for you. Authenticated calls add -Headers @{ Authorization = 'Bearer <token>' }.

Username & password

The simplest method. Register to receive an access token and a refresh token:

curl -X POST http://localhost:8084/api/v1/auth/register \
  -H 'Content-Type: application/json' \
  -d '{"username": "player1", "password": "secret123"}'
{"player_id": "...", "access_token": "...", "refresh_token": "...", "username": "player1"}

Use the access token in subsequent REST calls:

Authorization: Bearer <access_token>

Login reuses the same shape at /api/v1/auth/login.

Refresh & rotation

Access tokens are short-lived. When one expires (a 401), exchange the refresh token for a fresh pair at /api/v1/auth/refresh. Rotation is single-use: the server burns the presented refresh token and returns a new access token and a new refresh token, so always store both from the response.

curl -X POST http://localhost:8084/api/v1/auth/refresh \
  -H 'Content-Type: application/json' \
  -d '{"refresh_token": "<refresh_token>"}'
# => {"access_token": "...", "refresh_token": "..."}

The official SDKs persist the refresh token, attach the access token to every call, and refresh-and-retry on a 401 automatically.

OAuth / social login

For game clients, Asobi uses server-side token validation. The client authenticates with the platform SDK, obtains an ID token (JWT), and sends it to Asobi:

curl -X POST http://localhost:8084/api/v1/auth/oauth \
  -H 'Content-Type: application/json' \
  -d '{"provider": "google", "token": "eyJhbGciOiJSUzI1NiIs..."}'

Flow: platform SDK → ID token → Asobi validates against provider JWKS → existing player (login) or new one (create + link).

Supported providers

 Provider   | provider value | Issuer
------------|----------------|--------------------------------------------------
 Google     | "google"       | https://accounts.google.com
 Apple      | "apple"        | https://appleid.apple.com
 Microsoft  | "microsoft"    | https://login.microsoftonline.com/common/v2.0
 Discord    | "discord"      | https://discord.com
 Steam      | "steam"        | custom (Steam Web API session ticket)

Provider configuration

Add credentials to sys.config:

{asobi, [
    {oidc_providers, #{
        google => #{
            issuer => <<"https://accounts.google.com">>,
            client_id => <<"YOUR_CLIENT_ID">>,
            client_secret => <<"YOUR_CLIENT_SECRET">>
        },
        apple => #{
            issuer => <<"https://appleid.apple.com">>,
            client_id => <<"YOUR_CLIENT_ID">>,
            client_secret => <<"YOUR_CLIENT_SECRET">>
        }
    }}
]}

Steam

Steam uses session tickets rather than OIDC. The client calls ISteamUser::GetAuthSessionTicket and sends the hex-encoded ticket:

curl -X POST http://localhost:8084/api/v1/auth/oauth \
  -H 'Content-Type: application/json' \
  -d '{"provider": "steam", "token": "14000000..."}'

Asobi validates via the Steam Web API. Config:

{asobi, [
    {steam_api_key, <<"YOUR_STEAM_WEB_API_KEY">>},
    {steam_app_id, <<"YOUR_STEAM_APP_ID">>}
]}

Linking providers

Players can link additional providers to their existing account (Google + Steam on the same player, say). Requires an authenticated session:

curl -X POST http://localhost:8084/api/v1/auth/link \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Content-Type: application/json' \
  -d '{"provider": "discord", "token": "eyJhbGciOi..."}'

Unlink via DELETE /api/v1/auth/unlink. Asobi refuses to unlink the last auth method to avoid locking the player out.

WebSocket authentication

After obtaining an access token (via any method above), connect to the WebSocket and authenticate as the first message:

{"type": "session.connect", "payload": {"token": "<access_token>"}}

Token behaviour is provider-agnostic - it works the same regardless of which login path produced it.

SDK integration

-- Defold (Lua)
local id_token = google_sign_in.get_id_token()
asobi.auth.oauth("google", id_token, function(result)
    -- access token stored internally
end)
// Unity (C#)
string idToken = googleSignIn.IdToken;
var response = await asobi.Auth.OAuth("google", idToken);
// access token stored internally

Where next?