# Appendix

## 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:

```javascript
// 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:

```json
{
  "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

```javascript
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

```python
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

```java
@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

```javascript
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

```csharp
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

```javascript
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**: <support@oten.dev>

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

```javascript
// 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

```bash
# 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

```bash
# Test JWKS endpoint
curl -s https://account.sbx.oten.dev/.well-known/jwks.json | jq .
```

### Configuration Validation Script

```javascript
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

```javascript
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](broken://pages/bJ3yHAB6ah0s2qAer3PL) 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:**

```json
{
  "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

### Related Documentation

* [**Error Codes Reference**](broken://pages/bJ3yHAB6ah0s2qAer3PL): Complete error documentation
* [**Common Errors**](broken://pages/ChQqcK8cqX9LLgyQtBJT): Step-by-step troubleshooting
* [**OAuth Error Handling**](https://gitlab.silvertiger.tech/documents/idp/-/blob/main/best-practices/oauth-error-handling.md): 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)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://oten.gitbook.io/identity-support/integration/prerequisites/appendix.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
