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 scopesuser
- 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