Skip to content
IRC-CodingIRC-Coding
OAuth 2.0Authorization Code FlowAccess TokenRefresh TokenAPI Security

OAuth 2.0: Authorization Code Flow & Access Token

Master OAuth 2.0 authorization framework. Learn Authorization Code Flow, Client Credentials, Access and Refresh Token with code examples.

S

schutzgeist

5 min read
OAuth 2.0: Authorization Code Flow & Access Token

OAuth 2.0: Authorization Code, Flows & Access Token

This post is a conceptual guide to the OAuth 2.0 authorization framework – covering flows, tokens, and practical implementation.

In a Nutshell

OAuth 2.0 is a standardized authorization framework that allows third-party applications to access user resources without ever knowing the user’s credentials.

Core Technical Overview

OAuth 2.0 is a widely-adopted authorization protocol for web, desktop, and mobile applications. It enables an application (client) to access resources on behalf of a user from another system (resource server).

Key characteristics:

  • Delegation: Users grant applications specific access rights
  • Token-based: Access tokens replace passwords
  • Role-based: Clear separation of participating actors
  • Standardized: RFC 6749 defines the framework

Participating roles:

  • Resource Owner: The user who owns the resources
  • Client: The application requesting access
  • Authorization Server: Issues and validates tokens
  • Resource Server: Hosts the protected resources

OAuth 2.0 does not convey user identity—it only authorizes access to resources. For identity verification, use OpenID Connect.

Exam-Relevant Key Points

  • Authorization framework, not authentication
  • Roles: Resource Owner, Client, Authorization Server, Resource Server
  • Access Token and optional Refresh Token
  • Authorization Code Flow for web applications
  • Client Credentials Flow for server-to-server communication
  • HTTP-based and supports REST APIs
  • Token-based access improves security
  • Protection against password sharing through delegation mechanism

Core Components

  1. Resource Owner: User or system that owns the resources
  2. Client: Application that needs resource access
  3. Authorization Server: Issues tokens and verifies them
  4. Resource Server: Hosts the protected resources
  5. Access Token: Short-lived token for resource access
  6. Refresh Token: Long-lived token for renewing access tokens
  7. Authorization Code: Temporary code for token exchange
  8. Redirect URI: Target URL after authorization
  9. Scope: Defines the access scope
  10. 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);
        
        // Check scope
        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);
        
        // Verify 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

// Renew token 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 Considerations

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: Centralized 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 refresh
  • State Management: Client must track token state
  • Security Risks: Incorrect implementation can create vulnerabilities

Common Exam Questions

  1. What’s the difference between OAuth 2.0 and OpenID Connect? OAuth 2.0 handles authorization; OpenID Connect adds identity (authentication).

  2. Explain the Authorization Code Flow. The client redirects the user to the Authorization Server, receives a code, and exchanges that code for a token.

  3. When do you use the Client Credentials Flow? For server-to-server communication without user interaction.

  4. What is the purpose of the state parameter? CSRF protection by verifying the request was initiated by the client.

Key References

  1. https://tools.ietf.org/html/rfc6749
  2. https://oauth.net/2/
  3. https://auth0.com/docs/authorization-framework/overview
Back to Blog
Share:

Related Posts