OAuth 2.0 is the industry standard for authorisation, powering "Sign in with Google", API access tokens, and third-party integrations across the web. Despite its ubiquity, OAuth is frequently implemented incorrectly, creating security vulnerabilities. This guide covers the core concepts, common flows, and security practices you need to implement OAuth properly.
Understanding OAuth 2.0 Roles
OAuth 2.0 defines four roles that interact during the authorisation process. The Resource Owner is typically the end user who owns the data. The Client is your application that wants to access the user's data. The Authorization Server handles user authentication and issues tokens (Google, GitHub, or your own server). The Resource Server hosts the protected data and validates access tokens.
A crucial distinction: OAuth 2.0 is an authorisation framework, not an authentication protocol. It determines what a client can access, not who the user is. For authentication (verifying identity), use OpenID Connect (OIDC), which is a thin identity layer built on top of OAuth 2.0. OIDC adds an ID token containing user identity claims, which is what you actually need for "Sign in with" functionality.
Authorization Code Flow with PKCE
The Authorization Code flow with PKCE (Proof Key for Code Exchange) is the recommended flow for nearly all applications — web apps, single-page applications, and mobile apps. It works by redirecting the user to the authorization server, receiving a short-lived authorization code, then exchanging that code for access and refresh tokens via a back-channel request.
The flow step by step:
- Generate PKCE values: Create a random code verifier and derive a code challenge from it using SHA-256. This prevents authorization code interception attacks.
- Redirect to authorize: Send the user to the authorization server with your client ID, requested scopes, redirect URI, and code challenge.
- Receive the code: After the user authenticates and consents, the authorization server redirects back to your app with an authorization code.
- Exchange for tokens: Your server sends the code and the original code verifier to the token endpoint, receiving access and refresh tokens.
Token Management
Access tokens should be short-lived — typically 15 minutes to 1 hour. When an access token expires, use the refresh token to obtain a new one without requiring user interaction. Refresh tokens should be stored securely server-side and rotated on each use (one-time-use refresh tokens) to limit the damage if one is compromised.
For server-rendered web applications, store tokens in encrypted HTTP-only cookies. Never store tokens in localStorage or sessionStorage where they are accessible to JavaScript and vulnerable to XSS attacks. For SPAs that cannot use a backend-for-frontend pattern, consider using the token handler pattern where a lightweight backend manages token storage and proxies API requests.
Security Considerations
Always validate the state parameter to prevent CSRF attacks. Use exact redirect URI matching — never accept wildcard or partial matches. Request only the scopes your application actually needs (principle of least privilege). Validate ID tokens by checking the signature, issuer, audience, and expiration claims.
Never use the Implicit flow for new applications. While historically recommended for SPAs, it exposes tokens in the URL fragment and has been deprecated in favour of Authorization Code with PKCE. Similarly, the Resource Owner Password Credentials flow should be avoided as it requires your application to handle user credentials directly.
Implementation Libraries
Do not implement OAuth from scratch. Use well-maintained libraries that handle the security-critical details correctly. For Node.js, consider Passport.js with the appropriate strategy, or the newer Auth.js (formerly NextAuth.js) for Next.js applications. For backend APIs, libraries like jose for JWT validation handle the cryptographic operations safely.
At Born Digital, we implement OAuth flows across a wide range of projects — from social login on eCommerce sites to API authentication for B2B platforms. The key takeaway is to rely on proven libraries, follow the latest security recommendations (OAuth 2.1 consolidates many of these), and keep token handling on the server side whenever possible. The complexity of OAuth is in the details, and getting those details wrong creates real security risks.