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

Error Code
Common Cause
Quick Fix

invalid_request

Missing request parameter or JAR expired

invalid_request_object

Invalid JAR signature/format

invalid_grant

Code expired/used/invalid

invalid_client

Wrong credentials or IP not whitelisted

unauthorized_client

Public client using client_credentials

access_denied

User denied access

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=xyz789

Symptoms

  • Authorization request fails immediately with 400 error

  • User gets redirected to callback URL with error parameters

  • Error code: invalid_request

  • Error description: "Request parameter is required"

  • Missing request parameter when request_uri not 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' parameter

Step 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_id and request parameters

  • Verify 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=xyz789

Symptoms

  • Authorization request fails with 400 error

  • User gets redirected to callback URL with error parameters

  • Error code: invalid_request_object

  • JAR token is created but rejected by Oten IDP

Root Causes

  1. Invalid JWT signature - wrong client secret or private key

  2. Unsupported signing algorithm - must be HS256 or EdDSA

  3. Malformed JWT structure - invalid JWT format

  4. Missing or incorrect Key ID (for EdDSA)

  5. 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 directly

Step 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=xyz789

Symptoms

  • Authorization request fails with 400 error

  • Error code: invalid_request

  • Error 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_grant

  • Various 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_client

  • Client credentials are rejected

Root Causes

  1. Wrong client_id - doesn't match registration

  2. Wrong client_secret - incorrect or missing

  3. Client not found - client_id not registered

  4. Wrong authentication method - using wrong auth method

  5. IP address not whitelisted - for client_credentials grant

  6. 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 supported

Step 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_client

  • Specific 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_client

  • Error 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=xyz789

Symptoms

  • User gets redirected to callback with error

  • Error code: access_denied

  • User 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=xyz789

Token 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_error

  • Usually 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


Need Help? Contact Oten support at [email protected]

Last updated