Quick Start
Get a full CIAM stack running locally in under 5 minutes — PostgreSQL, Redis/Valkey, Kafka, OpenSearch, and the auth server.
Clone the repository
Get the source code and navigate to the project root.
git clone https://github.com/ciamplatform/ciam-auth.git cd ciam-auth
Start infrastructure services
Docker Compose starts PostgreSQL, Redis/Valkey, Kafka, and OpenSearch.
docker compose up -d # Verify services are healthy docker compose ps NAME STATUS ciam-postgres running (healthy) ciam-valkey running (healthy) ciam-kafka running (healthy)
Build and run the auth server
Spring Boot with live reload. Flyway runs schema migrations automatically on startup.
cd ciam-auth mvn spring-boot:run # Output (abbreviated) ... Running Flyway migrations V1, V2, V3... ... TenantContextFilter registered ... Started CiamAuthApplication on port 8080
Verify the discovery endpoint
The OIDC discovery document confirms the server is running correctly.
curl http://localhost:8080/.well-known/openid-configuration | jq . { "issuer": "http://localhost:8080", "authorization_endpoint": "http://localhost:8080/oauth2/authorize", "token_endpoint": "http://localhost:8080/oauth2/token", "jwks_uri": "http://localhost:8080/oauth2/jwks", "userinfo_endpoint": "http://localhost:8080/userinfo" }
Configuration
Key settings in ciam-auth/src/main/resources/application.yml:
ciam: platform: domain: auth.platform.com tenant: default-slug: dev strict-mode: false # set true in production spring: datasource: url: jdbc:postgresql://localhost:5432/ciam_auth hikari.maximum-pool-size: 20 data.redis.host: localhost kafka.bootstrap-servers: localhost:9092
Your First Tenant
curl -X POST http://localhost:8080/admin/tenants \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <admin-token>" \ -d '{ "name": "Acme Corp", "slug": "acme", "mfaRequired": false }'
Authorization Code + PKCE
The recommended flow for web and mobile applications. PKCE protects against authorization code interception attacks.
// 1. Generate code verifier + challenge const verifier = generateRandomString(64) const challenge = await sha256base64url(verifier) // 2. Redirect to authorization endpoint const params = new URLSearchParams({ response_type: 'code', client_id: 'your-client-id', redirect_uri: 'https://app.example.com/callback', scope: 'openid profile email', code_challenge: challenge, code_challenge_method: 'S256' }) window.location.href = `https://acme.auth.platform.com/oauth2/authorize?${params}` // 3. Exchange code for tokens at callback const resp = await fetch('https://acme.auth.platform.com/oauth2/token', { method: 'POST', body: new URLSearchParams({ grant_type: 'authorization_code', code: authCode, client_id: 'your-client-id', code_verifier: verifier }) })
Token Validation
// application.yml spring.security.oauth2.resourceserver.jwt: jwk-set-uri: https://acme.auth.platform.com/oauth2/jwks issuer-uri: https://acme.auth.platform.com // SecurityConfig.java @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/public/**").permitAll() .anyRequest().authenticated()) .oauth2ResourceServer(o -> o.jwt(Customizer.withDefaults())); return http.build(); }
API Reference
All admin endpoints require a Bearer token with admin role. Base URL: https://{tenant}.auth.platform.com/admin
Tenant API
User API
OAuth2 Client API
MFA API
Guide: Enabling MFA
curl -X PUT https://acme.auth.platform.com/admin/tenants/ten_01HX \ -H "Authorization: Bearer <admin-token>" \ -H "Content-Type: application/json" \ -d '{ "mfaRequired": true }'
Guide: Kafka Auth Events
{
"eventType": "LOGIN_SUCCESS",
"tenantId": "acme",
"userId": "user_01HX...",
"mfaMethod": "TOTP",
"timestamp": "2026-04-14T09:31:00Z"
}
Event types: LOGIN_SUCCESS, LOGIN_FAILURE, MFA_CHALLENGED, MFA_SUCCESS, ACCOUNT_LOCKED, USER_CREATED.