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-configuration

Sandbox Environment

Discovery URL: https://account.sbx.oten.dev/.well-known/openid-configuration

How 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

Parameter
Description
Oten Values

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-configuration

Sandbox 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

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 issuance

  • profile: 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 ID

  • name: User's full name

  • email: User's email address

Scope-to-Claims Mapping

Scope
Claims Included
Description

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 access

  • offline_access: Refresh token capability

  • workspace: 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:

Error Code
Endpoint
Description
Action Required

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

Token Endpoint:

{
  "error": "invalid_grant",
  "error_description": "Invalid authorization code: code expired"
}

Error Handling Guidelines

  • Always validate state parameter in authorization callbacks

  • Implement retry logic for server_error and temporarily_unavailable

  • Don't retry for invalid_grant, invalid_client, or configuration errors

  • Log 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 request OR request_uri parameter is REQUIRED

  • Cannot provide both request and request_uri parameters simultaneously

  • Missing request parameter results in invalid_request with 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_client error

  • Missing IP address triggers security warnings

Client Type Enforcement:

  • Client credentials grant restricted to confidential clients only

  • Public clients receive unauthorized_client error

  • Client type validation occurs before credential verification

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