-
Notifications
You must be signed in to change notification settings - Fork 0
Authentication
OntoKit uses Zitadel as its identity provider, implementing OpenID Connect (OIDC) for authentication.
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Browser │────>│ Zitadel │────>│ OntoKit │
│ (Web App) │<────│ (OIDC) │<────│ API │
└─────────────┘ └─────────────┘ └─────────────┘
│
JWT Access Token
Zitadel is included in the Docker Compose setup:
docker compose up -dWait for Zitadel to be ready:
# Check health endpoint
curl http://localhost:8080/debug/readyOpen http://localhost:8080/ui/console in your browser.
Default Admin Credentials:
- Username:
admin@ontokit.localhost - Password:
Admin123!
Run the setup script to create the OIDC application and configure credentials:
./scripts/setup-zitadel.sh --update-envThe script will:
- Wait for Zitadel to be ready
- Create an "OntoKit" project (or reuse existing)
- Create an "OntoKit Web" OIDC application (or reuse existing)
- Update both
ontokit-api/.envandontokit-web/.env.localwith credentials - Prompt to recreate the API container (if running in Docker)
Script flags:
| Flag | Description |
|---|---|
--update-env |
Update .env files with credentials |
--docker-init |
Start Docker stack if not running, then configure |
--force-secrets |
Regenerate client secrets (invalidates existing sessions) |
The script is idempotent — safe to run multiple times. It preserves existing sessions by reusing secrets when the client ID matches.
After the script updates the .env files, restart the services to pick up the new credentials:
# Full Docker mode — recreate API and worker containers
docker compose up -d --force-recreate api worker
# Hybrid mode — restart your local uvicorn process and the Next.js dev serverIf the automatic setup doesn't work, follow these steps:
- Go to Projects > Create Project
- Name:
OntoKit - Important: Leave "Assert Roles on Authentication" and "Check User Grants" disabled (these are advanced features that require additional configuration)
- In the OntoKit project, go to Applications > New
- Select Web Application
- Name:
OntoKit Web - Configure:
-
Redirect URIs:
http://localhost:3000/api/auth/callback/zitadel -
Post Logout URIs:
http://localhost:3000 -
Auth Method:
BASIC(client secret via HTTP Basic Auth) - Dev Mode: Enabled (allows http:// redirect URIs)
-
Redirect URIs:
- Note the Client ID and Client Secret
The API validates JWT tokens from Zitadel using the JWKS endpoint.
- Client sends request with
Authorization: Bearer <token>header - API fetches JWKS from Zitadel (
/.well-known/openid-configuration) - API validates the token signature using the public key
- API extracts user information from token claims
| Claim | Description |
|---|---|
sub |
User ID (unique identifier) |
email |
User's email address |
name |
User's display name |
preferred_username |
Username |
Endpoints that require authentication use the RequiredUser dependency:
from ontokit.core.auth import RequiredUser
@router.post("/projects")
async def create_project(user: RequiredUser):
# user is guaranteed to be authenticated
print(f"Creating project for user: {user.id}")For endpoints that work differently for authenticated vs anonymous users:
from ontokit.core.auth import OptionalUser
@router.get("/projects")
async def list_projects(user: OptionalUser):
if user:
# Show user's private projects too
pass
else:
# Only show public projects
passThe frontend (Next.js) uses NextAuth.js to manage authentication:
import { useSession } from "next-auth/react";
function MyComponent() {
const { data: session } = useSession();
const response = await fetch("/api/v1/projects", {
headers: {
Authorization: `Bearer ${session?.accessToken}`,
},
});
}Use the Device Authorization Grant flow:
# 1. Request device code
curl -X POST http://localhost:8000/api/v1/auth/device/code \
-H "Content-Type: application/json" \
-d '{"client_id": "your-client-id"}'
# Response includes device_code, user_code, and verification_uri
# 2. User visits verification_uri and enters user_code
# 3. Poll for token
curl -X POST http://localhost:8000/api/v1/auth/device/token \
-H "Content-Type: application/json" \
-d '{"client_id": "your-client-id", "device_code": "..."}'| Role | Permissions |
|---|---|
owner |
Full control, can delete project, transfer ownership |
admin |
Manage members, update settings, cannot delete |
editor |
Edit ontology content |
viewer |
Read-only access |
- Public projects: Anyone can view
- Private projects: Only members can view
- Create project: Any authenticated user
- Update project: Owner or admin
- Delete project: Owner only
- Manage members: Owner or admin
- Go to Users > Create User
- Fill in the user details
- Set a password
# Get admin PAT from the Zitadel container
docker cp ontokit-zitadel:/zitadel-data/admin.pat /tmp/admin.pat
PAT=$(cat /tmp/admin.pat)
# Create user
curl -X POST http://localhost:8080/management/v1/users/human \
-H "Authorization: Bearer $PAT" \
-H "Content-Type: application/json" \
-d '{
"userName": "testuser",
"profile": {
"firstName": "Test",
"lastName": "User"
},
"email": {
"email": "test@example.com",
"isEmailVerified": true
},
"password": {
"password": "TestPassword123!"
}
}'The OIDC application hasn't been created in Zitadel. Run the setup script:
./scripts/setup-zitadel.sh --update-envThen recreate the API container: docker compose up -d --force-recreate api worker
This usually means "User Grant Required". The OntoKit project has role checking enabled but users don't have grants. Fix by disabling role check:
# Get admin PAT
docker cp ontokit-zitadel:/zitadel-data/admin.pat /tmp/admin.pat
PAT=$(cat /tmp/admin.pat)
# Get project ID
PROJECT_ID=$(curl -s -H "Authorization: Bearer $PAT" \
-X POST "http://localhost:8080/management/v1/projects/_search" \
-H "Content-Type: application/json" \
-d '{"queries":[{"nameQuery":{"name":"OntoKit","method":"TEXT_QUERY_METHOD_EQUALS"}}]}' \
| python3 -c "import sys, json; print(json.load(sys.stdin)['result'][0]['id'])")
# Disable role check
curl -X PUT "http://localhost:8080/management/v1/projects/$PROJECT_ID" \
-H "Authorization: Bearer $PAT" \
-H "Content-Type: application/json" \
-d '{
"name": "OntoKit",
"projectRoleAssertion": false,
"projectRoleCheck": false
}'- Check that
ZITADEL_ISSUERmatches the Zitadel URL - Verify the token hasn't expired
- Ensure Zitadel is running:
curl http://localhost:8080/debug/ready
The JWKS cache may be stale. Restart the API server.
Ensure CORS_ORIGINS includes your frontend URL:
CORS_ORIGINS=["http://localhost:3000"]Clear cookies and try again. Check that redirect URIs match exactly.
If you reset the database with docker compose down -v, you need to reconfigure Zitadel:
- Start services:
docker compose up -d - Re-run the setup script:
./scripts/setup-zitadel.sh --update-env - Recreate API/worker containers:
docker compose up -d --force-recreate api worker
- API Reference - See all available endpoints
- Development - Development workflow