Definition
- OAuth2 – an authorization framework that lets a third-party application obtain limited access to a user's resources on another service without the user sharing their password.
- Delegated authorization – the user delegates limited, scoped, time-limited access rather than handing over credentials.
- Resource Owner – the human who owns the data and can grant access.
- Client – the application requesting access, registered with
client_idand optionallyclient_secret. - Authorization Server – authenticates the owner, shows consent, and issues tokens.
- Resource Server – hosts the protected APIs and validates tokens and scopes.
Rajesh wants a budgeting app to read his National Bank of India transaction history. He does not give the app his net-banking password. Instead the app sends him to the bank's own login page, Rajesh signs in there and approves 'allow this app to read my transactions,' and the bank hands the app a limited pass that only reads transactions and expires soon. Rajesh is the Resource Owner, the budgeting app is the Client, the bank's login system is the Authorization Server, and the bank's transactions API is the Resource Server.
Understanding the Concepts
What is OAuth2?
OAuth2 is a framework for delegated authorization. It lets a third-party application access a user's resources on another service without the user ever sharing their password with that application. Instead, the user authorizes the app at a trusted login page, and the app receives a scoped, time-limited access token.
The key word is delegated: the user delegates limited access, not their identity or their password.
package com.bank.oauth2;
// OAuth2 flow summary:
// 1. Budgeting app redirects Rajesh to National Bank's auth page
// 2. Rajesh logs in at the bank and approves "read transactions"
// 3. Bank issues an access token with scope=transactions:read
// 4. Budgeting app uses the token to call the bank's API
// 5. Bank API checks the token and scope, then returns data
// At no point does the budgeting app see Rajesh's password!
What are the four roles?
OAuth2 defines four distinct roles. Being able to name them cleanly is what interviewers look for.
The Resource Owner is the human who owns the data and can grant access. In the banking example, this is Rajesh.
The Client is the application that wants access on the owner's behalf. It is registered with the Authorization Server and has a client_id (and usually a client_secret for confidential clients). In the example, this is the budgeting app.
The Authorization Server authenticates the owner, shows a consent screen, and issues tokens at its token endpoint. In the example, this is the bank's login and token system.
The Resource Server hosts the protected APIs and validates the access token (checking signature or introspection and scopes) before serving data. In the example, this is the bank's transactions API.
package com.bank.oauth2;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// Resource Server configuration
@Configuration
public class ResourceServerConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/transactions/**")
.hasAuthority("SCOPE_transactions:read")
.requestMatchers("/api/v1/transfers/**")
.hasAuthority("SCOPE_transfers:write")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> {}));
return http.build();
}
}
Budgeting app with scope=transactions:read:
GET /api/v1/transactions --> 200 OK (scope matches)
POST /api/v1/transfers --> 403 Forbidden (scope missing)
How does scoped access work?
The client requests specific scopes like transactions:read, and the issued access token encodes exactly those scopes. The Resource Server enforces scopes on each endpoint, so the app can only do what the user authorized.
Tokens expire, so access is time-limited, and the owner can revoke consent at any time.
package com.bank.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@RestController
public class TransactionController {
@GetMapping("/api/v1/transactions")
@PreAuthorize("hasAuthority('SCOPE_transactions:read')")
public List<Map<String, String>> getTransactions() {
return List.of(
Map.of("date", "2024-10-25", "desc", "EMI Payment",
"amount", "-15000.00", "balance", "125000.75"),
Map.of("date", "2024-10-26", "desc", "Salary Credit",
"amount", "+85000.00", "balance", "210000.75")
);
}
}
Why not just share the password?
Sharing the password gives the app full, unlimited access to everything. It cannot be scoped, time-limited, or revoked without changing the password (which breaks everything else).
OAuth2 solves all three problems: tokens are scoped to specific permissions, they expire after a set time, and they can be revoked independently without affecting the user's password.
Password sharing (dangerous):
- App has FULL access to everything
- Cannot scope: reads, writes, deletes all possible
- Cannot revoke without changing password
- Cannot audit which app did what
- App stores your password (if breached, ALL apps compromised)
OAuth2 token (safe):
- App has LIMITED access (scope=transactions:read only)
- Time-limited (expires in 1 hour)
- Revocable (bank revokes token, app loses access)
- Auditable (bank logs which token accessed what)
- App never sees the password
How is this different from API keys?
| Aspect | OAuth2 Token | API Key |
|---|---|---|
| Delegation | User delegates to app | Usually issued to the developer directly |
| Scope | Fine-grained per-token scopes | Usually broad, all-or-nothing |
| Expiry | Short-lived (minutes to hours) | Often long-lived or never-expiring |
| Revocation | Per-token, per-user | Per-key (affects all users) |
| User consent | Explicit consent screen | No user involvement |
| Best for | Third-party app access | Internal service authentication |
package com.bank.security;
// API key: simple but no delegation, no user consent
public class ApiKeyAuth {
public boolean validate(String apiKey) {
return "BANK_API_KEY_123".equals(apiKey); // all-or-nothing
}
}
// OAuth2: delegated, scoped, user-consented
public class OAuth2Auth {
public boolean validate(String token, String requiredScope) {
Claims claims = jwtParser.parse(token).getPayload();
List<String> scopes = claims.get("scope", List.class);
return scopes.contains(requiredScope); // fine-grained
}
}
What is the difference between confidential and public clients?
A confidential client is a server-side web app that can safely store a client secret. It uses the Authorization Code flow with the secret to exchange codes for tokens.
A public client is a single-page app or mobile app that cannot safely store a secret (it can be decompiled or inspected). It uses the Authorization Code flow with PKCE instead of a client secret.
Confidential client (server-side web app):
- Has client_id + client_secret
- Secret stored securely on the server
- Uses Authorization Code flow
- Backend exchanges code + secret for token
Public client (SPA or mobile app):
- Has client_id only (no secret)
- Cannot safely store a secret
- Uses Authorization Code flow + PKCE
- Code verifier replaces the secret
What are the common OAuth2 flows?
OAuth2 defines several grant types (flows) for different scenarios:
- Authorization Code – the recommended flow for web apps with a backend; code exchanged server-to-server.
- Authorization Code + PKCE – for public clients (SPAs, mobile apps) that cannot store a secret.
- Client Credentials – for machine-to-machine authentication with no user involved.
- Implicit (deprecated) – returned token directly in the browser; now replaced by Auth Code + PKCE.
- Resource Owner Password (deprecated) – user gives password to the client; defeats the purpose of OAuth2.
// Each flow has a different grant_type:
// Authorization Code: grant_type=authorization_code
// Client Credentials: grant_type=client_credentials
// Refresh Token: grant_type=refresh_token
// PKCE: same as Auth Code but with code_verifier/code_challenge
What should a senior engineer know?
Senior engineers know that OAuth2 is about authorization, not authentication. It answers what the client may do, not who the user is. Services that need to know the user's identity layer OpenID Connect on top, which adds an ID token.
Senior engineers also know that the access token is opaque to the client's business logic: the client should not parse it or make assumptions about its format. Only the Resource Server validates it.
Visual Overview
OAuth2 Four Roles:
┌──────────────────┐
│ Resource Owner │ (Rajesh -- owns the data)
│ "Allow read?" │
└────────┬─────────┘
│ consent
▼
┌──────────────────┐ ┌──────────────────┐
│ Client │──────►│ Authorization │
│ (Budgeting App) │ code │ Server (Bank) │
│ │◄──────│ Issues token │
│ client_id + │ token │ scope=txn:read │
│ client_secret │ └──────────────────┘
└────────┬─────────┘
│ Bearer token
▼
┌──────────────────┐
│ Resource Server │ (Bank Transactions API)
│ Validates token │
│ Checks scope │
│ Returns data │
└──────────────────┘
Why Tokens Beat Password Sharing: ┌─────────────────┐ ┌─────────────────┐ │ Password Sharing│ │ OAuth2 Token │ │ │ │ │ │ Full access ✗ │ │ Scoped access ✓ │ │ No expiry ✗ │ │ Time-limited ✓ │ │ No revoke ✗ │ │ Revocable ✓ │ │ No audit ✗ │ │ Auditable ✓ │ │ Secret shared ✗ │ │ No password ✓ │ └─────────────────┘ └─────────────────┘
Key Takeaway
OAuth2 is an authorization framework that lets a third-party application access a user's resources on another service without the user ever sharing their password with that application. Instead of handing over credentials, the user authorizes the app at a trusted login page, and the app receives a scoped, expiring access token. The key word is delegated authorization: the user delegates limited access, not their identity or their password.
OAuth2 defines four roles, and being able to name them cleanly is what interviewers look for. The Resource Owner is the user who owns the data. The Client is the application requesting access on the user's behalf. The Authorization Server authenticates the owner, shows a consent screen, and issues tokens. The Resource Server hosts the protected APIs and validates the token and its scopes before serving data.
In the banking example, a budgeting app wants to read Rajesh's transactions from National Bank of India. Rajesh logs in at the bank's own Authorization Server and consents to transactions:read only. The app receives a token limited to that scope, so when it reads transactions the Resource Server allows it, but when it tries to transfer money the Resource Server refuses because the token lacks the write scope. Rajesh never gave the app his password, and he can revoke consent at any time.
The nuance that separates a senior answer is that OAuth2 is about authorization, not authentication. It says what a client may do, not who the user is. When a service also needs the user's identity, OpenID Connect is layered on top, adding an ID token. And because a server-side app can keep a client secret but a single-page or mobile app cannot, the choice of flow and extra protections like PKCE depends on whether the client is confidential or public.
Remember one sentence: OAuth2 lets apps access your data with a scoped, revocable token instead of your password, keeping the password out of third-party hands entirely.
---