Authentication

// Config: Engine - [], Foundation - [v1.64.34], Toolkit - [v6.1.19]

This is the part of a Steam multiplayer game that is most often done halfway. Steam can prove who a connecting player is, but only if the client and the server each do their part and each clean up after themselves. This page is the whole flow in order, the one rule per step, and the mistakes that break it.

Why bother #

A network library tells you a connection exists. It does not tell you whose it is. Without authentication a client can claim any Steam ID it likes. With it, the server asks Steam, and Steam answers whether the ticket really belongs to that account and that game.

The flow #

sequenceDiagram
    participant C as Client
    participant S as Server (Game Server)
    participant ST as Steam
    C->>ST: GetAuthTicket
    ST-->>C: ticket (single use)
    C->>S: ticket + claimed Steam ID (your netcode)
    S->>ST: BeginSession(ticket, claimed ID)
    ST-->>S: return value (OK or a rejection)
    Note over S: Not OK: kick now, no callback will come
    ST-->>S: validation callback (OK or a failure)
    Note over S: OK: create the player. Not OK: end session and kick
    Note over C,S: ... the match runs ...
    S->>ST: EndSession(user)
    C->>ST: CancelAuthTicket(ticket)

One rule per step #

Step Who Call The rule
1 Client Client.GetAuthTicket Ask once per connection. Keep the ticket object, you need it again in step 7.
2 Client your netcode Send the ticket bytes and the claimed Steam ID to the server.
3 Server Server.BeginSession Check the return value. It is an EBeginAuthSessionResult. Anything other than k_EBeginAuthSessionResultOK means Steam refused the ticket. Kick the connection.
4 Server the callback you passed This is the real answer. Only k_EAuthSessionResponseOK means the player is who they say. Do not create the player before it.
5 Server the same callback, later Steam calls it again if the player goes offline or the ticket is cancelled. Treat any non-OK answer as “this player is gone”: end the session and disconnect. Ignore a second OK.
6 Server Server.EndSession Call it when the player leaves, including when they leave before step 4 finished and when validation fails.
7 Client API.Authentication.CancelAuthTicket Cancel every ticket you were given when you stop playing.

Valve’s own documentation states the two clean-up rules: the server must call EndAuthSession when the multiplayer session ends, and the client must call CancelAuthTicket for every handle it received. It also states that a ticket may be used only once.

What we measured #

These are results from running the calls against live Steam, not assumptions.

Test Result
BeginSession with a bad ticket Returns InvalidTicket. The callback never fires. A server that waits for the callback waits forever.
BeginSession twice with the same ticket The second returns DuplicateRequest. Tickets are single use.
A bad BeginSession, then a good one for the same user (before the Toolkit fix) The good session’s answer was delivered to the bad one’s callback. The Toolkit now registers a session only when Steam returns OK.

The common mistakes #

  • Ignoring the return value of BeginSession. The connection sits open and unauthenticated, never spawned and never kicked.
  • Spawning the player before the callback. The return value only says the ticket looked well formed. The callback says it is genuine.
  • Trusting the claimed Steam ID afterwards. Take the identity from the validated session, not from later messages.
  • Never calling EndSession. Steam keeps tracking a player who has left, and a returning player’s new ticket can be refused.
  • Never cancelling client tickets. Every ticket is a live handle until you cancel it.
  • Reusing a ticket. Ask for a new one for every connection.
  • Handling only the first callback. The second one is how you learn a player went offline.

How the demos do it #

Sample Where
PurrNet, FishNet, NGO, Mirror PlayerController. The client keeps its ticket and cancels it when the controller despawns. The server checks the BeginSession return value, ignores a second ticket from the same connection, kicks and ends the session on any failure, spawns once, and ends the session when the controller despawns. The pawn also ends the session when it is destroyed.
Netcode for Entities SteamApprovalClientSystem and SteamApprovalServerSystem in SteamConnectionApproval.cs. The client system keeps and cancels its tickets. The server system turns a rejected BeginSession into a disconnect, and ends the session on every failure and disconnect.
Template PlaceholderNetwork does not authenticate. Follow Integrating-Networking.md and the table above when you connect your own library.

Checklist for your own game #

  1. Does the client keep its ticket and cancel it when it leaves?
  2. Does the server check the BeginSession return value?
  3. Does the server wait for the callback before it creates the player?
  4. Does the callback handle a second, non-OK answer?
  5. Does every exit path call EndSession: a failed validation, a disconnect before validation finished, and a normal leave?
  6. Is the player’s identity taken from the validated session?

Rate This Article!