Step 2: Configure OAuth Client

Now that you've chosen your OAuth library, it's time to configure your OAuth client with Oten IDP endpoints and credentials.

Need context? Check the Integration Flow Overview to see how this step fits into the complete process.

What You'll Learn

In this step, you will:

  • Set up OAuth client configuration using Discovery (recommended)

  • Configure Oten IDP endpoints automatically or manually

  • Understand different client types and their configurations

  • Set up environment variables securely

  • Test your basic configuration

Configuration Methods

Use OpenID Connect Discovery to automatically fetch configuration:

πŸ“– Detailed Guide: See Discovery Configuration for complete implementation examples.

// Automatic configuration using discovery
async function createOAuthConfig() {
  const discoveryUrl = 'https://account.oten.com/.well-known/openid-configuration';
  const response = await fetch(discoveryUrl);
  const discoveryConfig = await response.json();

  return {
    // Client credentials (from Oten registration)
    clientId: process.env.OTEN_CLIENT_ID,
    clientSecret: process.env.OTEN_CLIENT_SECRET,

    // Automatically discovered endpoints
    authorizationURL: discoveryConfig.authorization_endpoint,
    tokenURL: discoveryConfig.token_endpoint,
    userInfoURL: discoveryConfig.userinfo_endpoint,
    jwksURI: discoveryConfig.jwks_uri,
    issuer: discoveryConfig.issuer,

    // Application settings
    redirectURI: process.env.OTEN_REDIRECT_URI,
    scopes: ['openid', 'profile', 'email'],

    // OAuth flow settings
    responseType: 'code',
    grantType: 'authorization_code'
  };
}

// Usage
const config = await createOAuthConfig();

Method 2: Manual Configuration

If you prefer to configure endpoints manually:

const oauthConfig = {
  // Client credentials (from Oten registration)
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret', // Only for confidential clients

  // Oten IDP endpoints (manual)
  authorizationURL: 'https://account.oten.com/v1/oauth/authorize',
  tokenURL: 'https://account.oten.com/v1/oauth/token',
  userInfoURL: 'https://account.oten.com/v1/oauth/userinfo',

  // Application settings
  redirectURI: 'https://yourapp.com/callback',
  scopes: ['openid', 'profile', 'email'],

  // OAuth flow settings
  responseType: 'code',
  grantType: 'authorization_code'
};

Oten IDP Endpoints

Primary Endpoints

const otenEndpoints = {
  // Authorization endpoint - where users are redirected to login
  authorization: 'https://account.oten.com/v1/oauth/authorize',

  // Token endpoint - where authorization codes are exchanged for tokens
  token: 'https://account.oten.com/v1/oauth/token',

  // UserInfo endpoint - where user information is retrieved
  userInfo: 'https://account.oten.com/v1/oauth/userinfo',

  // JWKS endpoint - for token signature verification
  jwks: 'https://account.oten.com/.well-known/jwks.json',

  // OpenID Connect discovery endpoint
  discovery: 'https://account.oten.com/.well-known/openid_configuration'
};

Discovery Endpoint Usage

Many libraries support automatic configuration via the discovery endpoint:

// Automatic discovery (recommended)
const issuerUrl = 'https://account.oten.com';

// The library will automatically fetch:
// - Authorization endpoint
// - Token endpoint  
// - UserInfo endpoint
// - JWKS endpoint
// - Supported scopes and response types

Environment Variables Setup

Create Environment File

Create a .env file (never commit to version control):

# Oten OAuth Configuration
OTEN_CLIENT_ID=your_client_id_here
OTEN_CLIENT_SECRET=your_client_secret_here
OTEN_REDIRECT_URI=https://yourapp.com/callback

# Oten Endpoints (optional if using discovery)
OTEN_ISSUER=https://account.oten.com
OTEN_AUTH_URL=https://account.oten.com/v1/oauth/authorize
OTEN_TOKEN_URL=https://account.oten.com/v1/oauth/token

# Application Settings
NODE_ENV=development
PORT=3000
SESSION_SECRET=your_session_secret_here

Load Environment Variables

// Load environment variables
require('dotenv').config();

const config = {
  clientId: process.env.OTEN_CLIENT_ID,
  clientSecret: process.env.OTEN_CLIENT_SECRET,
  redirectURI: process.env.OTEN_REDIRECT_URI,
  issuer: process.env.OTEN_ISSUER
};

