What Is OAuth and How Does It Work?

OAuth is an open standard that lets one application access a user's data in another service without ever handling that user's password. When you click "Sign in with Google" or let an app post to your calendar, OAuth is the protocol doing the work behind the scenes.

What OAuth actually is

OAuth 2.0 is an authorization framework, defined in RFC 6749. Its job is delegation: the resource owner (a user) grants a third-party application limited permission to act on their behalf against a protected resource (an API), without sharing credentials. The application receives a short-lived access token instead of a password.

A common point of confusion is treating OAuth as a login system. By itself, OAuth answers "is this app allowed to do X?" — not "who is this person?". Authentication is layered on top by OpenID Connect (OIDC), an identity layer built on OAuth 2.0 that adds an ID token describing the user. "Sign in with Google" is really OIDC plus OAuth working together.

The four roles

Every OAuth interaction involves four parties. Keeping them straight makes the rest of the protocol easy to follow.

  • Resource owner — the user who owns the data and grants access.
  • Client — the application requesting access (a web app, mobile app, or backend service).
  • Authorization server — issues tokens after the user consents (for example, Google's or GitHub's OAuth server).
  • Resource server — the API that holds the protected data and accepts access tokens.

The authorization server and resource server are often run by the same provider, but the spec treats them as distinct so they can scale independently.

How the authorization code flow works

The authorization code flow is the most widely used and most secure of the OAuth grant types for apps that can interact with a user's browser. The key idea is that the sensitive token exchange happens over a direct back-channel, not through the browser's address bar.

  1. The client redirects the user to the authorization server with its client_id, requested scope, a redirect_uri, a state value, and response_type=code.
  2. The user authenticates with the provider and approves the requested scopes on a consent screen.
  3. The authorization server redirects back to the client's redirect_uri with a short-lived authorization code.
  4. The client exchanges that code — along with its client secret — directly with the authorization server's token endpoint.
  5. The authorization server returns an access token (and often a refresh token).
  6. The client calls the resource server, sending the access token in an Authorization: Bearer <token> header.

Because the authorization code is exchanged server-to-server, the access token never appears in a URL or browser history. The state parameter is echoed back unchanged so the client can detect cross-site request forgery on the callback.

PKCE for public clients

Single-page apps and mobile apps cannot keep a client secret confidential, so they use PKCE (Proof Key for Code Exchange, RFC 7636). The client generates a random code_verifier, sends its SHA-256 hash as the code_challenge on the initial request, then presents the original verifier when exchanging the code. An attacker who intercepts the authorization code cannot use it without the matching verifier. PKCE is now recommended for confidential clients too. You can experiment with the hashing step using a hash generator, and decode the URL-encoded callback parameters with a URL encoder/decoder.

Tokens, scopes, and refresh

Scopes are space-delimited strings that bound what the token can do — for example read:user or repo. Requesting the narrowest scope a feature needs is the practical form of least privilege in OAuth.

Access tokens are deliberately short-lived (minutes to an hour). When one expires, the client uses a refresh token to obtain a new access token without prompting the user again. Refresh tokens are long-lived and highly sensitive, so they must be stored securely and can be revoked by the authorization server at any time.

The access token's format is not fixed by the OAuth spec. Many providers issue a JWT (JSON Web Token), which encodes claims like the issuer, audience, scopes, and expiry. A JWT is signed but, unless encrypted, is readable by anyone — never put secrets in one. To inspect a token's claims, paste it into a JWT decoder; for the differences between bearer tokens and signed JWTs, see OAuth 2.0 vs JWT and JWT tokens explained.

Why OAuth matters

Before OAuth, sharing data between services often meant handing over your actual username and password — the "password anti-pattern." That gave the third party full, unrevokable access to everything. OAuth replaced this with scoped, revocable tokens, which is why it underpins nearly every modern API integration and is the foundation that APIs rely on for delegated access.

  • Users never expose their password to third-party apps.
  • Access is scoped to specific permissions, not all-or-nothing.
  • Tokens can be revoked individually without changing the password.
  • Each integration gets its own credentials, so a breach is contained.

Common pitfalls

OAuth is secure when implemented correctly, but several mistakes recur often enough to be worth memorizing.

  • Skipping the state parameter. Without it, the callback is vulnerable to CSRF. Always generate state, store it, and verify it on return.
  • Loose redirect URI matching. Authorization servers must match redirect_uri exactly. Wildcards or open redirects let attackers steal authorization codes.
  • Using the implicit flow. The legacy implicit grant returned tokens directly in the URL fragment and is now discouraged; use the authorization code flow with PKCE instead.
  • Not running over TLS. Tokens and codes in transit are only safe over HTTPS. The spec mandates TLS for the token endpoint, and the OAuth security best practices call for it on the redirect endpoint too.
  • Treating the access token as identity. An access token says what the bearer may do, not who they are. Use OIDC ID tokens for authentication, and always validate a JWT's signature, issuer, audience, and expiry before trusting its claims.
  • Storing tokens carelessly. Refresh tokens in particular should never sit in client-side storage where scripts can read them; prefer secure, server-side or HttpOnly cookie storage.

When to use it

Reach for OAuth whenever a user needs to grant your application access to their account on another service, or when you want to offload sign-in to an established identity provider. For pure server-to-server access with no user involved, the client credentials grant — where the app authenticates as itself — is the right variant. If you only need to verify who a user is for login, use OpenID Connect on top of OAuth rather than rolling your own session scheme.

Frequently Asked Questions

Not exactly. OAuth handles authorization (granting access to data), while authentication (proving who you are) is added by OpenID Connect, a layer built on top of OAuth. "Sign in with Google" uses both together.

OAuth is a framework for delegating access, and a JWT is one possible format for the tokens it issues. OAuth defines the flow; a JWT is a self-contained, signed token that can carry the claims an access or ID token needs.

The code is exchanged for a token over a direct server-to-server request, so the token never appears in the browser URL or history. This keeps access tokens out of reach of scripts, logs, and referrer headers.

PKCE (Proof Key for Code Exchange) protects the authorization code from interception by binding it to a random secret only the client knows. It is required for public clients like mobile and single-page apps and recommended for all clients.

An access token is short-lived and sent with each API request to prove permission. A refresh token is long-lived and used only to obtain new access tokens when they expire, so it must be stored securely and can be revoked.