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
Method 1: Discovery Configuration (Recommended)
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 typesEnvironment 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_hereLoad 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: subC# 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:
Navigation
β Previous: Step 1: Choose OAuth Library - Select your library
β Overview: Integration Flow Overview - See the big picture
β Next: Step 3: Implement Authorization Flow - Implement JAR authorization
Progress: Step 2 of 5 complete β
Last updated