// Validate required configuration
if (!config.clientId) {
  throw new Error('OTEN_CLIENT_ID is required');
}

if (!config.redirectURI) {
  throw new Error('OTEN_REDIRECT_URI is required');
}

Technology-Specific Configurations

Node.js with openid-client

const { Issuer } = require('openid-client');

async function setupOAuthClient() {
  // Discover Oten configuration
  const otenIssuer = await Issuer.discover('https://account.oten.com');

  // Create client
  const client = new otenIssuer.Client({
    client_id: process.env.OTEN_CLIENT_ID,
    client_secret: process.env.OTEN_CLIENT_SECRET,
    redirect_uris: [process.env.OTEN_REDIRECT_URI],
    response_types: ['code'],
    grant_types: ['authorization_code', 'refresh_token'],
    token_endpoint_auth_method: 'client_secret_basic'
  });
  
  return client;
}

Python with Authlib

from authlib.integrations.flask_client import OAuth
from flask import Flask

app = Flask(__name__)
app.secret_key = os.environ.get('SESSION_SECRET')

oauth = OAuth(app)

# Configure Oten OAuth
oten = oauth.register(
    name='oten',
    client_id=os.environ.get('OTEN_CLIENT_ID'),
    client_secret=os.environ.get('OTEN_CLIENT_SECRET'),
    server_metadata_url='https://account.oten.com/.well-known/openid_configuration',
    client_kwargs={
        'scope': 'openid profile email',
        'response_type': 'code',
        'grant_type': 'authorization_code'
    }
)

Java Spring Boot

# application.yml
spring:
  security:
    oauth2:
      client:
        registration:
          oten:
            client-id: ${OTEN_CLIENT_ID}
            client-secret: ${OTEN_CLIENT_SECRET}
            scope: openid,profile,email
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
            client-name: Oten
        provider:
          oten:
            issuer-uri: https://account.oten.com
            authorization-uri: https://account.oten.com/v1/oauth/authorize
            token-uri: https://account.oten.com/v1/oauth/token
            user-info-uri: https://account.oten.com/v1/oauth/userinfo
            jwk-set-uri: https://account.oten.com/.well-known/jwks.json
            user-name-attribute: sub

C# ASP.NET Core

// Startup.cs or Program.cs
services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
    options.LoginPath = "/Account/Login";
    options.LogoutPath = "/Account/Logout";
})
.AddOpenIdConnect(options =>
{
    options.Authority = "https://account.oten.com";
    options.ClientId = Configuration["Oten:ClientId"];
    options.ClientSecret = Configuration["Oten:ClientSecret"];
    options.ResponseType = "code";
    options.SaveTokens = true;
    
    options.Scope.Clear();
    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("email");
    
    options.CallbackPath = "/signin-oidc";
    options.SignedOutCallbackPath = "/signout-callback-oidc";
    
    options.GetClaimsFromUserInfoEndpoint = true;
    options.ClaimActions.MapJsonKey("email", "email");
    options.ClaimActions.MapJsonKey("name", "name");
});

πŸ”’ Client Types and Security

Confidential Clients (Server-Side)

For applications that can securely store secrets:

const confidentialClientConfig = {
  clientId: process.env.OTEN_CLIENT_ID,
  clientSecret: process.env.OTEN_CLIENT_SECRET, // Secret is secure
  
  // Authentication method for token endpoint
  tokenEndpointAuthMethod: 'client_secret_basic', // or 'client_secret_post'
  
  // Grant types
  grantTypes: ['authorization_code', 'refresh_token'],
  
  // Response types
  responseTypes: ['code']
};

Public Clients (Client-Side)

For SPAs, mobile apps, and other clients that cannot store secrets:

const publicClientConfig = {
  clientId: process.env.OTEN_CLIENT_ID,
  // No client secret for public clients
  
  // PKCE is required for security
  usePKCE: true,
  
  // Authentication method
  tokenEndpointAuthMethod: 'none',
  
  // Grant types
  grantTypes: ['authorization_code', 'refresh_token'],
  
  // Response types
  responseTypes: ['code']
};

🎯 Scope Configuration

Standard Scopes

const scopes = {
  // Required for OpenID Connect
  openid: 'openid',
  
  // User profile information
  profile: 'profile', // name, given_name, family_name, etc.
  
  // Email information
  email: 'email', // email, email_verified
  
  // Additional scopes (if available)
  phone: 'phone', // phone_number, phone_number_verified
  address: 'address', // formatted address information
  
  // Custom Oten scopes
  workspace: 'workspace', // workspace information
  roles: 'roles' // user roles and permissions
};

