Player Accounts and Authentication for Web Games
Why Authentication Matters for Games
Authentication answers one question: who is this player? Once you know who they are, you can store their data under a persistent identifier that is independent of any single browser or device. The player's account ID becomes the key for cloud saves, leaderboard entries, purchase history, social connections, and analytics. Without authentication, all of these features are tied to browser-local storage, which is ephemeral and device-specific.
Web games face a specific tension between authentication and accessibility. A web game's greatest strength is zero installation: click a link and start playing. Requiring sign-in before the first play session undermines this advantage. Players who are forced to create an account before they have experienced the game have an extremely high abandonment rate. The solution is deferred authentication: let the player play first, save locally, and offer account creation later as a way to protect their existing progress.
OAuth: Sign In with Existing Accounts
OAuth (specifically OAuth 2.0 and OpenID Connect) lets players sign in with their existing Google, Apple, Discord, GitHub, or other accounts. The player clicks "Sign in with Google," is redirected to Google's consent page, approves the request, and is redirected back to the game with an authorization code. The game's server exchanges this code for an access token and an ID token that contains the player's unique identifier, display name, and email address.
Google is the most widely used OAuth provider because nearly everyone has a Google account. Google Sign-In provides a one-tap button that signs the player in without a redirect if they are already logged into Google in the same browser. The response includes a JWT ID token that the game server verifies and extracts the user's unique Google ID from. Google's client library handles the entire flow in the browser, requiring only a client ID obtained from Google Cloud Console.
Discord is popular for gaming communities. Many gamers already have Discord accounts, and sign-in with Discord feels natural for web games. Discord OAuth provides the player's username, avatar, and a unique Discord user ID. Discord also provides APIs for reading the player's server memberships and friend list, which can power social features like friends-only leaderboards. The Discord OAuth flow requires a redirect, so it is slightly more friction than Google's one-tap.
Apple Sign In is required if your game is distributed through the Apple App Store (as a PWA wrapper, for example). Apple provides a unique user ID and optionally the player's name and email (the player can choose to hide their email). Apple Sign In uses the standard OAuth redirect flow with a JWT response.
Offering multiple OAuth providers gives players choice, but each provider adds integration work and UI complexity. For most indie web games, Google alone covers the majority of users. Adding Discord as a second option captures the gaming audience that prefers Discord as their identity. More than two providers is rarely necessary for games.
Guest Accounts with Upgrade Paths
The guest account pattern provides the best first-time experience. When a new player visits the game, the system generates a random unique ID (a UUID), stores it in LocalStorage, and uses it as the player identifier for local saves and optionally for cloud saves. The player is playing and saving progress within seconds, with no sign-in required.
The guest ID is anonymous: no email, no name, no personal information. Cloud saves tied to a guest ID work normally, but the player has no way to recover their account if they clear their browser data or switch devices. The upgrade prompt appears when the player has invested enough to care: "Sign in to protect your progress" after completing level 5, or "Link an account to play on other devices" after 30 minutes of play. This prompt does not block gameplay, it simply offers the option.
When the player upgrades from a guest account to an OAuth account, the server links the guest ID to the OAuth user ID. All existing saves, leaderboard entries, and other data associated with the guest ID are now accessible via the OAuth account. If the player later clears their browser and signs in with OAuth, they get their progress back. This linking operation is a one-time database update: UPDATE players SET oauth_id = $1, oauth_provider = $2 WHERE guest_id = $3.
Handle the edge case where a player upgrades to an OAuth account that is already linked to a different guest account. This happens when the same person played as a guest on two different browsers and then tries to link the same Google account to both. The system should merge the two accounts (combining progress from both) or let the player choose which guest account to keep. Automatic merging is ideal but requires game-specific logic to define how data from two accounts combines.
JWT Tokens and Session Management
JSON Web Tokens (JWTs) are the standard authentication mechanism for web game APIs. After a successful OAuth sign-in, the game server creates a JWT containing the player's user ID, an expiration timestamp, and a signature. The client stores this token (in memory, not in LocalStorage, for security) and includes it in the Authorization header of every API request. The server verifies the signature and expiration on each request to confirm the player's identity.
JWT expiration should balance security with player convenience. A short expiration (15-30 minutes) is more secure but requires frequent token refreshes. A longer expiration (7-30 days) is more convenient but means a stolen token remains valid for longer. The standard approach is a short-lived access token (15 minutes to 1 hour) paired with a long-lived refresh token (30 days). When the access token expires, the client silently exchanges the refresh token for a new access token without requiring the player to sign in again.
Store the access token in a JavaScript variable (memory only), not in LocalStorage or cookies. Memory storage means the token is lost when the tab closes, but the refresh token (stored as an HTTP-only cookie) allows silent re-authentication on the next visit. This prevents cross-site scripting (XSS) attacks from stealing the access token and limits the damage of a stolen refresh token (which cannot be used from a different origin due to the HTTP-only, SameSite cookie restrictions).
Managed Auth Services
Firebase Authentication is the easiest way to add accounts to a web game. It supports Google, Apple, GitHub, Twitter, Facebook, email/password, phone number, and anonymous sign-in. The JavaScript SDK provides pre-built UI components and handles the entire OAuth flow, token management, and session persistence. Firebase Auth is free for unlimited users (you pay only for other Firebase services like Firestore or Realtime Database). The SDK detects returning users automatically and restores their session on page load.
Supabase Auth is the open-source alternative. It supports Google, Apple, Discord, GitHub, and other OAuth providers, plus email/password and magic link (passwordless) authentication. Supabase Auth integrates directly with its PostgreSQL database through Row Level Security (RLS) policies, which means you can write database queries that automatically filter data by the authenticated user without any server-side middleware. The free tier supports up to 50,000 monthly active users.
Auth0 and Clerk are dedicated authentication services that provide more features than Firebase or Supabase Auth: multi-factor authentication, social login for dozens of providers, user management dashboards, and enterprise SSO. They are overkill for most indie games but relevant for games with regulatory requirements, enterprise partnerships, or very large user bases. Auth0's free tier covers up to 25,000 monthly active users.
Security Considerations for Games
Never trust authentication tokens that come from the client without server-side verification. A player can modify the JavaScript in their browser to send a fake token or someone else's user ID. Every API endpoint must verify the JWT signature on the server using the signing secret or public key. Libraries in every server language handle this: jsonwebtoken for Node.js, PyJWT for Python, and Firebase Admin SDK for Firebase tokens.
Rate limiting protects against brute-force attacks on login endpoints and spam on game APIs. Apply per-IP rate limits on sign-in attempts (5 attempts per minute) and per-user rate limits on game actions (1 save per second, 1 score submission per second). Most API frameworks include rate limiting middleware, and reverse proxies like Cloudflare provide IP-level rate limiting at the network edge.
HTTPS is non-negotiable. All authentication flows, token exchanges, and API calls must happen over HTTPS. Browsers enforce this for OAuth redirect URIs, and modern game hosting (Cloudflare Pages, Vercel, Netlify, S3 with CloudFront) provides HTTPS by default. HTTP connections expose tokens to interception on public networks.
Start with anonymous guest accounts for zero-friction onboarding, offer OAuth upgrade (Google and/or Discord) after the player has invested in the game, and use a managed auth service (Firebase Auth or Supabase Auth) to avoid building authentication infrastructure from scratch. Store access tokens in memory only, verify them server-side on every request, and always use HTTPS.