Configuration Reference
OpenID Connect Discovery
Oten IDP provides OpenID Connect Discovery endpoints that automatically provide all configuration information your application needs. This is the recommended way to get configuration details.
Discovery Endpoints
Production Environment
Discovery URL: https://account.oten.com/.well-known/openid-configurationSandbox Environment
Discovery URL: https://account.sbx.oten.dev/.well-known/openid-configurationHow to Use Discovery
Instead of hardcoding endpoints, fetch configuration dynamically:
// Fetch configuration automatically
async function getOIDCConfiguration(environment = 'production') {
const discoveryUrl = environment === 'production'
? 'https://account.oten.com/.well-known/openid-configuration'
: 'https://account.sbx.oten.dev/.well-known/openid-configuration';
const response = await fetch(discoveryUrl);
const config = await response.json();
return {
issuer: config.issuer,
authorizationEndpoint: config.authorization_endpoint,
tokenEndpoint: config.token_endpoint,
jwksUri: config.jwks_uri,
supportedScopes: config.scopes_supported,
supportedResponseTypes: config.response_types_supported,
supportedGrantTypes: config.grant_types_supported,
supportedCodeChallengeMethods: config.code_challenge_methods_supported,
supportedTokenAuthMethods: config.token_endpoint_auth_methods_supported,
supportedClaims: config.claims_supported,
supportedIdTokenSigningAlgs: config.id_token_signing_alg_values_supported
};
}
// Usage example
const config = await getOIDCConfiguration('development');
console.log('Authorization Endpoint:', config.authorizationEndpoint);
console.log('Token Endpoint:', config.tokenEndpoint);
console.log('Supported Scopes:', config.supportedScopes);🔧 Configuration Response Format
The discovery endpoint returns a JSON object with the following structure:
{
"issuer": "https://account.sbx.oten.dev",
"authorization_endpoint": "https://account.sbx.oten.dev/v1/oauth/authorize",
"token_endpoint": "https://account.sbx.oten.dev/v1/oauth/token",
"jwks_uri": "https://account.sbx.oten.dev/.well-known/jwks.json",
"response_types_supported": ["code"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["EdDSA"],
"scopes_supported": ["openid", "profile", "email"],
"token_endpoint_auth_methods_supported": ["client_secret_post"],
"claims_supported": ["sub", "name", "email"],
"code_challenge_methods_supported": ["S256", "plain"],
"grant_types_supported": ["authorization_code", "refresh_token"]
}Configuration Parameters Explained
issuer
Identity provider identifier
https://account.[env].oten.dev or https://account.oten.com
authorization_endpoint
OAuth authorization URL
/v1/oauth/authorize
token_endpoint
Token exchange URL
/v1/oauth/token
jwks_uri
Public keys for token verification
/.well-known/jwks.json
response_types_supported
Supported OAuth response types
["code"]
subject_types_supported
Subject identifier types
["public"]
id_token_signing_alg_values_supported
ID token signing algorithms
["EdDSA"]
scopes_supported
Available OAuth scopes
["openid", "profile", "email"]
token_endpoint_auth_methods_supported
Client authentication methods
["client_secret_post"]
claims_supported
Available user claims
["sub", "name", "email"]
code_challenge_methods_supported
PKCE challenge methods
["S256", "plain"]
grant_types_supported
Supported OAuth grant types
["authorization_code", "refresh_token"]
🌐 Manual Endpoint Configuration
If you prefer to configure endpoints manually (not recommended), here are the static endpoints:
Production Environment
Base Domain: oten.com
Authorization Server: https://account.oten.com
Token Endpoint: https://account.oten.com/v1/oauth/token
Authorization Endpoint: https://account.oten.com/v1/oauth/authorize
Token Validation: https://account.oten.com/v1/token/validate
User Info: https://account.oten.com/v1/userinfo
JWKS URI: https://account.oten.com/.well-known/jwks.json
OIDC Discovery: https://account.oten.com/.well-known/openid-configurationSandbox Environment
Base Domain: oten.dev
Authorization Server: https://account.sbx.oten.dev
Token Endpoint: https://account.sbx.oten.dev/v1/oauth/token
Authorization Endpoint: https://account.sbx.oten.dev/v1/oauth/authorize
Token Validation: https://account.sbx.oten.dev/v1/token/validate
User Info: https://account.sbx.oten.dev/v1/userinfo
JWKS URI: https://account.sbx.oten.dev/.well-known/jwks.json
OIDC Discovery: https://account.sbx.oten.dev/.well-known/openid-configuration🛠️ Library-Specific Configuration Examples
Node.js with Passport.js
const passport = require('passport');
const { Strategy: OIDCStrategy } = require('passport-openidconnect');
// Configure using discovery URL
passport.use('oten', new OIDCStrategy({
issuer: 'https://account.sbx.oten.dev',
authorizationURL: 'https://account.sbx.oten.dev/v1/oauth/authorize',
tokenURL: 'https://account.sbx.oten.dev/v1/oauth/token',
userInfoURL: 'https://account.sbx.oten.dev/v1/userinfo',
clientID: process.env.OTEN_CLIENT_ID,
clientSecret: process.env.OTEN_CLIENT_SECRET,
callbackURL: process.env.OTEN_REDIRECT_URI,
scope: ['openid', 'profile', 'email']
}, (issuer, profile, done) => {
return done(null, profile);
}));Python with Authlib
from authlib.integrations.flask_client import OAuth
oauth = OAuth(app)
# Configure using discovery URL
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.sbx.oten.dev/.well-known/openid-configuration',
client_kwargs={
'scope': 'openid profile email',
'code_challenge_method': 'S256' # Enable PKCE
}
)Java with Spring Security
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public ClientRegistrationRepository clientRegistrationRepository() {
return new InMemoryClientRegistrationRepository(otenClientRegistration());
}
private ClientRegistration otenClientRegistration() {
return ClientRegistration.withRegistrationId("oten")
.clientId(System.getenv("OTEN_CLIENT_ID"))
.clientSecret(System.getenv("OTEN_CLIENT_SECRET"))
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
.scope("openid", "profile", "email")
.issuerUri("https://account.sbx.oten.dev")
.build();
}
}React SPA with OIDC Client
import { UserManager } from 'oidc-client-ts';
const userManager = new UserManager({
authority: 'https://account.sbx.oten.dev',
client_id: process.env.REACT_APP_CLIENT_ID,
redirect_uri: `${window.location.origin}/callback`,
response_type: 'code',
scope: 'openid profile email',
post_logout_redirect_uri: window.location.origin,
// PKCE is automatically enabled
automaticSilentRenew: true,
silent_redirect_uri: `${window.location.origin}/silent-callback`,
// Discovery will automatically fetch endpoints
metadata: {
issuer: 'https://account.sbx.oten.dev',
authorization_endpoint: 'https://account.sbx.oten.dev/v1/oauth/authorize',
token_endpoint: 'https://account.sbx.oten.dev/v1/oauth/token',
userinfo_endpoint: 'https://account.sbx.oten.dev/v1/userinfo',
jwks_uri: 'https://account.sbx.oten.dev/.well-known/jwks.json'
}
});.NET Core
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.Authority = "https://account.sbx.oten.dev";
options.ClientId = Configuration["Oten:ClientId"];
options.ClientSecret = Configuration["Oten:ClientSecret"];
options.ResponseType = "code";
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
options.SaveTokens = true;
options.UsePkce = true; // Enable PKCE
});
}🔄 Dynamic Configuration Loading
Environment-Aware Configuration
class OtenConfig {
constructor(environment = 'development') {
this.environment = environment;
this.config = null;
}
async loadConfiguration() {
const discoveryUrl = this.getDiscoveryUrl();
try {
const response = await fetch(discoveryUrl);
if (!response.ok) {
throw new Error(`Failed to fetch configuration: ${response.status}`);
}
this.config = await response.json();
return this.config;
} catch (error) {
console.error('Failed to load OIDC configuration:', error);
// Fallback to static configuration
return this.getFallbackConfiguration();
}
}
getDiscoveryUrl() {
const baseUrl = this.environment === 'production'
? 'https://account.oten.com'
: 'https://account.sbx.oten.dev';
return `${baseUrl}/.well-known/openid-configuration`;
}
getFallbackConfiguration() {
const baseUrl = this.environment === 'production'
? 'https://account.oten.com'
: 'https://account.sbx.oten.dev';
return {
issuer: baseUrl,
authorization_endpoint: `${baseUrl}/v1/oauth/authorize`,
token_endpoint: `${baseUrl}/v1/oauth/token`,
jwks_uri: `${baseUrl}/.well-known/jwks.json`,
userinfo_endpoint: `${baseUrl}/v1/userinfo`,
scopes_supported: ['openid', 'profile', 'email'],
response_types_supported: ['code'],
grant_types_supported: ['authorization_code', 'refresh_token']
};
}
async getEndpoint(type) {
if (!this.config) {
await this.loadConfiguration();
}
const endpoints = {
authorization: this.config.authorization_endpoint,
token: this.config.token_endpoint,
userinfo: this.config.userinfo_endpoint,
jwks: this.config.jwks_uri
};
return endpoints[type];
}
async getSupportedFeatures() {
if (!this.config) {
await this.loadConfiguration();
}
return {
scopes: this.config.scopes_supported || [],
responseTypes: this.config.response_types_supported || [],
grantTypes: this.config.grant_types_supported || [],
codeChallengeMethod: this.config.code_challenge_methods_supported || [],
tokenAuthMethods: this.config.token_endpoint_auth_methods_supported || [],
claims: this.config.claims_supported || []
};
}
}
// Usage
const config = new OtenConfig('development');
await config.loadConfiguration();
const authEndpoint = await config.getEndpoint('authorization');
const supportedScopes = await config.getSupportedFeatures();
console.log('Available scopes:', supportedScopes.scopes);Official Contact Information
Support Channels
Technical Support: [email protected]
Developer Resources
Developer Portal: https://developer.oten.com (Coming Soon)
API Documentation: https://docs.oten.com (Coming Soon)
Status Page: https://status.oten.com (Coming Soon)
Rate Limits
Production Limits
Authorization endpoint: 10 requests/minute per IP
Token endpoint: 60 requests/minute per client
Validation endpoint: 1000 requests/minute per client
User Info endpoint: 100 requests/minute per token
Development Limits
Authorization endpoint: 20 requests/minute per IP
Token endpoint: 120 requests/minute per client
Validation endpoint: 2000 requests/minute per client
User Info endpoint: 200 requests/minute per token
🔐 Supported Algorithms and Methods
JAR Signing Algorithms
HS256: HMAC with SHA-256 (uses client_secret)
EdDSA: Edwards-curve Digital Signature with Ed25519
Token Signing Algorithms
EdDSA: Edwards-curve Digital Signature (for ID tokens and access tokens)
PKCE Code Challenge Methods
S256: SHA256 hash of code verifier (recommended)
plain: Plain text code verifier (not recommended for production)
Client Authentication Methods
client_secret_post: Client credentials in request body (default)
Supported Response Types
code: Authorization code flow (only supported type)
Supported Grant Types
authorization_code: Standard OAuth 2.0 authorization code flow
refresh_token: Token refresh using refresh tokens
Supported Scopes and Claims
Available Scopes
Based on the discovery endpoint, Oten IDP currently supports:
openid: Required for OpenID Connect flow - enables ID token issuanceprofile: Access to basic profile information (name, etc.)email: Access to user's email address
Available Claims
The following claims are available in ID tokens and UserInfo endpoint:
sub: Subject identifier - unique user IDname: User's full nameemail: User's email address
Scope-to-Claims Mapping
openid
sub
Minimum required scope, provides user identifier
profile
sub, name
Adds user's display name
email
sub, email
Adds user's email address
Example Scope Requests
// Minimum required scope
scope: "openid"
// Basic user information
scope: "openid profile"
// Full user information
scope: "openid profile email"Future Scopes (Coming Soon)
Additional scopes may be added in future releases:
phone: Phone number accessoffline_access: Refresh token capabilityworkspace: Workspace information access
Testing and Validation
Validate Discovery Endpoint
# Test discovery endpoint
curl -s https://account.sbx.oten.dev/.well-known/openid-configuration | jq .
# Check specific configuration values
curl -s https://account.sbx.oten.dev/.well-known/openid-configuration | jq '.scopes_supported'Validate JWKS Endpoint
# Test JWKS endpoint
curl -s https://account.sbx.oten.dev/.well-known/jwks.json | jq .Configuration Validation Script
async function validateOtenConfiguration(environment = 'development') {
const baseUrl = environment === 'production'
? 'https://account.oten.com'
: 'https://account.sbx.oten.dev';
console.log(`Validating Oten IDP configuration for ${environment}...`);
try {
// Test discovery endpoint
const discoveryResponse = await fetch(`${baseUrl}/.well-known/openid-configuration`);
if (!discoveryResponse.ok) {
throw new Error(`Discovery endpoint failed: ${discoveryResponse.status}`);
}
const config = await discoveryResponse.json();
console.log('✅ Discovery endpoint accessible');
// Validate required fields
const requiredFields = [
'issuer', 'authorization_endpoint', 'token_endpoint',
'jwks_uri', 'scopes_supported', 'response_types_supported'
];
const missingFields = requiredFields.filter(field => !config[field]);
if (missingFields.length > 0) {
throw new Error(`Missing required fields: ${missingFields.join(', ')}`);
}
console.log('✅ All required configuration fields present');
// Test JWKS endpoint
const jwksResponse = await fetch(config.jwks_uri);
if (!jwksResponse.ok) {
throw new Error(`JWKS endpoint failed: ${jwksResponse.status}`);
}
const jwks = await jwksResponse.json();
if (!jwks.keys || jwks.keys.length === 0) {
throw new Error('JWKS endpoint has no keys');
}
console.log('✅ JWKS endpoint accessible and has keys');
// Validate supported features
const validations = [
{
name: 'Authorization Code Flow',
check: config.response_types_supported.includes('code'),
message: 'Authorization code flow is supported'
},
{
name: 'PKCE Support',
check: config.code_challenge_methods_supported &&
config.code_challenge_methods_supported.includes('S256'),
message: 'PKCE with S256 is supported'
},
{
name: 'OpenID Connect',
check: config.scopes_supported.includes('openid'),
message: 'OpenID Connect is supported'
},
{
name: 'Client Secret Post',
check: config.token_endpoint_auth_methods_supported.includes('client_secret_post'),
message: 'Client secret post authentication is supported'
}
];
validations.forEach(validation => {
if (validation.check) {
console.log(`✅ ${validation.message}`);
} else {
console.log(`❌ ${validation.name} validation failed`);
}
});
console.log('\nConfiguration Summary:');
console.log(`Issuer: ${config.issuer}`);
console.log(`Authorization Endpoint: ${config.authorization_endpoint}`);
console.log(`Token Endpoint: ${config.token_endpoint}`);
console.log(`Supported Scopes: ${config.scopes_supported.join(', ')}`);
console.log(`Supported Claims: ${config.claims_supported.join(', ')}`);
return config;
} catch (error) {
console.error('❌ Configuration validation failed:', error.message);
throw error;
}
}
// Usage
validateOtenConfiguration('development')
.then(() => console.log('\nConfiguration validation completed successfully'))
.catch(error => console.error('\nConfiguration validation failed:', error.message));Health Check Script
async function healthCheck(environment = 'development') {
const baseUrl = environment === 'production'
? 'https://account.oten.com'
: 'https://account.sbx.oten.dev';
const endpoints = [
{ name: 'Discovery', url: `${baseUrl}/.well-known/openid-configuration` },
{ name: 'JWKS', url: `${baseUrl}/.well-known/jwks.json` },
{ name: 'Authorization', url: `${baseUrl}/v1/oauth/authorize`, method: 'HEAD' }
];
console.log(`Health check for ${environment} environment...`);
for (const endpoint of endpoints) {
try {
const response = await fetch(endpoint.url, {
method: endpoint.method || 'GET',
timeout: 5000
});
if (response.ok) {
console.log(`✅ ${endpoint.name}: OK (${response.status})`);
} else {
console.log(`⚠️ ${endpoint.name}: ${response.status} ${response.statusText}`);
}
} catch (error) {
console.log(`❌ ${endpoint.name}: ${error.message}`);
}
}
}Error Codes Reference
Complete Reference: See Error Codes Reference for comprehensive error documentation with troubleshooting guides.
Quick Error Reference
Most Common Errors:
invalid_request
Authorization
Missing request parameter or JAR expired
Implement/regenerate JAR
invalid_request_object
Authorization
Invalid JAR token
Fix JAR implementation
invalid_grant
Token
Authorization code/refresh token invalid
Restart auth flow
invalid_client
Token
Client auth failed or IP not whitelisted
Check credentials/IP
unauthorized_client
Token
Client not authorized for grant type
Use correct client type
server_error
Both
Internal server error
Retry with backoff
Error Response Formats
Authorization Endpoint:
Location: https://yourapp.com/callback?error=invalid_request&error_description=Request%20parameter%20is%20required&state=xyzToken Endpoint:
{
"error": "invalid_grant",
"error_description": "Invalid authorization code: code expired"
}Error Handling Guidelines
Always validate
stateparameter in authorization callbacksImplement retry logic for
server_errorandtemporarily_unavailableDon't retry for
invalid_grant,invalid_client, or configuration errorsLog detailed errors server-side for debugging
Provide user-friendly messages based on error codes
Security-Enhanced Error Handling
JAR (JWT-Secured Authorization Request) Requirements:
Either
requestORrequest_uriparameter is REQUIREDCannot provide both
requestandrequest_uriparameters simultaneouslyMissing
requestparameter results ininvalid_requestwith message "Request parameter is required"
IP Address Validation (Client Credentials Grant):
IP address validation is enforced when whitelist is configured
Requests from non-whitelisted IPs receive
invalid_clienterrorMissing IP address triggers security warnings
Client Type Enforcement:
Client credentials grant restricted to confidential clients only
Public clients receive
unauthorized_clienterrorClient type validation occurs before credential verification
Related Documentation
Error Codes Reference: Complete error documentation
Common Errors: Step-by-step troubleshooting
OAuth Error Handling: Implementation examples
Security Requirements
Mandatory Security Features
PKCE: Required for all public clients, recommended for confidential clients
JAR: Required for confidential clients only, forbidden for public clients
HTTPS: Required for all endpoints (except localhost in development)
State Parameter: Required to prevent CSRF attacks
Token Lifetimes
Authorization Code: 10 minutes
Access Token: 1 hour (configurable)
Refresh Token: 30 days (configurable)
ID Token: 1 hour
JAR Token: 5 minutes maximum
Client Registration Requirements
Required Information
Application name
Application type (Web App, SPA, Mobile App, Server-to-Server)
Redirect URIs (must be HTTPS except localhost)
Required scopes
JAR signing method (HS256 or EdDSA)
Optional Information
Application description
Application logo URL
Terms of service URL
Privacy policy URL
JWKS URI (for EdDSA)
Last updated