Understanding OAuth: Client Types, Flows, and Key Concepts

OAuth is the backbone of modern API security, enabling controlled access to resources without sharing user credentials. At its core, OAuth is all about authenticating to APIs — ensuring that clients (apps, users, or servers) access APIs securely and with the correct permissions.

Client Types: Public vs. Protected

OAuth clients come in two flavors:

  • Public Clients
    Public clients cannot securely store a client secret. Examples include:

    • Single Page Applications (SPAs) running entirely in the browser

    • Mobile apps without a backend server

  • Protected Clients
    Protected clients can securely store a client secret. These typically include:

    • Traditional web applications with server-side backends

    • Backend services

Note:
Some public clients (like mobile apps) can implement workarounds to act like protected clients by leveraging secure storage techniques and backend proxies.


Key Questions to Ask When Designing OAuth Integration

When working with OAuth, these critical questions guide your design:

  • What is the client type?
    Public or Protected? Some mobile apps may straddle the line with additional security layers.

  • What API or resource are you protecting?

    • What is the Resource Server?

    • What is the protected endpoint you’re accessing?

  • What is the required scope?
    Define scopes carefully for both:

    • Access tokens (short-lived API access)

    • Refresh tokens (long-lived re-authentication)

  • Is user interaction required?

    • If yes, you’ll likely need the Authorization Code Flow (e.g., for mobile or web apps).

    • If no, you’ll use the Client Credentials Flow (server-to-server communication).


OAuth Architecture: Resource Server vs Authentication Server

It’s crucial to distinguish between the Authentication Server and the Resource Server:

  • OAuth Authentication Server

    • Authenticates users

    • Issues Access Tokens (and optionally Refresh Tokens)

    • Responsible for Authorization Code and Client Credentials exchanges

  • Resource Server

    • Hosts protected resources (APIs, databases)

    • Acts as a Policy Enforcer, checking that incoming requests present valid access tokens

    • Enforces the authorization policies dictated by the OAuth scopes and claims

Important:
The Resource Server and the Authentication Server are often different services!


OAuth vs OpenID Connect (OIDC)

While OAuth handles authorization to APIs, OIDC (OpenID Connect) adds a layer of authentication:

  • OIDC issues an ID Token to authenticate users across different domains (such as SaaS providers).

  • OAuth enforces API access policies through its Resource Server.

  • With OIDC, the relying party (like a SaaS app) is responsible for enforcing any further policies — similar to how SAML operates.

In simple terms:

  • OAuth = Authorization (API access)

  • OIDC = Authentication (User identity verification)


OAuth Flows: When to Use Which?

Different use cases call for different OAuth flows:

1. Client Credentials Flow (Server-to-Server)

  • Use when: No user interaction is required.

  • Example: One service authenticating to another service automatically (machine-to-machine).

2. Authorization Code Grant Flow (Web and Mobile Apps)

  • Use when: User interaction (login) is required.

  • Example: A web app or mobile app where a user logs in via an authentication server and then accesses APIs.

After authentication:

  • The user receives an authorization code.

  • The client exchanges this code for an access token (and possibly a refresh token).

3. Implicit Grant (Legacy, Browser-Based Apps)

  • Use when: Lightweight browser apps needed fast tokens (though now discouraged in favor of Authorization Code Flow with PKCE).

  • Example: Earlier SPAs without secure server-side token exchange.

When to Use Which? – Detailed Examples

1. Client Credentials Flow (Server-to-Server)

Use when: No user interaction is required. This flow is ideal for backend services communicating with each other, like microservices or scheduled jobs.

How it works:

  1. The client (your backend app) authenticates directly with the Authentication Server using its Client ID and Client Secret.
  2. The Authentication Server verifies the credentials and returns an Access Token.
  3. The client uses the Access Token to access the protected API hosted by the Resource Server.

Example Scenario:

  • An internal reporting service needs to pull data nightly from a protected API.
  • No user is involved — the service itself needs an access token.

Example HTTP Request to Token Endpoint:

POST /oauth2/token HTTP/1.1
Host: auth-server.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
&scope=read:data write:data

Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "read:data write:data"
}

The client then attaches the Access Token as a Bearer token in the Authorization header of API requests.

2. Authorization Code Grant Flow (Web and Mobile Apps)

Use when: User interaction (login) is required — the most common flow for web applications and mobile apps.

How it works:

  1. The user navigates to the app and clicks “Login.”
  2. The app redirects the user to the Authentication Server’s Authorization Endpoint with parameters like client_id, redirect_uri, scope, and response_type=code.
  3. The user authenticates (enters username and password, MFA, etc.).
  4. The Authentication Server redirects the user back to the app with an Authorization Code.
  5. The app exchanges the Authorization Code for an Access Token (and optionally a Refresh Token) by calling the Authentication Server’s Token Endpoint.
  6. The app uses the Access Token to call the protected API.

Example Scenario:

  • A user logs into a dashboard (e.g., a financial app) that needs to pull user-specific data from an API.

Step 1: Redirect User to Authorization Endpoint

GET https://auth-server.example.com/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https%3A%2F%2Fyourapp.example.com%2Fcallback
  &scope=openid profile email read:data
  &state=xyz123

Step 2: User Logs In and is Redirected Back

After login, user is redirected to:

https://yourapp.example.com/callback?code=AUTH_CODE_HERE&state=xyz123

Step 3: Exchange Authorization Code for Access Token

POST /oauth2/token HTTP/1.1
Host: auth-server.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=AUTH_CODE_HERE
&redirect_uri=https%3A%2F%2Fyourapp.example.com%2Fcallback
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET

Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "def50200a45bb9aa...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "openid profile email read:data"
}

The app now uses the Access Token to access protected APIs on behalf of the user!

Summary of Flow Usage

  • Client Credentials Flow: No user involvement. Perfect for machine-to-machine authentication.
  • Authorization Code Grant Flow: User logs in. Suitable for web apps, mobile apps, and any scenario requiring interactive authentication.