OIDC Configuration Guide
This guide explains how to configure the OIDC module in IOTA SDK.
Environment Variables
Required Variables
Add these variables to your .env file:
# OIDC Issuer URL (must match your domain)
OIDC_ISSUER_URL=https://your-domain.com
# Crypto key for encrypting signing keys (32+ characters recommended)
OIDC_CRYPTO_KEY=your-very-secure-random-key-here-32-chars-minimum
# Database: SDK builds the connection from DB_NAME, DB_HOST, DB_PORT, DB_USER, DB_PASSWORD (see installation). Use the same DB for OIDC.
DB_NAME=dbname
DB_HOST=localhost
DB_PORT=5432
DB_USER=user
DB_PASSWORD=passwordOptional Variables (supported in central config)
# Token lifetimes (defaults shown)
OIDC_ACCESS_TOKEN_LIFETIME=1h # 1 hour
OIDC_ID_TOKEN_LIFETIME=1h # 1 hour
OIDC_REFRESH_TOKEN_LIFETIME=720h # 30 daysOther OIDC-related variables (e.g. OIDC_AUTH_CODE_LIFETIME, OIDC_REQUIRE_PKCE, OIDC_ALLOWED_ORIGINS, OIDC_RATE_LIMIT_*) are not present in the central pkg/configuration OIDCOptions; they may be used by other layers or not yet implemented.
Crypto Key Generation
The OIDC_CRYPTO_KEY is used to encrypt RSA private keys before storing them in the database.
Generate a Secure Crypto Key
Option 1: Using OpenSSL
openssl rand -base64 32Option 2: Using Python
import secrets
print(secrets.token_urlsafe(32))Option 3: Using Node.js
crypto.randomBytes(32).toString('base64')Important Security Notes
- Never commit the crypto key to version control
- Use different keys for development, staging, and production
- Store the key in a secure secret management system (AWS Secrets Manager, HashiCorp Vault, etc.)
- Rotate the key periodically (see Key Rotation section below)
Key Rotation
When rotating the crypto key:
- Generate a new crypto key
- Decrypt existing signing keys with old crypto key
- Re-encrypt with new crypto key
- Update
OIDC_CRYPTO_KEYin environment - Restart the application
Token Lifetime Configuration
Default Token Lifetimes
| Token Type | Default Lifetime | Recommended Range |
|---|---|---|
| Access Token | 1 hour | 15 minutes - 1 hour |
| ID Token | 1 hour | 15 minutes - 1 hour |
| Refresh Token | 30 days | 7 days - 90 days |
| Authorization Code | 5 minutes | 1 minute - 10 minutes |
Per-Client Token Lifetimes
You can configure custom lifetimes for specific clients:
-- Set custom token lifetimes for a client
UPDATE oidc_clients
SET
access_token_lifetime = INTERVAL '30 minutes',
id_token_lifetime = INTERVAL '30 minutes',
refresh_token_lifetime = INTERVAL '7 days'
WHERE client_id = 'my-client-id';Lifetime Tuning Guidelines
Short-lived Access Tokens (15-30 minutes):
- Better security (reduced window for token theft)
- Encourages token refresh
- More frequent token refreshes
- Higher load on token endpoint
Long-lived Access Tokens (2-24 hours):
- Fewer token refreshes
- Better performance
- Longer vulnerability window
- Not recommended for sensitive applications
Refresh Token Lifetime:
- Mobile apps: 30-90 days
- Web apps: 7-30 days
- Backend services: No refresh tokens (use client credentials)
Multi-Tenant Considerations
Tenant Isolation
OIDC module respects multi-tenant architecture:
- Clients are global (not tenant-specific)
- Auth requests include tenant_id after user authentication
- Refresh tokens are scoped to user + tenant + client
- User info returns data for authenticated tenant only
Security Best Practices
Variables such as OIDC_RATE_LIMIT_*, OIDC_ALLOWED_ORIGINS, OIDC_AUTH_CODE_LIFETIME, and OIDC_REQUIRE_PKCE are not part of the central pkg/configuration OIDCOptions. They may be implemented by other layers or not yet functional—verify support in your deployment before relying on them.
1. Client Configuration
Public Clients (SPAs, Mobile Apps):
INSERT INTO oidc_clients (client_id, name, application_type, redirect_uris, require_pkce)
VALUES (
'spa-client',
'My SPA Application',
'spa',
ARRAY['http://localhost:3000/callback'],
true -- ALWAYS true for public clients
);Confidential Clients (Backend Services):
INSERT INTO oidc_clients (
client_id,
name,
application_type,
redirect_uris,
client_secret_hash,
require_pkce
)
VALUES (
'backend-client',
'My Backend Service',
'web',
ARRAY['https://api.example.com/callback'],
'$2a$10$hashed_secret_here', -- Use bcrypt
false -- Optional for confidential clients
);2. Redirect URI Validation
Strict Validation Rules:
- Exact match required (no wildcards)
- HTTPS required in production
- Localhost allowed only in development
- Do not use
http://in production - Do not use wildcard subdomains
Example:
-- Good: Exact URLs
redirect_uris = ARRAY['https://app.example.com/callback']
-- Bad: Wildcards (not supported)
redirect_uris = ARRAY['https://*.example.com/callback']
-- Development only
redirect_uris = ARRAY['http://localhost:3000/callback']3. Scope Configuration
Recommended Scopes:
| Scope | Description | Required? |
|---|---|---|
openid | OpenID Connect authentication | Yes (always) |
profile | User profile info (name, picture) | Recommended |
email | User email address | Recommended |
offline_access | Refresh token | Optional |
Custom Scopes:
-- Add custom scopes for specific clients
UPDATE oidc_clients
SET scopes = ARRAY['openid', 'profile', 'email', 'custom:read', 'custom:write']
WHERE client_id = 'my-client';4. Rate Limiting
Note: These variables are not part of central
pkg/configurationand may require custom implementation.
Protect endpoints from abuse:
# In .env
OIDC_RATE_LIMIT_ENABLED=true
# Per-endpoint limits
OIDC_RATE_LIMIT_AUTHORIZE=100 # per 15 min per IP
OIDC_RATE_LIMIT_TOKEN=50 # per 15 min per client
OIDC_RATE_LIMIT_USERINFO=200 # per 15 min per token5. CORS Configuration
Note:
OIDC_ALLOWED_ORIGINSis not part of centralpkg/configurationand may require custom implementation.
Configure CORS for browser-based clients:
# Allow specific origins
OIDC_ALLOWED_ORIGINS=https://app.example.com,https://admin.example.com
# DO NOT use wildcard in production
OIDC_ALLOWED_ORIGINS=* # INSECURE6. Token Storage
Client-side storage guidelines:
| Storage Type | Access Tokens | Refresh Tokens | ID Tokens |
|---|---|---|---|
| Memory | Best | No | OK |
| LocalStorage | XSS risk | Never | Low risk |
| SessionStorage | OK | Never | OK |
| HttpOnly Cookie | Best | Best | OK |
| Secure Cookie | Best | Best | OK |
7. Client Secret Management
Generate strong client secrets:
# 64-character random secret
openssl rand -hex 32Hash secrets before storage:
import "golang.org/x/crypto/bcrypt"
hashedSecret, _ := bcrypt.GenerateFromPassword([]byte(secret), bcrypt.DefaultCost)Production Checklist
Before deploying to production:
-
OIDC_CRYPTO_KEYis secure and stored in secret manager - All redirect URIs use HTTPS (no http://)
- PKCE is required for public clients
- Token lifetimes are configured appropriately
- Rate limiting is enabled
- CORS is configured with specific origins (no wildcard)
- Client secrets are hashed with bcrypt
- Monitoring and alerting are configured
- Backup and recovery procedures are documented
- Key rotation procedure is documented
- Security audit has been performed
Troubleshooting
Common Issues
Issue: “invalid_client” error
- Check client_id and client_secret are correct
- Verify client exists in database
- Ensure client is active (
is_active = true)
Issue: “invalid_redirect_uri” error
- Redirect URI must exactly match registered URI
- Check for trailing slashes
- Verify protocol (http vs https)
Issue: “invalid_grant” error
- Authorization code may have expired (5 min default)
- Code may have already been used (one-time use)
- Verify code_verifier matches code_challenge (PKCE)
Issue: Signing key errors
- Bootstrap keys:
make db migrate up - Verify OIDC_CRYPTO_KEY is set correctly
- Check database connectivity
Issue: Token refresh fails
- Verify refresh token has not expired
- Check client_id matches original authorization
- Ensure refresh token hasn’t been revoked