Access Tokens vs Refresh Tokens (for Game Developers)
Once a player signs in, the provider hands your game one or two tokens, and it's easy to mix up what they're for. Getting it wrong leads to players who get logged out every hour, or refresh tokens sitting in plaintext where anyone can read them. Here's the difference, why it exists, and how to handle both in a Unity game.
Two tokens, two jobs
An access token is the key you present to make an authenticated request — "here's proof I'm allowed to do this." It's deliberately short-lived, often about an hour. The short life is the point: if it leaks, the window an attacker has is small, and then it's dead.
A refresh token does one thing — it gets you a new access token. It's long-lived, and you never send it with normal requests. It sits in secure storage and comes out only when the access token has expired and you need a fresh one, without dragging the player back through a login screen.
The split is a security trade-off. You want the credential that's used constantly to expire fast, and the credential that renews it to be used rarely and guarded closely. One is a day pass; the other is how you get tomorrow's day pass.
There's often a third token in the mix. The id_token, from OpenID Connect, is a signed statement of who the player is — you read it once at login to learn their identity. It's not an API key. Don't send it as one, and don't try to pull identity out of an access token, which is usually opaque.
How it plays out over a session
A typical lifecycle looks like this:
- The player signs in. Your game receives an access token and a refresh token.
- The game makes calls with the access token. Everything works.
- An hour later the access token expires. The next call fails with a
401. - The game uses the refresh token to request a new access token — no login screen, no interruption.
- Play continues. Repeat step 3–4 as needed.
The player experiences one thing: they signed in once and stayed signed in. All the expiry and renewal is invisible.
Rotation: the detail people miss
Good systems rotate refresh tokens. Every time you spend a refresh token, the server gives you a brand-new one and invalidates the old. This means a leaked refresh token has a short useful life, and a reused old token is a signal that something's wrong.
The practical consequence for your code: store the new refresh token every refresh. If you keep using the original, your next renewal fails because that token was retired the moment you first refreshed. This is the single most common bug in hand-rolled token handling.
With Oathwall, the numbers are concrete: an access token expires in about an hour, and refreshing returns both a new access token and a new refresh token — the old refresh token is invalidated on rotation, and the refresh session lasts 30 days on a rolling basis, extended each time you refresh.
Storing tokens in Unity
Where you put these matters as much as how you use them.
The wrong move is PlayerPrefs in plaintext. On desktop that's a readable file; on a rooted or jailbroken device it's trivial to pull. A refresh token there is a long-lived key to the player's account sitting in the open.
The right move is platform-secure storage — the Keychain on iOS, Keystore-backed storage on Android — so the refresh token is protected by the OS. The access token is shorter-lived and lower-stakes, but there's no reason to be careless with it either. Storing auth tokens in Unity covers the where-and-how in full, including the WebGL case.
If you'd rather not build this yourself, Oathwall's Unity SDK stores and refreshes the session for you. SilentLogin() resumes a saved session on the next launch, and the refresh — including rotation — is handled under the hood:
using PixitGames.SSOLoginKit;
public class LoginManager : SsoClientBase
{
// On launch, resume the stored session if there is one.
// Token storage and rotation are handled for you.
void Start() => SilentLogin();
protected override void OnLoginSuccess(LoginResult user)
{
Debug.Log($"Welcome back, {user.name}");
}
}
To understand where these tokens come from in the first place, read OAuth 2.0 explained and what PKCE protects. To put it all into a real integration, the Unity authentication guide is the hub.
Oathwall is available on the Unity Asset Store.
Frequently asked questions
- What's the difference between an access token and a refresh token?
- An access token is a short-lived key used to make authenticated calls; it expires fast (often an hour) to limit damage if it leaks. A refresh token is a long-lived credential whose only job is to get new access tokens without making the player sign in again. You send the access token with requests; you keep the refresh token to renew them.
- How long do these tokens last?
- It depends on the issuer. With Oathwall, an access token expires in about an hour, and the refresh session lasts 30 days on a rolling basis — each refresh extends it. The exact numbers vary by provider and service, but short-access, long-refresh is the standard shape.
- What is refresh token rotation?
- Each time you use a refresh token, the server issues a new one and invalidates the old. Always store the new refresh token the call returns. Rotation limits a stolen refresh token's usefulness and makes reuse detectable.
- Where should I store tokens in a Unity game?
- Not in plaintext PlayerPrefs. Prefer platform-secure storage — Keychain on iOS, the Keystore-backed options on Android. If you use Oathwall's Unity SDK, it stores and refreshes the session for you, so you're not hand-rolling token storage.
- What is an id_token and how is it different?
- An id_token (from OpenID Connect) is a signed statement of who the user is — it's for your app to read once at login. An access token is for calling APIs. Don't use an id_token as an API key, and don't try to read identity out of an opaque access token.