// Combine scopes
const requestedScopes = ['openid', 'profile', 'email', 'workspace'];

Dynamic Scope Selection

function buildScopes(userType, features) {
  const baseScopes = ['openid', 'profile', 'email'];
  
  if (userType === 'admin') {
    baseScopes.push('roles', 'workspace');
  }
  
  if (features.includes('phone_verification')) {
    baseScopes.push('phone');
  }
  
  return baseScopes;
}

Advanced Configuration Options

Timeout Settings

const advancedConfig = {
  // HTTP timeouts
  timeout: 30000, // 30 seconds
  
  // Token refresh settings
  refreshTokenTolerance: 300, // Refresh 5 minutes before expiry
  
  // Clock skew tolerance
  clockTolerance: 60, // 1 minute tolerance for JWT validation
  
  // Retry settings
  retryAttempts: 3,
  retryDelay: 1000 // 1 second between retries
};

Custom Headers

const customConfig = {
  // Custom headers for all requests
  headers: {
    'User-Agent': 'MyApp/1.0.0',
    'X-Client-Version': '1.0.0'
  },
  
  // Custom parameters
  customParameters: {
    // Add custom parameters to authorization requests
    ui_locales: 'en-US',
    prompt: 'consent' // Force consent screen
  }
};

πŸ§ͺ Testing Your Configuration

Configuration Validation

async function validateConfiguration() {
  try {
    // Test discovery endpoint
    const response = await fetch('https://account.oten.com/.well-known/openid_configuration');
    const config = await response.json();

    console.log('βœ… Discovery endpoint accessible');
    console.log('Supported scopes:', config.scopes_supported);
    console.log('Supported response types:', config.response_types_supported);

    // Validate client configuration
    if (!process.env.OTEN_CLIENT_ID) {
      throw new Error('❌ Client ID not configured');
    }

    if (!process.env.OTEN_REDIRECT_URI) {
      throw new Error('❌ Redirect URI not configured');
    }
    
    console.log('βœ… Client configuration valid');
    
  } catch (error) {
    console.error('❌ Configuration validation failed:', error.message);
    throw error;
  }
}

Test Authorization URL Generation

function testAuthorizationURL() {
  const authURL = new URL('https://account.oten.com/v1/oauth/authorize');

  authURL.searchParams.set('client_id', process.env.OTEN_CLIENT_ID);
  authURL.searchParams.set('redirect_uri', process.env.OTEN_REDIRECT_URI);
  authURL.searchParams.set('response_type', 'code');
  authURL.searchParams.set('scope', 'openid profile email');
  authURL.searchParams.set('state', 'test-state');
  
  console.log('Test authorization URL:');
  console.log(authURL.toString());
  
  // You can manually test this URL in a browser
  return authURL.toString();
}

πŸ” Configuration Troubleshooting

Common Issues

Invalid Client ID

// Check if client ID is correct
if (error.message.includes('invalid_client')) {
  console.error('❌ Invalid client ID. Check your OTEN_CLIENT_ID');
  console.log('Current client ID:', process.env.OTEN_CLIENT_ID);
}

Redirect URI Mismatch

// Validate redirect URI format
function validateRedirectURI(uri) {
  try {
    const url = new URL(uri);
    
    if (url.protocol !== 'https:' && !url.hostname.includes('localhost')) {
      throw new Error('Redirect URI must use HTTPS in production');
    }
    
    console.log('βœ… Redirect URI format valid:', uri);
    return true;
  } catch (error) {
    console.error('❌ Invalid redirect URI:', error.message);
    return false;
  }
}

Debug Configuration

function debugConfiguration() {
  console.log('=== OAuth Configuration Debug ===');
  console.log('Client ID:', process.env.OTEN_CLIENT_ID ? 'βœ… Set' : '❌ Missing');
  console.log('Client Secret:', process.env.OTEN_CLIENT_SECRET ? 'βœ… Set' : '❌ Missing');
  console.log('Redirect URI:', process.env.OTEN_REDIRECT_URI);
  console.log('Environment:', process.env.NODE_ENV);
  console.log('================================');
}

Configuration Checklist

Before proceeding to the next step, ensure:


Progress: Step 2 of 5 complete βœ…

Last updated