Common Errors
This guide provides step-by-step solutions for the most frequently encountered errors when integrating with Oten IDP.
π Error Reference: See Error Codes Reference for complete error documentation. π Implementation Guide: See OAuth Error Handling for code examples.
π― How to Use This Guide
Each error includes:
Symptoms and when the error occurs
Root causes and why it happens
Step-by-step solutions to resolve the issue
Prevention tips to avoid the error in the future
Working code examples showing correct implementation
π Quick Error Lookup
π¨ Critical JAR-Related Errors
Error: "invalid_request" (Missing Request Parameter)
When it occurs: Authorization endpoint rejects requests without required request parameter
Error Response Format
HTTP/1.1 302 Found
Location: https://yourapp.com/callback?
error=invalid_request&
error_description=Request%20parameter%20is%20required&
state=xyz789Symptoms
Authorization request fails immediately with 400 error
User gets redirected to callback URL with error parameters
Error code:
invalid_requestError description: "Request parameter is required"
Missing
requestparameter whenrequest_urinot provided
Root Cause
Oten IDP requires either request OR request_uri parameter for all authorization requests (RFC 9101). When neither parameter is provided, you get "Request parameter is required" error.
Step-by-Step Solution
Step 1: Identify the problem
// β This approach will ALWAYS fail with Oten IDP
const authURL = `https://account.oten.com/v1/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectURI}&response_type=code&scope=openid profile email&state=${state}`;
// Missing required 'request' parameterStep 2: Understand JAR requirement
// Oten IDP requires either:
// Option 1: request parameter (JWT-Secured Authorization Request)
// Option 2: request_uri parameter (URL pointing to JAR)
// You CANNOT provide both or neither
// β Invalid - neither request nor request_uri
const authURL = `https://account.oten.com/v1/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectURI}`;
// β Invalid - both request and request_uri
const authURL = `https://account.oten.com/v1/oauth/authorize?client_id=${clientId}&request=${jwt}&request_uri=${uri}`;Step 3: Implement JAR with request parameter
// β
Correct implementation with JAR
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
function createJAR(authParams, clientSecret) {
const now = Math.floor(Date.now() / 1000);
const jarPayload = {
// JWT Claims
iss: authParams.client_id,
aud: 'https://account.oten.com',
iat: now,
exp: now + 300, // 5 minutes
jti: crypto.randomUUID(),
// OAuth Parameters (ALL must be in JAR)
client_id: authParams.client_id,
redirect_uri: authParams.redirect_uri,
response_type: 'code',
scope: authParams.scope,
state: authParams.state,
code_challenge: authParams.code_challenge,
code_challenge_method: 'S256'
};
return jwt.sign(jarPayload, clientSecret, { algorithm: 'HS256' });
}
// Usage
const authParams = {
client_id: process.env.CLIENT_ID,
redirect_uri: process.env.REDIRECT_URI,
scope: 'openid profile email',
state: generateState(),
code_challenge: generateCodeChallenge(),
};
const requestJWT = createJAR(authParams, process.env.CLIENT_SECRET);
const authURL = `https://account.oten.com/v1/oauth/authorize?client_id=${authParams.client_id}&request=${requestJWT}`;Step 3: Verify implementation
Check that authorization URL only contains
client_idandrequestparametersVerify JAR contains all OAuth parameters in JWT payload
Test with a real authorization request
Prevention Tips
Always include request parameter - it's required for Oten IDP
Include ALL OAuth parameters in JAR payload, not URL
Set appropriate expiration (max 5 minutes)
See JAR Complete Guide for detailed implementation
Alternative for Legacy Applications
If your application cannot implement JAR due to technical constraints, contact [email protected] to discuss enabling traditional OAuth flow as a temporary solution.
Include in your request:
Application details and technical constraints
Reason why JAR cannot be implemented
Security measures you have in place
Error: "invalid_request_object"
When it occurs: JAR token has invalid format or signature
Error Response Format
HTTP/1.1 302 Found
Location: https://yourapp.com/callback?
error=invalid_request_object&
error_description=Invalid%20Request%20Object%3A%20JWT%20signature%20verification%20failed&
state=xyz789Symptoms
Authorization request fails with 400 error
User gets redirected to callback URL with error parameters
Error code:
invalid_request_objectJAR token is created but rejected by Oten IDP
Root Causes
Invalid JWT signature - wrong client secret or private key
Unsupported signing algorithm - must be HS256 or EdDSA
Malformed JWT structure - invalid JWT format
Missing or incorrect Key ID (for EdDSA)
Unregistered public key (for EdDSA)
Step-by-Step Solution
Step 1: Verify JWT structure
// Check if your JAR is valid JWT format
const jwtParts = requestJWT.split('.');
if (jwtParts.length !== 3) {
console.error('Invalid JWT format - must have 3 parts separated by dots');
}
// Decode and inspect (without verification)
const header = JSON.parse(atob(jwtParts[0]));
const payload = JSON.parse(atob(jwtParts[1]));
console.log('JWT Header:', header);
console.log('JWT Payload:', payload);Step 2: Fix HS256 signature issues
// β
Correct HS256 implementation
const jwt = require('jsonwebtoken');
function createJAR_HS256(jarPayload, clientSecret) {
// Ensure algorithm is exactly 'HS256'
return jwt.sign(jarPayload, clientSecret, {
algorithm: 'HS256',
// Don't include keyid for HS256
});
}
// Common mistakes to avoid:
// β Wrong algorithm
// jwt.sign(payload, secret, { algorithm: 'RS256' }); // Not supported
// β Wrong secret format
// jwt.sign(payload, Buffer.from(secret, 'base64')); // Use string directlyStep 3: Fix EdDSA signature issues
// β
Correct EdDSA implementation
const fs = require('fs');
function createJAR_EdDSA(jarPayload, privateKeyPath, keyId) {
const privateKey = fs.readFileSync(privateKeyPath, 'utf8');
return jwt.sign(jarPayload, privateKey, {
algorithm: 'EdDSA',
keyid: keyId // Required for EdDSA
});
}
// Verify your private key format
const privateKey = fs.readFileSync('private-key.pem', 'utf8');
if (!privateKey.includes('BEGIN PRIVATE KEY')) {
console.error('Private key must be in PEM format');
}Step 4: Validate JAR payload (Confidential Clients Only)
// Note: JAR is only required for confidential clients
// Public clients should use PKCE with direct parameters instead
// Ensure all required claims are present
function validateJARPayload(payload) {
const requiredClaims = ['iss', 'aud', 'iat', 'exp', 'jti'];
const requiredOAuthParams = ['client_id', 'redirect_uri', 'response_type'];
const missing = [...requiredClaims, ...requiredOAuthParams]
.filter(claim => !payload[claim]);
if (missing.length > 0) {
throw new Error(`Missing required claims: ${missing.join(', ')}`);
}
// Validate timing
const now = Math.floor(Date.now() / 1000);
if (payload.exp <= now) {
throw new Error('JAR token expired');
}
if (payload.iat > now + 60) {
throw new Error('JAR issued in future');
}
}Prevention Tips
Use correct client secret from Oten registration
Only use HS256 or EdDSA algorithms
Include keyid for EdDSA but not for HS256
Validate JAR payload before signing
Test JAR creation with a JWT debugger
Error: "invalid_request" (JAR Expired)
When it occurs: JAR token has exceeded its expiration time
Error Response Format
HTTP/1.1 302 Found
Location: https://yourapp.com/callback?
error=invalid_request&
error_description=JAR%20token%20has%20expired&
state=xyz789Symptoms
Authorization request fails with 400 error
Error code:
invalid_requestError description mentions JAR expiration
JAR was created more than 5 minutes ago
Root Cause
JAR tokens have a maximum lifetime of 5 minutes for security reasons.
Step-by-Step Solution
Step 1: Check JAR expiration
// Decode JAR to check expiration
const jwtParts = requestJWT.split('.');
const payload = JSON.parse(atob(jwtParts[1]));
const now = Math.floor(Date.now() / 1000);
const expiresAt = payload.exp;
const issuedAt = payload.iat;
console.log('Current time:', now);
console.log('JAR issued at:', issuedAt);
console.log('JAR expires at:', expiresAt);
console.log('JAR age (seconds):', now - issuedAt);
console.log('Time until expiry (seconds):', expiresAt - now);
if (expiresAt <= now) {
console.error('JAR has expired');
}Step 2: Generate fresh JAR
// β
Always generate JAR just before use
function createFreshJAR(authParams, clientSecret) {
const now = Math.floor(Date.now() / 1000);
const jarPayload = {
// JWT Claims
iss: authParams.client_id,
aud: 'https://account.oten.com',
iat: now,
exp: now + 300, // 5 minutes from now
jti: crypto.randomUUID(),
// OAuth Parameters
...authParams
};
return jwt.sign(jarPayload, clientSecret, { algorithm: 'HS256' });
}
// Generate JAR immediately before redirect
const requestJWT = createFreshJAR(authParams, clientSecret);
const authURL = `https://account.oten.com/v1/oauth/authorize?client_id=${clientId}&request=${requestJWT}`;
// Redirect immediately
res.redirect(authURL);Prevention Tips
Generate JAR just before use - don't pre-generate
Set expiration to 5 minutes maximum
Don't cache JAR tokens
Implement automatic retry with fresh JAR if expired
Token Endpoint Errors
Error: "invalid_grant"
When it occurs: Authorization code or refresh token is invalid
Error Response Format
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": "invalid_grant",
"error_description": "Invalid authorization code: code not found"
}Symptoms
Token exchange fails with 400 error
Error code:
invalid_grantVarious error descriptions depending on specific issue
Common Scenarios
Scenario 1: Authorization code not found
{
"error": "invalid_grant",
"error_description": "Invalid authorization code: code not found"
}Scenario 2: Authorization code already used
{
"error": "invalid_grant",
"error_description": "Invalid authorization code: code already used"
}Scenario 3: Authorization code expired
{
"error": "invalid_grant",
"error_description": "Invalid authorization code: code expired"
}Scenario 4: PKCE verification failed
{
"error": "invalid_grant",
"error_description": "Invalid code verifier"
}Step-by-Step Solution
Step 1: Check authorization code
// Verify you're using the code from callback
app.get('/callback', (req, res) => {
const { code, state, error } = req.query;
if (error) {
return handleAuthError(error, req, res);
}
if (!code) {
return res.status(400).send('Missing authorization code');
}
// Use code immediately - don't store it
exchangeCodeForTokens(code, req.session.codeVerifier);
});Step 2: Verify PKCE parameters
// Ensure code_verifier matches code_challenge
async function exchangeCodeForTokens(code, codeVerifier) {
const tokenRequest = {
grant_type: 'authorization_code',
code: code,
redirect_uri: process.env.REDIRECT_URI,
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
code_verifier: codeVerifier // Must match original code_challenge
};
const response = await fetch('/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams(tokenRequest)
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Token exchange failed: ${error.error_description}`);
}
return await response.json();
}Prevention Tips
Use authorization code immediately after receiving it
Don't reuse authorization codes - they're single-use
Verify PKCE code_verifier matches original code_challenge
Check redirect_uri matches exactly
Handle token exchange errors gracefully
Error: "invalid_client"
When it occurs: Client authentication failed
Error Response Format
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"error": "invalid_client",
"error_description": "Client authentication failed"
}Symptoms
Token exchange fails with 401 error
Error code:
invalid_clientClient credentials are rejected
Root Causes
Wrong client_id - doesn't match registration
Wrong client_secret - incorrect or missing
Client not found - client_id not registered
Wrong authentication method - using wrong auth method
IP address not whitelisted - for client_credentials grant
Client not confidential - public client attempting client_credentials
Step-by-Step Solution
Step 1: Verify client credentials
// Check your environment variables
console.log('Client ID:', process.env.CLIENT_ID);
console.log('Client Secret exists:', !!process.env.CLIENT_SECRET);
console.log('Client Secret length:', process.env.CLIENT_SECRET?.length);
// Verify against Oten registration
if (!process.env.CLIENT_ID || !process.env.CLIENT_SECRET) {
throw new Error('Missing client credentials in environment variables');
}Step 2: Check authentication method
// β
Correct client authentication (client_secret_post)
const tokenRequest = {
grant_type: 'authorization_code',
code: authorizationCode,
redirect_uri: process.env.REDIRECT_URI,
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET // Include in body
};
// β Don't use Basic authentication
// const auth = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
// headers: { 'Authorization': `Basic ${auth}` } // Not supportedStep 3: Check IP whitelist (for client_credentials grant)
// If using client_credentials grant and getting IP errors
if (grantType === 'client_credentials') {
// Check your current IP
const response = await fetch('https://api.ipify.org?format=json');
const { ip } = await response.json();
console.log('Current IP:', ip);
// Verify IP is in Oten client whitelist
// Contact Oten support to add IP to whitelist
}
// Example error response for IP not in whitelist:
// {
// "error": "invalid_client",
// "error_description": "Invalid client IP: 192.168.1.100 is not in whitelist"
// }Step 4: Verify client type (for client_credentials grant)
// β
Client credentials grant requires confidential client
if (grantType === 'client_credentials') {
// Ensure your client is registered as "confidential" not "public"
// Public clients cannot use client_credentials grant
const tokenRequest = {
grant_type: 'client_credentials',
client_id: process.env.CLIENT_ID, // Must be confidential client
client_secret: process.env.CLIENT_SECRET, // Required for confidential clients
scope: 'read:data write:data'
};
}Prevention Tips
Double-check client credentials from Oten registration
Use client_secret_post authentication method
Store credentials securely in environment variables
Test with curl to verify credentials work
Check IP whitelist for client_credentials grant
Verify client type (confidential vs public)
Error: "unauthorized_client"
When it occurs: Client not authorized for the requested grant type
Error Response Format
HTTP/1.1 403 Forbidden
Content-Type: application/json
{
"error": "unauthorized_client",
"error_description": "Public clients are not authorized for client credentials grant"
}Symptoms
Token exchange fails with 403 error
Error code:
unauthorized_clientSpecific to client_credentials grant type
Root Cause
Public clients cannot use the client_credentials grant type - only confidential clients are authorized.
Step-by-Step Solution
Step 1: Check client type
// Verify your client registration type
console.log('Client ID:', process.env.CLIENT_ID);
console.log('Grant type:', 'client_credentials');
// Check if you have a client secret (confidential clients only)
if (!process.env.CLIENT_SECRET) {
console.error('Missing client secret - this indicates a public client');
console.error('Public clients cannot use client_credentials grant');
}Step 2: Use correct grant type for client type
// β
For public clients - use authorization_code with PKCE
if (clientType === 'public') {
const tokenRequest = {
grant_type: 'authorization_code',
code: authorizationCode,
redirect_uri: process.env.REDIRECT_URI,
client_id: process.env.CLIENT_ID,
code_verifier: codeVerifier // PKCE required for public clients
};
}
// β
For confidential clients - can use client_credentials
if (clientType === 'confidential') {
const tokenRequest = {
grant_type: 'client_credentials',
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
scope: 'read:data'
};
}Step 3: Register confidential client if needed
// If you need machine-to-machine authentication:
// 1. Register a new confidential client in Oten
// 2. Get client_id and client_secret
// 3. Configure IP whitelist if required
// 4. Use client_credentials grant
const confidentialClientConfig = {
client_id: 'your_confidential_client_id',
client_secret: 'your_confidential_client_secret',
client_type: 'confidential',
grant_types: ['client_credentials'],
ip_whitelist: ['192.168.1.100', '10.0.0.50'] // Optional
};Prevention Tips
Use correct client type for your use case
Public clients: Use authorization_code with PKCE
Confidential clients: Can use client_credentials for M2M
Check client registration in Oten admin portal
Error: "invalid_client" (IP Not Whitelisted)
When it occurs: Client credentials grant from non-whitelisted IP address
Error Response Format
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"error": "invalid_client",
"error_description": "Invalid client IP: 192.168.1.100 is not in whitelist"
}Symptoms
Client credentials grant fails with 401 error
Error code:
invalid_clientError description mentions specific IP address
Valid client credentials but request rejected
Root Cause
Oten IDP implements IP address validation for client credentials grant when IP whitelist is configured for enhanced security.
Step-by-Step Solution
Step 1: Check your current IP address
// Check your current public IP
async function checkCurrentIP() {
try {
const response = await fetch('https://api.ipify.org?format=json');
const { ip } = await response.json();
console.log('Current public IP:', ip);
return ip;
} catch (error) {
console.error('Failed to get IP:', error);
}
}
// Check if IP is causing the issue
const currentIP = await checkCurrentIP();
console.log('If you see this IP in error message, it needs to be whitelisted:', currentIP);Step 2: Verify IP whitelist configuration
// Contact Oten support to:
// 1. Check if IP whitelist is configured for your client
// 2. Add your IP address to the whitelist
// 3. Verify whitelist configuration
// Example whitelist configuration:
const clientConfig = {
client_id: 'your_client_id',
client_type: 'confidential',
ip_whitelist: [
'192.168.1.100', // Your current IP
'10.0.0.50', // Server IP
'203.0.113.0/24' // IP range (if supported)
]
};Step 3: Test from whitelisted IP
// After IP is added to whitelist, test the request
async function testClientCredentials() {
const tokenRequest = {
grant_type: 'client_credentials',
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
scope: 'read:data'
};
try {
const response = await fetch('/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams(tokenRequest)
});
if (response.ok) {
console.log('β
IP whitelist validation passed');
return await response.json();
} else {
const error = await response.json();
console.error('β Still failing:', error);
}
} catch (error) {
console.error('Network error:', error);
}
}Prevention Tips
Configure IP whitelist in Oten admin portal
Use static IP addresses for production servers
Document whitelisted IPs for your team
Monitor IP changes in your infrastructure
Test from all deployment environments
Security Benefits
Prevents unauthorized access even with valid credentials
Provides additional layer of protection for machine-to-machine authentication
Enables detection of credential theft or misuse from unexpected locations
Supports compliance with network security policies
Error: "access_denied"
When it occurs: User denied authorization on consent screen
Error Response Format
HTTP/1.1 302 Found
Location: https://yourapp.com/callback?
error=access_denied&
error_description=The%20resource%20owner%20or%20authorization%20server%20denied%20the%20request&
state=xyz789Symptoms
User gets redirected to callback with error
Error code:
access_deniedUser clicked "Deny" or "Cancel" on consent screen
Root Cause
This is normal user behavior - user chose not to grant access to your application.
Step-by-Step Solution
Step 1: Handle gracefully
app.get('/callback', (req, res) => {
const { error, error_description, state } = req.query;
if (error === 'access_denied') {
// This is normal - user denied access
return res.render('access-denied', {
message: 'You chose not to grant access to this application.',
action: 'You can try again if you change your mind.',
loginUrl: '/login'
});
}
// Handle other errors...
});Step 2: Provide clear messaging
<!-- access-denied.html -->
<div class="error-message">
<h2>Access Denied</h2>
<p>You chose not to grant access to this application.</p>
<p>If you change your mind, you can <a href="/login">try again</a>.</p>
</div>Prevention Tips
Explain why you need permissions before redirecting to auth
Request minimal scopes - only what you actually need
Provide clear value proposition to users
Handle denial gracefully - don't show error messages
π Network and Configuration Errors
Error: "server_error"
When it occurs: Internal server error on Oten IDP
Error Response Format
Authorization Endpoint:
HTTP/1.1 302 Found
Location: https://yourapp.com/callback?
error=server_error&
error_description=The%20authorization%20server%20encountered%20an%20unexpected%20condition&
state=xyz789Token Endpoint:
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
{
"error": "server_error",
"error_description": "The authorization server encountered an unexpected condition that prevented it from fulfilling the request"
}Symptoms
Intermittent failures
Error code:
server_errorUsually temporary issues
Step-by-Step Solution
Step 1: Implement retry logic
async function makeRequestWithRetry(requestFn, maxRetries = 3) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await requestFn();
} catch (error) {
lastError = error;
// Only retry for server errors
if (error.message.includes('server_error') && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
throw lastError;
}
// Usage for token exchange
const tokens = await makeRequestWithRetry(() =>
exchangeCodeForTokens(code, codeVerifier)
);Step 2: Check Oten status
# Check Oten service status
curl -I https://account.oten.com
# Check discovery endpoint
curl -s https://account.oten.com/.well-known/openid-configuration | jq .Prevention Tips
Implement retry logic with exponential backoff
Monitor Oten status page
Set appropriate timeouts
Log errors for investigation
π§ Quick Debugging Checklist
Authorization Endpoint Issues
Token Endpoint Issues
Network Issues
π Additional Resources
Error Codes Reference: Complete error documentation
JAR Implementation Guide: Detailed JAR examples
API Reference: Complete API documentation
Security Best Practices: Security guidelines
Need Help? Contact Oten support at [email protected]
Last updated