⏱️ Reading time: 13 min

Every time you tap “Continue with Google” on a new app and never type a password, you’re using OAuth 2.0. The protocol was formalized in RFC 6749 in October 2012, and today it underpins nearly every delegated login on the web.

📑 En este artículo
  1. TL;DR
  2. What is OAuth 2.0?
  3. Why the open authorization standard matters
  4. How the delegated authorization flow works
  5. Practical examples: how to get started with OAuth2
    1. Step 1: build the authorization URL
    2. Step 2: exchange the code for a token
    3. Step 3: call the API with the token
    4. Step 4: verify the granted scope
  6. Real-world use cases
  7. Common mistakes and best practices
  8. Comparison: OAuth2 versus other authorization standards
  9. Going deeper: PKCE and the inner workings of the OAuth2 flow
  10. Frequently Asked Questions
    1. Does OAuth 2.0 completely replace passwords?
    2. What’s the difference between OAuth2 and OpenID Connect?
    3. Is the implicit flow of the open authorization protocol still safe?
    4. What is PKCE and why does the delegated authorization flow need it?
    5. How long does an access token last in OAuth2?
    6. What happens if an OAuth2 refresh token leaks?
  11. References

Understanding it thoroughly avoids two common mistakes among developers: treating it as a login system and copying examples of already deprecated flows. This article explains its roles, its current flows, and the errors that most often break real implementations.

TL;DR

  • OAuth 2.0 separates identity from authorization: an app gets access without ever seeing your password
  • The authorization code flow with PKCE is the current standard for web and mobile apps
  • Access tokens expire in minutes; refresh tokens renew them without asking for login again
  • Confusing OAuth with an authentication protocol is the most common mistake when implementing it
  • RFC 6749 defines four roles: resource owner, client, authorization server, and resource server

What is OAuth 2.0?

OAuth 2.0 is an open authorization protocol that lets an application access a user’s data on another service without ever handling or seeing their password. It works with temporary tokens issued by an authorization server, defined in the IETF’s RFC 6749 since 2012.

The protocol distinguishes four participants: the user who owns the data, the application that wants it, the server that issues the tokens, and the server that accepts them to deliver the resource. Nearly every public API today (GitHub, Spotify, Slack, Google Cloud) implements some variant of this scheme so a third party can gain access without asking for the original password.

Contrary to what many people assume, the authorization protocol does not verify identity on its own. That confusion is so common that it ended up spawning a separate standard, OpenID Connect, built precisely on top of OAuth to solve the “who is this user” problem that OAuth never promised to solve.

OAuth 2.0 was formalized in RFC 6749 in October 2012. Foto de Albert Stoynov en Unsplash

Why the open authorization standard matters

Before this standard, the only way for a third-party application to use your account on another service was to ask for your password directly. That pattern forced the user to trust their full credential to an app that could store it, leak it, or abuse it with no limit on scope.

The authorization protocol solves that problem by separating two things that used to go together: who you are and what an application can do on your behalf. A third-party app receives a token with limited permissions, never the password, and that token can be revoked at any time without having to change the access credential.

That separation enabled the integration ecosystem we take for granted today. When you connect Google Calendar to a productivity app, or authorize a Slack bot to read a channel, the user decides exactly what scope they grant (read-only, a single calendar, no access to contacts), and the application never stores the real password.

It also solved a scaling problem for large platforms. With short-lived, revocable tokens, a provider like GitHub can offer thousands of third-party OAuth Apps without a breach in one of them compromising the user’s full account across other integrations.

How the delegated authorization flow works

The standard defines four participants that interact in every exchange. The resource owner is the user who owns the data. The client is the application that wants to access that data on their behalf. The authorization server verifies the user’s identity and issues the tokens. The resource server is the API that ultimately delivers the data, validating the token before responding.

The following figure summarizes how these four roles relate to each other:

flowchart TD
    U["User (resource owner)"] --> AS["Authorization server"]
    AS -->|"issues code"| C["Client application"]
    C -->|"exchanges code"| AS
    AS -->|"issues access token"| C
    C -->|"uses token"| RS["Resource server (API)"]
    RS -->|"returns data"| C

RFC 6749 defines several “grant types,” each designed for a different kind of application. The most widely used today is the authorization code grant, designed for applications that can keep a secret on the server, or that use PKCE if they can’t. The client credentials grant is for server-to-server communication with no user involved. The device authorization grant, defined later in RFC 8628, solves the case of a smart TV or a console without a comfortable keyboard: the device displays a short code and the user enters it from their phone.

⚠️ Heads up: the implicit flow (response_type=token) returned the access token directly in the browser URL. It ended up exposed in browser history and server logs, so the current revision of the standard discourages it for new projects in favor of the authorization code grant with PKCE.

