Every time a user clicks "Sign in with Google" or an application accesses your data on behalf of another service, OAuth 2.0 is working behind the scenes. It is the most widely used authorization framework on the internet, powering billions of authentication flows daily. Yet it is also one of the most misunderstood protocols in software development.
This guide explains OAuth 2.0 and its companion protocol OpenID Connect in clear, practical terms — what problem they solve, how they work, which flow to use for which scenario, and how to implement them securely.
OAuth 2.0: Authorization, Not Authentication
A critical distinction that many developers miss: OAuth 2.0 is an authorization protocol, not an authentication protocol. It answers the question "What is this application allowed to do?" not "Who is this user?" When you authorize a fitness app to read your Google Calendar, OAuth allows the fitness app to access your calendar data without ever sharing your Google password. The fitness app knows it has calendar access, but OAuth alone does not tell it who you are.
This is why OpenID Connect (OIDC) exists — it is a thin identity layer on top of OAuth 2.0 that adds authentication. OIDC introduces the ID token, which contains user identity information (name, email, profile picture). When you "Sign in with Google," you are using OIDC for authentication and OAuth for authorization in a single flow.
The Key Players
Four roles participate in an OAuth flow. The Resource Owner is the user who owns the data and grants permission. The Client is the application requesting access (your web app, mobile app, or server). The Authorization Server is the service that authenticates the user and issues tokens (Google, GitHub, your own Okta instance). The Resource Server is the API that hosts the protected data and accepts tokens.
Understanding these roles clarifies the flow: the Client asks the Resource Owner for permission. The Resource Owner authenticates with the Authorization Server and grants permission. The Authorization Server issues tokens to the Client. The Client presents tokens to the Resource Server to access protected resources.
Authorization Code Flow: The Standard for Web Applications
The Authorization Code flow is the most secure and most commonly used OAuth flow. It is the recommended flow for server-side web applications and single-page applications (with PKCE). The flow involves redirecting the user to the authorization server's login page, the user authenticating and granting permission, the authorization server redirecting back with an authorization code, and the client exchanging the code for tokens in a back-channel (server-to-server) request.
The authorization code itself is short-lived and can only be used once. The token exchange happens server-to-server, keeping tokens out of the browser's URL bar and history. This is what makes the Authorization Code flow more secure than the Implicit flow (which is now deprecated for most use cases).
PKCE: Essential Security for Public Clients
PKCE (Proof Key for Code Exchange, pronounced "pixy") is an extension that protects against authorization code interception attacks. It is required for mobile apps and single-page applications and recommended for all clients. The client generates a random code verifier and its SHA-256 hash (code challenge). The code challenge is sent with the authorization request. The code verifier is sent with the token exchange request. The authorization server verifies that the verifier matches the challenge, proving the same client that started the flow is completing it.
Tokens: Access, Refresh, and ID
Access tokens authorize API requests. They are typically JWTs with a short lifetime (15 minutes to 1 hour). Include them in API requests via the Authorization header. Never store them in localStorage (vulnerable to XSS attacks) — use httpOnly cookies or in-memory storage for browser-based applications.
Refresh tokens obtain new access tokens without user interaction. They have longer lifetimes (days to months) but must be stored securely. Implement refresh token rotation — issue a new refresh token with each use and invalidate the old one. If a stolen refresh token is used, the rotation detects the theft when the legitimate client tries to use the already-rotated token.
ID tokens are JWTs containing user identity claims — name, email, subject identifier, issuer, and expiration. They are issued by the authorization server during the OIDC flow and should be validated on the client side for signature (verify it was issued by the expected authorization server), expiration, audience (verify it was issued for your application), and issuer (verify it came from the expected provider).
Scopes: Defining Permission Boundaries
Scopes define what the client can access. When you authorize a third-party app, the consent screen shows the scopes it is requesting: "This app wants to read your email," "Access your calendar," "View your profile." Define the minimum scopes your application needs. Never request more access than necessary — users are more likely to deny broad permission requests, and excessive permissions increase the damage from a compromised token.
Security Best Practices
Always use HTTPS for all OAuth endpoints and redirect URIs. Validate the state parameter to prevent CSRF attacks — generate a random value, store it in the session, and verify it matches when the authorization server redirects back. Use exact redirect URI matching, not pattern matching, to prevent open redirect vulnerabilities. Validate all tokens on the server side — never trust client-side token validation for authorization decisions.
Implement token revocation so users can disconnect applications they no longer trust. Handle token errors gracefully — expired tokens should trigger a transparent refresh flow, not an error page. Log authentication events for audit trails and anomaly detection.
Implementing Social Login
Social login (Sign in with Google, GitHub, Apple) uses OAuth 2.0 and OIDC to authenticate users without requiring them to create a new account. Most identity libraries (NextAuth.js, Passport.js, Auth.js) provide pre-built integrations with major providers. Use these libraries instead of implementing the OAuth flow manually — they handle token management, session management, PKCE, state validation, and security edge cases that are easy to get wrong in a custom implementation.
When implementing social login, always request an email address and verify it before linking to an existing account. Handle the case where a user has accounts with multiple providers but the same email address. And provide an account recovery mechanism for users who lose access to their social login provider.
ZeonEdge implements secure authentication systems using OAuth 2.0 and OpenID Connect for web and mobile applications. Learn more about our authentication solutions.
Priya Sharma
Full-Stack Developer and open-source contributor with a passion for performance and developer experience.