Rudra Documentation
Rudra is a managed authentication platform powered by Keycloak. It gives you Clerk-level features — user management, SSO, organizations, webhooks, RBAC, analytics, and coupons — all running on your own infrastructure.
🔐 Full Auth Stack
Email/password, social login, magic links, MFA, SAML & OIDC enterprise SSO — all configurable per project.
🏢 B2B Organizations
Multi-tenant orgs with member roles, invitations, and domain-based auto-join for enterprise customers.
🎟️ Coupon System
Create discount codes with plan restrictions, usage limits, and expiry. Applied during project creation.
📊 Analytics & Webhooks
User signup trends, login tracking, webhook delivery to your apps, and a full activity audit log.
Quick Start
Rudra runs entirely via Docker Compose. One command spins up 6 services:
git clone <your-repo>
cd keycloak-saas
docker compose up --build
Wait approximately 2 minutes for all services to initialize (Keycloak takes the longest). Then open:
| Service | URL | Purpose |
|---|---|---|
| Dashboard | http://localhost:3000 | Your Rudra control panel |
| API Docs | http://localhost:8000/docs | Interactive Swagger API docs |
| Keycloak | http://localhost:8080 | Keycloak admin console (admin / admin) |
Architecture
Keycloak
IAM engine handling realms, users, authentication flows, sessions, identity providers, and token issuance.
PostgreSQL
Keycloak's persistent store for realm configs, users, credentials, and sessions.
MongoDB
Tenant metadata, organizations, invitations, webhooks, coupons, analytics events, and activity logs.
Redis
Session caching, rate limiting, and webhook delivery queues.
FastAPI
Management API layer (50+ endpoints) wrapping Keycloak Admin API with plan enforcement and business logic.
React + Nginx
Dashboard frontend served via nginx, which also proxies /api/ requests to FastAPI.
1. Create Your Account
When you open the dashboard at http://localhost:3000, you'll see the login screen.
Click "Create one"
At the bottom of the login form, click the signup link.
Fill in your details
Enter your full name, email, company name (optional), and a password (minimum 8 characters).
You're in!
You'll be redirected to the Dashboard with your stats overview. This is your platform admin account — it manages all your authentication projects.
2. Create a Project
A "project" in Rudra maps to a Keycloak realm. Each project is an isolated authentication environment for one of your applications.
Select a Plan
Choose from Free, Pro ($25/mo), Business ($99/mo), or Enterprise ($499/mo). Each plan determines user limits, features, and quotas. You can upgrade later.
Name Your Project
Enter a display name (e.g. "My SaaS App") and a realm identifier (auto-generated, e.g. my-saas-app). The realm ID is used in your Keycloak auth URLs.
Apply a Coupon (optional)
If you have a discount code, enter it in the coupon field. It validates in real-time and the Order Summary sidebar shows your discounted price.
Click "Create Project"
Rudra provisions a new Keycloak realm with default auth settings, and you're taken to the project management page.
Your Auth URL
After creation, your users will authenticate at:
http://localhost:8080/realms/{your-realm-id}/protocol/openid-connect/auth
?client_id=YOUR_CLIENT_ID
&response_type=code
&redirect_uri=http://localhost:3001/callback
3. Manage Users
Click into your project and select the Users tab. Here you can create, search, view, impersonate, and delete users.
Create a User
Click "+ Add User" and fill in:
Username: janedoe
Email: [email protected]
First Name: Jane
Last Name: Doe
Password: ••••••••
The user is created in Keycloak and a user.created webhook is fired.
User Actions
| Action | Description | Plan |
|---|---|---|
| 👁️ View | See user details, active sessions, assigned roles | All |
| 🔑 Impersonate | Log in as this user for debugging | Pro+ |
| 🚪 Revoke Sessions | Force logout from all devices | All |
| 🗑️ Delete | Permanently remove user and all their sessions | All |
Search & Filter
Use the search bar to filter by username, email, first name, or last name. The search queries Keycloak's user API directly.
4. Set Up SSO
The SSO tab lets you add social login providers and enterprise SAML connections.
Add Google Login
Get Google OAuth credentials
Go to Google Cloud Console → Create OAuth 2.0 Client ID. Set the redirect URI to: http://localhost:8080/realms/{realm}/broker/google/endpoint
Add in Rudra
Click "+ OIDC Provider" → Select "Google" → Enter your Client ID and Client Secret → Click "Add Provider"
Add GitHub Login
Same flow as Google. Create an OAuth App at GitHub Developer Settings with callback URL: http://localhost:8080/realms/{realm}/broker/github/endpoint
Add Enterprise SAML
Business+ Click "+ SAML Provider" and provide:
Alias: okta-saml
Entity ID: https://www.okta.com/exk123abc
SSO URL: https://company.okta.com/app/abc/sso/saml
Signing Certificate: (paste X.509 cert from IdP)
5. Organizations (B2B)
Pro+ Organizations enable multi-tenant B2B features. Each org has members with roles and optional domain-based auto-join.
Create an Organization
Name: Acme Corp
Slug: acme-corp (auto-generated)
Auto-join Domains: acme.com, subsidiary.com
Any user whose email ends in @acme.com can auto-join this org.
Invite Members
Click "Invite", enter an email, select the target org, and choose a role (admin or member). Invitations expire in 7 days.
Organization via API
Create OrganizationPOST /api/tenants/{realm}/organizations { "name": "Acme Corp", "slug": "acme-corp", "allowed_email_domains": ["acme.com"] }
Add MemberPOST /api/tenants/{realm}/organizations/acme-corp/members { "user_id": "keycloak-user-uuid", "role": "admin" }
6. Roles & RBAC
The Roles tab lets you create custom roles and assign them to users. Roles are stored as Keycloak realm roles.
Create a Role
POST /api/tenants/{realm}/roles
{
"name": "editor",
"description": "Can edit content but not manage users"
}
Assign Role to User
POST /api/tenants/{realm}/users/{user_id}/roles/editor
Roles appear in the user's JWT token, so your app can check permissions:
// In your app's JWT payload:
{
"realm_access": {
"roles": ["editor", "default-roles-my-app"]
}
}
7. Webhooks
Pro+ Webhooks send real-time HTTP notifications to your app when events occur.
Create a Webhook
In the Webhooks tab, click "Add Endpoint" and select the events you want:
| Event | When Fired |
|---|---|
user.created | New user registered or created via API |
user.updated | User profile changed |
user.deleted | User removed |
organization.created | New org created |
organizationMembership.created | Member added to org |
session.revoked | User session terminated |
invitation.created | New invitation sent |
Webhook Payload
POST https://your-app.com/webhooks
Headers:
Content-Type: application/json
X-Webhook-Secret: a1b2c3...your-secret...
Body:
{
"type": "user.created",
"data": {
"username": "janedoe",
"email": "[email protected]",
"user_id": "kc-uuid-here"
}
}
Verify Webhooks
Each webhook endpoint gets a unique secret (shown once at creation). Verify it in your app:
# Python / FastAPI
@app.post("/webhooks")
async def handle_webhook(request: Request):
secret = request.headers.get("X-Webhook-Secret")
if secret != os.environ["RUDRA_WEBHOOK_SECRET"]:
raise HTTPException(401, "Invalid secret")
body = await request.json()
print(f"Event: {body['type']}, Data: {body['data']}")
return {"ok": True}
8. Coupons & Discounts
Create discount codes that customers can apply when creating projects. Accessible from the Coupons page in the sidebar.
Create a Coupon
Navigate to Coupons
Click "Coupons" in the sidebar, then "New Coupon".
Configure the coupon
Set code (e.g. WELCOME50), discount %, max redemptions, valid plans, and optional expiry.
Coupon Fields
| Field | Description | Example |
|---|---|---|
| Code | Unique coupon code (auto-uppercased) | STARTUP30 |
| Discount % | Percentage off (1–100) | 30 |
| Max Redemptions | Usage limit (-1 = unlimited) | 100 |
| Valid Plans | Restrict to specific plans (empty = all) | pro, business |
| Expires In | Days until expiry (0 = never) | 90 |
How Customers Use Coupons
When creating a project, there's a "Have a coupon code?" field on step 2. The code validates in real-time and the Order Summary shows the discount:
Plan: Business $99/mo
Coupon: STARTUP30 -30%
─────────────────────────────────────
Total: $69/mo
You save $30/mo
Track Redemptions
Click the 👁️ icon on any coupon to see who redeemed it, on which project, and when.
9. Project Settings
The Settings tab in each project controls authentication behavior, branding, and plan management.
Authentication Toggles
| Setting | Description | Plan |
|---|---|---|
| Password Authentication | Enable/disable email + password sign-in | All |
| Social Login | Enable Google, GitHub, etc. | All |
| Magic Links | Passwordless email login | Pro+ |
| Multi-Factor Auth | Require MFA for all users | All |
| Block Disposable Emails | Reject mailinator, tempmail, etc. | Pro+ |
| Password Breach Detection | Check against known breaches | Pro+ |
| Bot Protection | Rate limiting & CAPTCHA | Pro+ |
Branding
Set a primary color (used in Keycloak login themes) and a logo URL per project.
Plan Management
Upgrade or downgrade your project's plan directly from Settings. Changes take effect immediately.
10. Analytics Dashboard
Pro+ The project Overview tab shows key metrics:
User Signups
Daily bar chart showing new user registrations over the last 30 days.
Login Activity
Total successful logins and failed login attempts from Keycloak events.
Resource Counts
Users, applications, SSO providers, organizations, roles, webhooks — at a glance.
Activity Log
Platform-wide audit trail on the Dashboard: who did what, when, on which project.
Connect Your Application
After creating a project and configuring auth, you need to register your app as an OIDC client and point your frontend to Keycloak.
Step 1: Register an Application
Go to your project → Applications tab → Click "+ Add Application":
Client ID: my-web-app
Protocol: OpenID Connect
Redirect URIs: http://localhost:3001/callback
Step 2: Configure Your Frontend
Use any OIDC client library. Here's the configuration your app needs:
{
"authority": "http://localhost:8080/realms/YOUR_REALM",
"client_id": "my-web-app",
"redirect_uri": "http://localhost:3001/callback",
"response_type": "code",
"scope": "openid profile email"
}
Step 3: Verify Tokens
Your backend validates JWTs using Keycloak's public key:
# JWKS endpoint (auto-discovered):
http://localhost:8080/realms/YOUR_REALM/protocol/openid-connect/certs
Code Examples
React (oidc-client-ts)
import { UserManager } from 'oidc-client-ts';
const userManager = new UserManager({
authority: 'http://localhost:8080/realms/my-app',
client_id: 'my-web-app',
redirect_uri: 'http://localhost:3001/callback',
response_type: 'code',
scope: 'openid profile email',
});
// Login
userManager.signinRedirect();
// Callback page
const user = await userManager.signinRedirectCallback();
console.log('Token:', user.access_token);
console.log('Roles:', user.profile.realm_access?.roles);
Python Backend (JWT Verification)
import jwt
import requests
KEYCLOAK_URL = "http://localhost:8080/realms/my-app"
# Fetch Keycloak public key
jwks = requests.get(f"{KEYCLOAK_URL}/protocol/openid-connect/certs").json()
def verify_token(token: str):
header = jwt.get_unverified_header(token)
key = next(k for k in jwks["keys"] if k["kid"] == header["kid"])
public_key = jwt.algorithms.RSAAlgorithm.from_jwk(key)
return jwt.decode(token, public_key, algorithms=["RS256"],
audience="my-web-app")
Node.js / Express
const { Issuer } = require('openid-client');
async function setup() {
const keycloak = await Issuer.discover(
'http://localhost:8080/realms/my-app'
);
const client = new keycloak.Client({
client_id: 'my-web-app',
redirect_uris: ['http://localhost:3001/callback'],
response_types: ['code'],
});
// Generate login URL
const url = client.authorizationUrl({ scope: 'openid profile email' });
// Exchange code for tokens
const tokenSet = await client.callback(
'http://localhost:3001/callback',
{ code: req.query.code }
);
console.log('Access Token:', tokenSet.access_token);
}
Webhook Handler (Python)
from fastapi import FastAPI, Request, HTTPException
import os
app = FastAPI()
@app.post("/webhooks/rudra")
async def webhook(request: Request):
# Verify the webhook secret
secret = request.headers.get("X-Webhook-Secret")
if secret != os.environ["WEBHOOK_SECRET"]:
raise HTTPException(401)
event = await request.json()
if event["type"] == "user.created":
# Provision user in your database
print(f"New user: {event['data']['email']}")
elif event["type"] == "organization.created":
# Set up tenant workspace
print(f"New org created")
return {"received": True}
API Reference
All endpoints require a Bearer token (from /api/auth/login) except register and login. Full interactive docs at http://localhost:8000/docs.
Authentication
Projects (Tenants)
Users
Sessions
Organizations
SSO / Identity Providers
Roles
Applications (Clients)
Webhooks
Coupons
Analytics & Dashboard
Plans & Limits
Troubleshooting
"Backend service unavailable" on login
The backend hasn't started yet. Wait 1-2 minutes after docker compose up for Keycloak to finish initializing. Check with:
docker compose ps # All services should show "healthy"
docker compose logs backend # Check for Python errors
"Internal Server Error" on register
Check docker compose logs backend for the actual Python traceback. Common causes:
- MongoDB connection failed — wait for MongoDB healthcheck to pass
- Dependency issue — rebuild with
docker compose build --no-cache backend
Keycloak takes forever to start
Keycloak 24 on first boot imports its database schema which takes 60-120 seconds. The backend won't start until Keycloak's healthcheck passes. Subsequent restarts are much faster.
Port conflicts
If ports 3000, 5432, 6379, 8000, 8080, or 27017 are in use, stop conflicting services or edit docker-compose.yml ports.
Clean restart
docker compose down -v # Stop everything and delete data volumes
docker compose up --build # Rebuild and start fresh
Check API directly
# Health check
curl http://localhost:8000/api/health
# Register via CLI
curl -X POST http://localhost:8000/api/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"test1234","name":"Test"}'
# Login
curl -X POST http://localhost:8000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"test1234"}'