In the code flow, the sequence is always the same. The application redirects the user to the authorization server with the list of requested scopes. The user approves or denies access from a screen controlled by the provider itself, never by the client application. If they approve, the server redirects back with a single-use authorization code, which the application exchanges for an access token by calling the server directly, without the browser taking part in that step.

sequenceDiagram
    participant U as User
    participant C as Application
    participant AS as Authorization server
    participant RS as Resource server
    C->>U: redirects to login screen with code_challenge
    U->>AS: approves access
    AS-->>C: redirects with authorization code
    C->>AS: exchanges code + code_verifier for token
    AS-->>C: responds with access token
    C->>RS: requests data with the token
    RS-->>C: responds with the resource
    Note over C,AS: the code_verifier prevents an attacker from using an intercepted code

The resulting access token is usually opaque or in JWT format, and it lives for a short time: minutes or hours. Along with it, the server usually issues a longer-lived refresh token, which the application exchanges for a new access token without the user having to approve anything again, until that refresh token also expires or is revoked.

Practical examples: how to get started with OAuth2

To see the full flow in action, all you need is a browser, curl, and a GitHub account. GitHub exposes a standard authorization code flow that serves as a real example of how any OAuth2 integration works in production.

Before writing a single command, you need to register an OAuth App at github.com/settings/developers. That registration gives you a public client_id and a private client_secret, and asks you to declare a redirect_uri (for this example, http://localhost:3000/callback).

Step 1: build the authorization URL

https://github.com/login/oauth/authorize?client_id=Iv1.8a61f9b3a7aba766&redirect_uri=http://localhost:3000/callback&scope=read:user

When you open this URL in the browser, GitHub shows the approval screen. If the user accepts, GitHub redirects to http://localhost:3000/callback?code=1a2b3c4d5e6f7g8h: that code is the single-use authorization code.

Step 2: exchange the code for a token

curl -X POST https://github.com/login/oauth/access_token \
  -H "Accept: application/json" \
  -d client_id=Iv1.8a61f9b3a7aba766 \
  -d client_secret=your_client_secret \
  -d code=1a2b3c4d5e6f7g8h

Expected output:

{"access_token":"gho_16C7e42F292c6912E7710c838347Ae178B4a","token_type":"bearer","scope":"read:user"}

Step 3: call the API with the token

curl -H "Authorization: Bearer gho_16C7e42F292c6912E7710c838347Ae178B4a" https://api.github.com/user

Expected output (simplified):

{"login":"octocat","id":1,"name":"The Octocat"}

Step 4: verify the granted scope

To confirm which permissions were actually authorized for that token, you need to look at the response headers, not just the body:

curl -I -H "Authorization: Bearer gho_16C7e42F292c6912E7710c838347Ae178B4a" https://api.github.com/user

The X-OAuth-Scopes: read:user header in the response confirms that the token has exactly the scope requested, no more and no less.

Tokens issued by GitHub for OAuth Apps carry the gho_ prefix.

Real-world use cases

The most visible case for any user is the “Continue with Google” or “Continue with GitHub” button that appears on thousands of products. That button almost always combines the authorization protocol with OpenID Connect: the first gives the application access to specific data (email, profile), the second confirms the identity of whoever logged in.

Third-party integrations on social networks are another everyday example. An app that schedules posts on X, or a bot that reads messages in a Slack channel, receives a token with specific scopes (post, read a channel) and never sees the account’s password.

In payments, Stripe Connect uses a variant of the same scheme so a marketplace platform receives authorization from each seller to process their charges, without the seller sharing their banking credentials with the marketplace. On devices without a comfortable keyboard, like a smart TV, services such as YouTube use the device authorization grant: the TV displays a short code, the user types it in from their phone, and the token ends up reaching the device without it ever having had direct access to the login form.

Common mistakes and best practices

  • Confusing authorization with authentication: using the access token to decide “who” the user is leaves the developer treating a token as proof of identity, something the standard never guaranteed.
  • Omitting the state parameter: without a random value verified when returning from the authorization server, the application is exposed to CSRF attacks that force the user to authorize an account that isn’t theirs.
  • Not implementing PKCE in public clients: an SPA or mobile app can’t keep a real client_secret, so without code_verifier and code_challenge, an intercepted authorization code is enough to steal the entire token.
  • Storing tokens in localStorage: a token accessible from JavaScript is exposed to any XSS on the page; httpOnly and secure cookies reduce that attack surface.
  • Requesting excessive scopes: asking for full access when the application only needs to read one specific piece of data makes the damage worse in the event of a breach, and also lowers the users’ approval rate.
💡 Tip: if your application is an SPA or a mobile app, always use PKCE. Without it, an intercepted authorization code is enough to steal the access token without needing the client_secret.

Comparison: OAuth2 versus other authorization standards

OptionWhen to use itAdvantageLimitation
OAuth 2.0 (code + PKCE)Web or mobile apps that access third-party APIsShort-lived, revocable tokens, granular scopesDoesn’t authenticate identity on its own
OpenID ConnectLogin or “Continue with X”Adds an identity layer (ID token) on top of OAuth2Depends on the provider’s OAuth2 implementation
API KeysInternal APIs or simple server-to-server integrationsSimplicity, no redirect flowNo expiration or granular scopes; full access if leaked
SAMLTraditional enterprise SSOMature and standard in corporate environmentsHeavy for REST APIs or modern mobile apps

Going deeper: PKCE and the inner workings of the OAuth2 flow

PKCE (Proof Key for Code Exchange, RFC 7636) solves a specific problem for public clients: an SPA or a mobile app can’t keep a client_secret without exposing it in the code. Without that secret, anyone who intercepts the authorization code (for example, via a misconfigured deep link) could exchange it for a token.

The solution is to generate, on the client itself, a random string called code_verifier. Before redirecting the user, the application computes code_challenge = BASE64URL(SHA256(code_verifier)) and sends it along with the authorization request. When it comes time to exchange the code for the token, the application sends the original code_verifier; the server recomputes the hash and compares it against the code_challenge it stored at the start. If they don’t match, it rejects the exchange, even if the attacker has the code.

Another detail that separates mature implementations from basic ones is whether the access token is opaque or a signed JWT. An opaque token forces the resource server to query the authorization server on every request via the introspection endpoint (RFC 7662). A JWT, on the other hand, allows the signature to be validated locally without that extra call, at the cost of not being able to revoke it instantly without a separate revocation list.

stateDiagram-v2
    [*] --> Issued
    Issued --> Active
    Active --> Expired: the useful lifetime minutes pass
    Expired --> Active: the refresh token is used
    Active --> Revoked: the user withdraws access
    Revoked --> [*]
    Expired --> [*]: the refresh token also expired

A more recent development from the ecosystem itself is DPoP (RFC 9449), which binds the access token to a cryptographic key that only the legitimate client holds. That way, stealing the token alone (without that key) is no longer enough to use it elsewhere, something traditional bearer tokens could never prevent.

Your next step: register a free OAuth App at github.com/settings/developers and repeat the four curl commands from this article against your own account to watch the code-for-token exchange happen live.

📬 Get new articles by email

We only email about big articles (1-2 a month).

Frequently Asked Questions

Does OAuth 2.0 completely replace passwords?

No. The user still authenticates with a password (or passkey, or 2FA) directly on the authorization server. What OAuth prevents is that password being shared with the third-party application.

What’s the difference between OAuth2 and OpenID Connect?

OAuth2 authorizes access to data or actions; OpenID Connect is built on top of it to confirm identity, adding a signed ID token that certifies who logged in.

Is the implicit flow of the open authorization protocol still safe?

It’s not recommended for new projects. It returns the token directly in the browser URL, which exposes it in history and logs; the authorization code grant with PKCE replaces it in practically every case.

What is PKCE and why does the delegated authorization flow need it?

It’s an extension (RFC 7636) that protects public clients, like SPAs and mobile apps, which can’t keep a client_secret without exposing it in the source code.

How long does an access token last in OAuth2?

It varies by provider, but it usually ranges from minutes to a few hours. The short lifespan is intentional: it limits the damage if the token leaks.

What happens if an OAuth2 refresh token leaks?

An attacker can generate new access tokens indefinitely until the user or the provider revokes it manually, which is why refresh tokens must be stored with at least as much care as a password.

References

  • RFC 6749: the official IETF specification for OAuth 2.0.
  • RFC 7636: defines PKCE, the extension for public clients.
  • oauth.net/2: the OAuth community’s reference site, maintained by one of the standard’s co-authors.
  • GitHub Docs: the official guide for implementing the authorization flow used in this article’s examples.
  • RFC 8628: defines the device authorization grant used on TVs and consoles.

📱 Like this content? Follow @programacion on Telegram for daily tech content in Spanish: quick summaries, fresh content every day.

Featured image: Foto de Zaqy Al Fattah en Unsplash

Did it work for you? Got a different error? Say so below: questions get answered and help the next reader.

Leave a comment
Categories: SecurityTutorials

Clara Vásquez

Cybersecurity analyst focused on critical vulnerabilities, zero-days, and emerging threats. Covers high-impact CVEs, malware analysis, ransomware incidents, and security trends with a LATAM lens.

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *

You can include code inside <code>…</code> or, for several lines, <pre><code>…</code></pre>.

This site uses Akismet to reduce spam. Learn how your comment data is processed.