Authentication
Secure your Kanchi instance with basic auth or OAuth (Google, GitHub).
Authentication is optional and disabled by default. When enabled, users must authenticate before accessing Kanchi. Choose basic auth for simple username/password login, or OAuth for single sign-on with Google or GitHub.
Authentication is opt-in. Existing deployments continue working without changes. Set AUTH_ENABLED=true to require authentication.
Why authenticate?
Use authentication when:
- Kanchi is exposed on a public network
- Multiple people access the same instance
- You need audit trails (who did what)
- Corporate policy requires authentication
- You want email-based access control
Skip authentication when:
- Running locally on trusted networks
- Single-user development environments
- Behind existing authentication (VPN, SSO gateway)
Authentication methods
Kanchi supports three authentication methods. Enable any combination:
Basic Auth
Username and password. Simple setup, no external dependencies.
Google OAuth
Single sign-on with Google accounts. Requires Google Cloud setup.
GitHub OAuth
Single sign-on with GitHub accounts. Requires GitHub OAuth app.
All methods support email pattern restrictions to limit who can access Kanchi.
Basic Authentication
Username and password authentication. Best for small teams or when OAuth isn't available.
Generate a password hash
For production, never use plain passwords. Generate a PBKDF2 hash:
python3 << 'SCRIPT'
import os, base64, hashlib
# Set your password here or via environment variable
password = os.environ.get('KANCHI_PASSWORD', 'your-secure-password').encode('utf-8')
# Generate random salt
salt = base64.b64encode(os.urandom(16)).decode('ascii').strip('=')
# 260,000 iterations (OWASP recommendation)
iterations = 260000
# Derive key using PBKDF2-SHA256
derived_key = hashlib.pbkdf2_hmac('sha256', password, salt.encode('utf-8'), iterations)
# Format as Django-compatible hash
hash_string = f"pbkdf2_sha256${iterations}${salt}${base64.b64encode(derived_key).decode('ascii')}"
print("\nYour password hash:")
print(hash_string)
print("\nAdd this to your environment:")
print(f"export BASIC_AUTH_PASSWORD_HASH='{hash_string}'")
SCRIPTExample output:
pbkdf2_sha256$260000$xQb1234abcd$N8DkMGHFJK...Development only: You can use BASIC_AUTH_PASSWORD=plain-text for local testing, but never in production.
Generate secrets
Create secure random secrets for token signing:
export SESSION_SECRET_KEY=$(openssl rand -hex 32)
export TOKEN_SECRET_KEY=$(openssl rand -hex 32)These secrets are critical. If they leak, attackers can forge authentication tokens. Store them securely (environment variables, secrets manager, etc.).
Configure environment variables
# Enable authentication
export AUTH_ENABLED=true
# Enable basic auth
export AUTH_BASIC_ENABLED=true
# Set credentials
export BASIC_AUTH_USERNAME=kanchi-admin
export BASIC_AUTH_PASSWORD_HASH='pbkdf2_sha256$260000$...'
# Secrets (from Step 2)
export SESSION_SECRET_KEY=<your-random-secret>
export TOKEN_SECRET_KEY=<your-random-secret>
# CORS (allow your frontend)
export ALLOWED_ORIGINS=http://localhost:3000,https://kanchi.example.com
export ALLOWED_HOSTS=localhost,127.0.0.1,kanchi.example.com
# Optional: Restrict by email pattern
export ALLOWED_EMAIL_PATTERNS='*@example.com,*@company.org'The username becomes the user's email in Kanchi. If BASIC_AUTH_USERNAME=admin, the user's email is admin (no @domain).
Restart Kanchi
docker compose restartdocker restart kanchi# Stop running processes (Ctrl+C)
# Then restart:
make devTest authentication
Navigate to http://localhost:3000 (or your Kanchi URL). You'll be redirected to /login.
Enter your credentials:
- Username:
kanchi-admin(or whatever you set) - Password: The password you hashed in Step 1
After successful login, you'll be redirected to the dashboard.
Google OAuth
Single sign-on with Google accounts. Users click "Continue with Google" and authenticate with their Google account.
Create Google OAuth credentials
-
Go to Google Cloud Console
-
Create a new project or select existing
-
Navigate to APIs & Services > Credentials
-
Click Create Credentials > OAuth 2.0 Client ID
-
Configure consent screen if prompted
-
Set Application type to Web application
-
Add Authorized redirect URI:
https://kanchi.example.com/api/auth/oauth/google/callbackReplace
kanchi.example.comwith your actual backend URL. -
Click Create
-
Copy Client ID and Client Secret
The redirect URI must exactly match your OAUTH_REDIRECT_BASE_URL + /api/auth/oauth/google/callback. Trailing slashes matter. Protocols (http vs https) matter.
Generate secrets
export SESSION_SECRET_KEY=$(openssl rand -hex 32)
export TOKEN_SECRET_KEY=$(openssl rand -hex 32)Configure environment variables
# Enable authentication
export AUTH_ENABLED=true
# Enable Google OAuth
export AUTH_GOOGLE_ENABLED=true
export GOOGLE_CLIENT_ID='123456-abcdef.apps.googleusercontent.com'
export GOOGLE_CLIENT_SECRET='GOCSPX-your-client-secret'
# OAuth redirect base URL (your backend's public URL)
export OAUTH_REDIRECT_BASE_URL='https://kanchi.example.com'
# Secrets
export SESSION_SECRET_KEY=<your-random-secret>
export TOKEN_SECRET_KEY=<your-random-secret>
# CORS
export ALLOWED_ORIGINS=https://kanchi.example.com
export ALLOWED_HOSTS=kanchi.example.com
# Optional: Restrict to specific email domains
export ALLOWED_EMAIL_PATTERNS='*@example.com,*@company.org'
# Optional: Token lifetimes (defaults shown)
export ACCESS_TOKEN_LIFETIME_MINUTES=30
export REFRESH_TOKEN_LIFETIME_HOURS=24OAUTH_REDIRECT_BASE_URL is the backend URL (where the API runs), not the frontend URL. This is where OAuth providers redirect after authentication.
Restart Kanchi
docker compose restartTest OAuth flow
- Navigate to
https://kanchi.example.com/login - Click Continue with Google
- A popup opens with Google sign-in
- Sign in and approve access
- Popup closes, you're redirected to dashboard
If it works: you're done! If not, check troubleshooting below.
GitHub OAuth
Single sign-on with GitHub accounts. Users click "Continue with GitHub" and authenticate with GitHub.
Create GitHub OAuth app
-
Click New OAuth App
-
Fill in details:
-
Application name: Kanchi
-
Homepage URL:
https://kanchi.example.com -
Authorization callback URL:
https://kanchi.example.com/api/auth/oauth/github/callback
-
-
Click Register application
-
Copy Client ID
-
Click Generate a new client secret
-
Copy Client Secret immediately (shown only once)
The callback URL must exactly match your OAUTH_REDIRECT_BASE_URL + /api/auth/oauth/github/callback.
Generate secrets
export SESSION_SECRET_KEY=$(openssl rand -hex 32)
export TOKEN_SECRET_KEY=$(openssl rand -hex 32)Configure environment variables
# Enable authentication
export AUTH_ENABLED=true
# Enable GitHub OAuth
export AUTH_GITHUB_ENABLED=true
export GITHUB_CLIENT_ID='Iv1.1234567890abcdef'
export GITHUB_CLIENT_SECRET='your-client-secret-here'
# OAuth redirect base URL (your backend's public URL)
export OAUTH_REDIRECT_BASE_URL='https://kanchi.example.com'
# Secrets
export SESSION_SECRET_KEY=<your-random-secret>
export TOKEN_SECRET_KEY=<your-random-secret>
# CORS
export ALLOWED_ORIGINS=https://kanchi.example.com
export ALLOWED_HOSTS=kanchi.example.com
# Optional: Restrict to specific email domains
export ALLOWED_EMAIL_PATTERNS='*@example.com,*@mycompany.com'Restart Kanchi
docker compose restartTest OAuth flow
- Navigate to
https://kanchi.example.com/login - Click Continue with GitHub
- Sign in with GitHub and authorize
- You're redirected back to Kanchi dashboard
First-time users are automatically created in Kanchi's database.
Email access control
Restrict who can access Kanchi based on email patterns. Works with all authentication methods.
Pattern syntax:
# Allow specific domain
ALLOWED_EMAIL_PATTERNS='*@example.com'
# Allow multiple domains
ALLOWED_EMAIL_PATTERNS='*@example.com,*@company.org'
# Allow specific user
ALLOWED_EMAIL_PATTERNS='admin@example.com'
# Complex patterns (Unix fnmatch wildcards)
ALLOWED_EMAIL_PATTERNS='*@example.com,admin@*.org,user+*@company.com'How it works:
- User authenticates with provider (Google, GitHub, or basic auth)
- Kanchi extracts email from authentication response
- Email is lowercased and checked against patterns
- If no match: login fails with
403 Forbidden - If match or empty pattern: login succeeds
Examples:
*@example.commatchesalice@example.com,bob@example.comadmin@*matchesadmin@example.com,admin@company.org*@*.example.commatchesuser@subdomain.example.com
Empty or unset ALLOWED_EMAIL_PATTERNS allows all emails. For production, always set explicit patterns.
Multiple authentication methods
Enable multiple methods simultaneously. Users choose their preferred method on the login page.
# Enable all three methods
export AUTH_ENABLED=true
# Basic auth
export AUTH_BASIC_ENABLED=true
export BASIC_AUTH_USERNAME='admin'
export BASIC_AUTH_PASSWORD_HASH='pbkdf2_sha256$...'
# Google OAuth
export AUTH_GOOGLE_ENABLED=true
export GOOGLE_CLIENT_ID='...'
export GOOGLE_CLIENT_SECRET='...'
# GitHub OAuth
export AUTH_GITHUB_ENABLED=true
export GITHUB_CLIENT_ID='...'
export GITHUB_CLIENT_SECRET='...'
# Common settings
export OAUTH_REDIRECT_BASE_URL='https://kanchi.example.com'
export ALLOWED_EMAIL_PATTERNS='*@example.com'
export SESSION_SECRET_KEY=$(openssl rand -hex 32)
export TOKEN_SECRET_KEY=$(openssl rand -hex 32)The login page shows buttons for all enabled methods. Email restrictions apply to all methods.
Troubleshooting
Production checklist
Before deploying authentication to production:
- Secrets are random — Never use
change-meor default values - Use HTTPS — All production URLs should use
https:// - Password hashes — Never use
BASIC_AUTH_PASSWORDin production - CORS configured — Set specific
ALLOWED_ORIGINS, not* - Email restrictions — Set
ALLOWED_EMAIL_PATTERNSto limit access - OAuth redirect URIs match — Verify in provider console
- Secrets are secure — Store in secrets manager or encrypted environment
- Test token refresh — Verify tokens refresh before expiry
- Monitor failed logins — Check logs for authentication errors
- Database backups — Ensure user data is backed up
How authentication works
Token system:
- User logs in (basic auth or OAuth)
- Backend generates two tokens:
- Access token (30 min default) — Used for API requests
- Refresh token (24 hours default) — Used to get new access tokens
- Tokens are signed with
TOKEN_SECRET_KEYusing HMAC-SHA256 - Token hashes (not tokens themselves) are stored in database
- Frontend stores tokens in localStorage
- Frontend includes access token in
Authorization: Bearerheader - Backend validates token on every request
- When access token expires, frontend automatically refreshes it
- When refresh token expires, user must re-login
OAuth flow:
- User clicks "Continue with Google/GitHub"
- Popup opens with provider authorization page
- User approves access
- Provider redirects to Kanchi callback URL with authorization code
- Backend exchanges code for provider's access token
- Backend fetches user info from provider
- Backend checks email against
ALLOWED_EMAIL_PATTERNS - User is created/updated in Kanchi database
- Kanchi access + refresh tokens are generated
- Tokens sent to frontend via postMessage
- Frontend stores tokens and completes login
Security features:
- Tokens are never stored in plain text (only SHA256 hashes)
- PBKDF2 password hashing with 260,000 iterations
- CSRF protection via OAuth state tokens
- Token expiration enforced on every request
- Automatic token refresh on 401 errors
- Email pattern allowlist prevents unauthorized access