/api/oauth/token

POST OAuth 2.0 Token Exchange Endpoint

Exchanges authorization code for access and refresh tokens.

Overview

The token endpoint is used to exchange an authorization code (received from the authorize endpoint) for access and refresh tokens. It validates the authorization code, PKCE verifier, and issues JWT tokens.

Endpoint URL
POST https://hub.regardingwork.com/api/oauth/token
Content-Type
application/json or application/x-www-form-urlencoded

Request Parameters

Parameter Type Required Description
grant_type string ✅ Yes Must be "authorization_code"
code string ✅ Yes Authorization code from /oauth/authorize callback
redirect_uri string ✅ Yes Must match the redirect_uri from authorization request
client_id string ❌ No Client identifier (optional, for tracking)
code_verifier string 🔒 Required if PKCE PKCE code verifier that matches the challenge

Request Examples

JSON Request
POST /api/oauth/token
Content-Type: application/json

{
  "grant_type": "authorization_code",
  "code": "abc123-authorization-code",
  "redirect_uri": "https://ce.regardingwork.com/api/auth/callback",
  "code_verifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
}
Form Data Request
POST /api/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=abc123-authorization-code&
redirect_uri=https%3A//ce.regardingwork.com/api/auth/callback&
code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

Success Response

HTTP 200 OK
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 86400,
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "scope": "profile email",
  "user": {
    "id": 12,
    "username": "janechen",
    "email": "jane@example.com"
  }
}
Response Fields
  • access_token - JWT access token (24 hour expiry)
  • token_type - Always "Bearer"
  • expires_in - Token expiry in seconds (86400 = 24 hours)
  • refresh_token - JWT refresh token (30 day expiry)
  • scope - Granted scopes
  • user - User information object

Error Responses

Invalid Grant Type

HTTP 400 Bad Request

{
  "error": "unsupported_grant_type",
  "error_description": "Only authorization code grant is supported"
}
Invalid Authorization Code

HTTP 400 Bad Request

{
  "error": "invalid_grant",
  "error_description": "Invalid or expired authorization code"
}
Redirect URI Mismatch

HTTP 400 Bad Request

{
  "error": "invalid_grant",
  "error_description": "Redirect URI mismatch"
}
Invalid PKCE Verifier

HTTP 400 Bad Request

{
  "error": "invalid_grant",
  "error_description": "Invalid code verifier"
}

Implementation Examples

// Exchange authorization code for tokens
async function exchangeCodeForTokens(code, state) {
    // Retrieve stored PKCE verifier and state
    const codeVerifier = sessionStorage.getItem('oauth_code_verifier');
    const storedState = sessionStorage.getItem('oauth_state');
    
    // Validate state parameter
    if (state !== storedState) {
        throw new Error('Invalid state parameter - potential CSRF attack');
    }
    
    try {
        const response = await fetch('https://hub.regardingwork.com/api/oauth/token', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                grant_type: 'authorization_code',
                code: code,
                redirect_uri: 'https://ce.regardingwork.com/api/auth/callback',
                code_verifier: codeVerifier
            })
        });
        
        if (!response.ok) {
            const error = await response.json();
            throw new Error(`Token exchange failed: ${error.error_description}`);
        }
        
        const tokens = await response.json();
        
        // Store tokens securely
        localStorage.setItem('access_token', tokens.access_token);
        localStorage.setItem('refresh_token', tokens.refresh_token);
        localStorage.setItem('user_data', JSON.stringify(tokens.user));
        
        // Clean up temporary storage
        sessionStorage.removeItem('oauth_code_verifier');
        sessionStorage.removeItem('oauth_state');
        
        return tokens;
        
    } catch (error) {
        console.error('Token exchange error:', error);
        throw error;
    }
}
import requests
import json

def exchange_code_for_tokens(code, redirect_uri, code_verifier):
    """Exchange authorization code for access tokens"""
    
    token_url = 'https://hub.regardingwork.com/api/oauth/token'
    
    payload = {
        'grant_type': 'authorization_code',
        'code': code,
        'redirect_uri': redirect_uri,
        'code_verifier': code_verifier
    }
    
    headers = {
        'Content-Type': 'application/json'
    }
    
    try:
        response = requests.post(token_url, json=payload, headers=headers)
        response.raise_for_status()
        
        tokens = response.json()
        
        # Store tokens securely
        access_token = tokens['access_token']
        refresh_token = tokens['refresh_token']
        user_data = tokens['user']
        
        return {
            'access_token': access_token,
            'refresh_token': refresh_token,
            'user': user_data
        }
        
    except requests.exceptions.HTTPError as e:
        error_data = e.response.json()
        raise Exception(f"Token exchange failed: {error_data.get('error_description', 'Unknown error')}")
    
    except Exception as e:
        raise Exception(f"Token exchange error: {str(e)}")
# Exchange authorization code for tokens
curl -X POST "https://hub.regardingwork.com/api/oauth/token" \
     -H "Content-Type: application/json" \
     -d '{
       "grant_type": "authorization_code",
       "code": "your-authorization-code-here",
       "redirect_uri": "https://ce.regardingwork.com/api/auth/callback",
       "code_verifier": "your-pkce-verifier-here"
     }'

# Example response:
# {
#   "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
#   "token_type": "Bearer",
#   "expires_in": 86400,
#   "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
#   "scope": "profile email",
#   "user": {
#     "id": 12,
#     "username": "janechen",
#     "email": "jane@example.com"
#   }
# }

Security Considerations

⏱️ Code Expiration

Authorization codes expire after 5 minutes for security. Exchange them immediately after receiving the callback.

🔒 PKCE Verification

When using PKCE, the code_verifier must match the original code_challenge using SHA256 hashing.

Using the Access Token

Once you have the access token, use it to make authenticated API requests:

// Make authenticated API request
fetch('https://game.regardingwork.com/api/user/data', {
    headers: {
        'Authorization': `Bearer ${access_token}`,
        'Content-Type': 'application/json'
    }
})
.then(response => response.json())
.then(data => {
    console.log('User data:', data);
});

Token Details:

  • Format: JWT (JSON Web Token)
  • Expiry: 24 hours
  • Usage: Include in Authorization header as "Bearer {token}"
  • Refresh: Use refresh_token to get new access_token