OAuth 2.0: Authorization Code, Flows & Access Token
This article is a glossary entry on the OAuth 2.0 authorization framework – including flows, tokens and practical implementation.
In a Nutshell
OAuth 2.0 is a standardized authorization framework that allows third parties to access user resources without knowing their credentials.
Compact Technical Description
OAuth 2.0 is a widespread authorization protocol for web, desktop and mobile applications. It enables an application (client) to access resources of another system (resource server) on behalf of a user.
Core characteristics:
- Delegation: Users delegate access rights to applications
- Token-based: Access tokens instead of passwords
- Role-based: Clear separation of involved actors
- Standardized: RFC 6749 defines the framework
Involved roles:
- Resource Owner: User who owns the resources
- Client: Application that wants to access resources
- Authorization Server: Issues tokens and validates them
- Resource Server: Provides the protected resources
OAuth 2.0 does not transmit user identity, but merely authorizes access to resources. For identity verification, there is OpenID Connect.
Exam-Relevant Key Points
- Authorization framework, not authentication
- Roles: Resource Owner, Client, Authorization Server, Resource Server
- Access Token and optionally Refresh Token
- Authorization Code Flow for web applications
- Client Credentials Flow for server-to-server communication
- HTTP-based and supports REST APIs (relevant for IHK)
- Token-based access increases security
- Protection against password sharing through delegation mechanism
Core Components
- Resource Owner: User or system that owns the resources
- Client: Application that needs access to resources
- Authorization Server: Issues tokens and verifies them
- Resource Server: Hosts the protected resources
- Access Token: Short-lived token for resource access
- Refresh Token: Long-lived token for token renewal
- Authorization Code: Temporary code for token exchange
- Redirect URI: Target URL after authorization
- Scope: Defines the access scope
- Grant Type: Determines the OAuth 2.0 flow
OAuth 2.0 Flows
1. Authorization Code Flow (for Web Apps)
1. Client → User: Redirect to Authorization Server
2. User → Authorization Server: Login & consent
3. Authorization Server → Client: Authorization Code
4. Client → Authorization Server: Exchange code for Access Token
5. Authorization Server → Client: Access Token + Refresh Token
6. Client → Resource Server: Request resources with Access Token
2. Client Credentials Flow (for Server-to-Server)
1. Client → Authorization Server: Send Client Credentials
2. Authorization Server → Client: Access Token
3. Client → Resource Server: Request resources with Access Token
3. Implicit Flow (deprecated, for Single-Page Apps)
1. Client → User: Redirect to Authorization Server
2. User → Authorization Server: Login & consent
3. Authorization Server → Client: Access Token directly in fragment
Practical Examples
Authorization Code Flow Implementation
// Frontend: Request authorization
const authUrl = 'https://auth.example.com/authorize';
const params = new URLSearchParams({
response_type: 'code',
client_id: 'your_client_id',
redirect_uri: 'https://yourapp.com/callback',
scope: 'read:profile write:data',
state: generateRandomString() // CSRF protection
});
window.location.href = `${authUrl}?${params}`;
// Callback handler
async function handleCallback(code, state) {
// Validate state
if (state !== getStoredState()) {
throw new Error('Invalid state - CSRF attack detected');
}
// Exchange code for token
const tokenResponse = await fetch('https://auth.example.com/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
client_id: 'your_client_id',
client_secret: 'your_client_secret',
redirect_uri: 'https://yourapp.com/callback'
})
});
const tokens = await tokenResponse.json();
localStorage.setItem('access_token', tokens.access_token);
localStorage.setItem('refresh_token', tokens.refresh_token);
}
Backend: Token Validation
// Spring Boot Example
@RestController
@RequestMapping("/api")
public class ApiController {
@GetMapping("/profile")
public ResponseEntity<UserProfile> getProfile(
@RequestHeader("Authorization") String authHeader) {
// Extract and validate token
String token = authHeader.replace("Bearer ", "");
if (!tokenValidator.isValid(token)) {
return ResponseEntity.status(401).build();
}
// Extract token claims
Claims claims = tokenValidator.getClaims(token);
String userId = claims.getSubject();
List<String> scopes = claims.get("scope", List.class);
// Scope check
if (!scopes.contains("read:profile")) {
return ResponseEntity.status(403).build();
}
UserProfile profile = userService.getProfile(userId);
return ResponseEntity.ok(profile);
}
@PostMapping("/data")
public ResponseEntity<?> createData(
@RequestHeader("Authorization") String authHeader,
@RequestBody DataRequest request) {
String token = authHeader.replace("Bearer ", "");
if (!tokenValidator.isValid(token)) {
return ResponseEntity.status(401).build();
}
Claims claims = tokenValidator.getClaims(token);
List<String> scopes = claims.get("scope", List.class);
// Check write permission
if (!scopes.contains("write:data")) {
return ResponseEntity.status(403).build();
}
Data data = dataService.create(request, claims.getSubject());
return ResponseEntity.ok(data);
}
}
Client Credentials Flow
# Python example for server-to-server communication
import requests
def get_access_token():
token_url = "https://auth.example.com/token"
data = {
'grant_type': 'client_credentials',
'client_id': 'your_client_id',
'client_secret': 'your_client_secret',
'scope': 'api:read api:write'
}
response = requests.post(token_url, data=data)
response.raise_for_status()
token_data = response.json()
return token_data['access_token']
def api_call():
access_token = get_access_token()
headers = {
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json'
}
response = requests.get(
'https://api.example.com/data',
headers=headers
)
return response.json()
Refresh Token Flow
// Token renewal with Refresh Token
async function refreshAccessToken() {
const refreshToken = localStorage.getItem('refresh_token');
if (!refreshToken) {
throw new Error('No refresh token available');
}
try {
const response = await fetch('https://auth.example.com/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_id: 'your_client_id',
client_secret: 'your_client_secret'
})
});
if (!response.ok) {
throw new Error('Token refresh failed');
}
const tokens = await response.json();
localStorage.setItem('access_token', tokens.access_token);
if (tokens.refresh_token) {
localStorage.setItem('refresh_token', tokens.refresh_token);
}
return tokens.access_token;
} catch (error) {
// Refresh token invalid - re-login required
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
redirectToLogin();
}
}
Security Notes
State Parameter (CSRF Protection)
function generateState() {
return Math.random().toString(36).substring(2, 15) +
Math.random().toString(36).substring(2, 15);
}
function storeState(state) {
sessionStorage.setItem('oauth_state', state);
}
function validateState(receivedState) {
const storedState = sessionStorage.getItem('oauth_state');
sessionStorage.removeItem('oauth_state');
return storedState === receivedState;
}
PKCE (Proof Key for Code Exchange)
// PKCE for mobile/single-page apps
function generatePKCE() {
const codeVerifier = generateRandomString(128);
const codeChallenge = base64urlEncode(sha256(codeVerifier));
return { codeVerifier, codeChallenge };
}
// Authorization request with PKCE
const params = new URLSearchParams({
response_type: 'code',
client_id: 'your_client_id',
code_challenge: pkce.codeChallenge,
code_challenge_method: 'S256',
redirect_uri: 'https://yourapp.com/callback'
});
Advantages and Disadvantages
Advantages
- Security: No password sharing with third parties
- Standardization: Widely adopted and well documented
- Flexibility: Different flows for different use cases
- Scalability: Central authorization for many services
- Revocation: Tokens can be revoked at any time
Disadvantages
- Complexity: More steps than simple authentication
- Performance: Additional network calls for token renewal
- State Management: Client must manage token state
- Security Risks: Incorrect implementation can create security gaps
Frequently Asked Exam Questions
-
What is the difference between OAuth 2.0 and OpenID Connect? OAuth 2.0 is for authorization, OpenID Connect adds identity (authentication).
-
Explain the Authorization Code Flow! Client redirects user to Authorization Server, receives code, exchanges code for token.
-
When do you use Client Credentials Flow? For server-to-server communication without user interaction.
-
What is the purpose of the State Parameter? CSRF protection by verifying that the request was initiated by the client.
Most Important Sources
- https://tools.ietf.org/html/rfc6749
- https://oauth.net/2/
- https://auth0.com/docs/authorization-framework/overview