Authentication Integration⚓︎
This guide covers how to integrate applications with prokube's authentication system.
Who Is This For?⚓︎
This guide covers three different scenarios:
| Scenario | Description | Section |
|---|---|---|
| Simple auth (headers) | You want your app protected by login, and will read user info from HTTP headers | Using Platform Authentication |
| SSO with Keycloak | Your app has built-in OIDC support and you want it to handle authentication itself | Using SSO with Keycloak |
| Platform developers | You're adding a new component to prokube itself (integrated with Dex) | Platform OIDC Integration |
Most users should start with "Simple auth"—it requires no configuration and works with any application. Use "SSO with Keycloak" if your app needs to handle OIDC directly.
Authentication Architecture⚓︎
Keycloak is the identity provider where users and groups are managed. There are two ways to authenticate:
Path 1: Platform Authentication (headers)
User → oauth2-proxy → Dex → Keycloak
↓
Your app reads HTTP headers (kubeflow-userid, kubeflow-groups)
Path 2: SSO with Keycloak (direct OIDC)
User → Your app → Keycloak
↓
Your app validates tokens and handles sessions itself
Path 1 is simpler (no app changes needed). Path 2 gives your app full control over the authentication flow.
Dex is an OIDC broker used by platform components (MLflow, ArgoCD, Grafana). User applications typically don't need Dex—they can connect to Keycloak directly.
Using Platform Authentication⚓︎
This section is for users deploying applications on prokube who want authentication without modifying platform configuration.
How It Works⚓︎
Applications routed through kubeflow/kubeflow-gateway are automatically protected by oauth2-proxy. Users must log in before accessing your application—no configuration needed on your part.
- User accesses your application (e.g.,
https://cluster.example.com/myapp/) - Istio checks for a valid JWT token
- If no token: oauth2-proxy redirects to Dex → Keycloak login
- After login: Dex issues a JWT, oauth2-proxy sets it as a cookie
- Subsequent requests include the JWT and reach your application
This is the default behavior. If you followed the Networking Application Integration guide and used kubeflow/kubeflow-gateway, your application is already protected.
Reading User Information⚓︎
After authentication, user information is available in HTTP headers:
| Header | Content | Example |
|---|---|---|
kubeflow-userid |
User's email address | user@example.com |
kubeflow-groups |
Comma-separated group list | kubeflow-user,monitor-viewer |
Your application can read these headers to identify the user:
# Python/Flask example
from flask import request
@app.route('/api/data')
def get_data():
user_email = request.headers.get('kubeflow-userid')
user_groups = request.headers.get('kubeflow-groups', '').split(',')
# Use for authorization decisions
if 'admin' in user_groups:
return get_admin_data()
return get_user_data(user_email)
// Go example
func handler(w http.ResponseWriter, r *http.Request) {
userEmail := r.Header.Get("kubeflow-userid")
userGroups := strings.Split(r.Header.Get("kubeflow-groups"), ",")
// ...
}
Summary⚓︎
This approach is ideal when:
- You're deploying an application on prokube and want it protected
- Your application doesn't have built-in OIDC support (or you don't want to configure it)
- You're fine with header-based user identification
No platform changes required—deploy your app with a VirtualService pointing to kubeflow/kubeflow-gateway.
Using SSO with Keycloak⚓︎
If your application has built-in OIDC/OAuth2 support and you want it to handle authentication itself (instead of relying on headers), you can register it directly with Keycloak.
This approach:
- Requires no platform code changes
- Lets you self-service via the Keycloak admin console
- Gives you full control over OIDC configuration
- Means your app handles authentication independently from the platform
Step 1: Register a Client in Keycloak⚓︎
- Access the Keycloak admin console at
https://<domain>/auth/admin/ - Select the
prokuberealm - Go to Clients → Create client
- Configure the client:
- Client ID: Choose a unique ID (e.g.,
myapp-client) - Client authentication: On (confidential client)
- Valid redirect URIs: Your app's callback URL (e.g.,
https://<domain>/myapp/callback) - Web origins:
https://<domain> - After creation, go to the Credentials tab to get the client secret
Step 2: Configure Your Application⚓︎
Configure your application to use Keycloak as the OIDC provider:
| Endpoint | URL |
|---|---|
| Issuer | https://<domain>/auth/realms/prokube |
| Discovery | https://<domain>/auth/realms/prokube/.well-known/openid-configuration |
| Authorization | https://<domain>/auth/realms/prokube/protocol/openid-connect/auth |
| Token | https://<domain>/auth/realms/prokube/protocol/openid-connect/token |
| User Info | https://<domain>/auth/realms/prokube/protocol/openid-connect/userinfo |
| JWKS | https://<domain>/auth/realms/prokube/protocol/openid-connect/certs |
Scopes to request:
openid profile email groups
Example environment configuration:
env:
- name: OIDC_ISSUER_URL
value: "https://cluster.example.com/auth/realms/prokube"
- name: OIDC_CLIENT_ID
valueFrom:
secretKeyRef:
name: myapp-oidc-secret
key: client-id
- name: OIDC_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: myapp-oidc-secret
key: client-secret
- name: OIDC_REDIRECT_URI
value: "https://cluster.example.com/myapp/callback"
- name: OIDC_SCOPES
value: "openid,profile,email,groups"
Step 3: Exempt from Gateway Authentication⚓︎
Since your app handles its own authentication, you need to bypass the platform's gateway auth. Two AuthorizationPolicies control access at the gateway, and your path must be added to both:
| File | What it does | If your path is missing |
|---|---|---|
authorizationpolicy.istio-ingressgateway-require-jwt.yaml |
Denies requests without a valid JWT | Users get 403 Forbidden |
authorizationpolicy.istio-ingressgateway-oauth2-proxy.yaml |
Redirects to platform login when no auth header | Users get redirected to platform login instead of your app's login |
Both files are in paas/auth-system/oauth2-proxy/patches/ and have the same notPaths structure. Add your path to both files:
# Add to BOTH files - notPaths list (partial)
notPaths:
- /dex/*
- /dex/**
- /auth/*
- /oauth2/*
- /minio/*
- /argocd/*
- /mlflow/*
- /serving/*
- /myapp/* # ← Add your application path to both files
Both files required
If you only add your path to one file, your app's authentication flow will break. Missing from require-jwt → 403 error. Missing from oauth2-proxy → wrong login page.
JWT Token Structure (Keycloak)⚓︎
Keycloak tokens have a slightly different structure than Dex tokens:
{
"iss": "https://cluster.example.com/auth/realms/prokube",
"sub": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"aud": "myapp-client",
"exp": 1737809552,
"iat": 1737723152,
"email": "user@example.com",
"email_verified": true,
"groups": [
"/pk-user",
"/pk-admin"
],
"preferred_username": "user@example.com",
"given_name": "Test",
"family_name": "User"
}
Group format
Keycloak groups include a leading / (e.g., /pk-user). Your application should handle this when checking group membership.
Platform OIDC Integration (Dex)⚓︎
Platform developers only
This section is for developers adding new components to the prokube platform itself. If you're deploying an application on prokube, use Platform Authentication or SSO with Keycloak instead.
Use this when integrating a new platform component that has built-in OIDC/OAuth2 support and should handle authentication itself. Examples: MLflow, ArgoCD, Grafana, MinIO.
Step 1: Register an OIDC Client in Dex⚓︎
Add your application as a static client in the Dex configuration.
Edit paas/auth-system/dex/overlays/keycloak/config/config.yaml:
staticClients:
# ... existing clients ...
- id: {{ .Env.MYAPP_OIDC_CLIENT_ID }}
name: 'MyApp OIDC Auth Service'
redirectURIs: ["https://{{ .Env.DOMAIN }}/myapp/callback"]
secret: "{{ .Env.MYAPP_OIDC_CLIENT_SECRET }}"
The redirect URI must match exactly what your application is configured to use.
Step 2: Add Secrets to Deploy Script⚓︎
The deploy script auto-generates OIDC secrets for platform components. Add your application to deploy/deploy/deploy_paas.py:
1. Generate a secret in deploy_oidc_secrets():
def deploy_oidc_secrets(self):
# ... existing secrets ...
oidc_secrets["myapp-oidc-secret"] = secrets.token_urlsafe(SECRET_LENGTH)
2. Create a Kubernetes secret for your application:
# Create myapp oidc secret
myapp_metadata = client.V1ObjectMeta(
name="myapp-oidc-secret",
namespace="myapp"
)
myapp_data = {
"OIDC_CLIENT_ID": "myapp-oidc-client",
"OIDC_CLIENT_SECRET": oidc_secrets["myapp-oidc-secret"],
}
deploy_secret(self.k8s_core_api, myapp_metadata, myapp_data)
3. Add the client to Dex's environment variables:
dex_data = {
# ... existing clients ...
"MYAPP_OIDC_CLIENT_ID": "myapp-oidc-client",
"MYAPP_OIDC_CLIENT_SECRET": oidc_secrets["myapp-oidc-secret"],
}
This ensures secrets are generated automatically during deployment and never committed to git.
Step 3: Configure Your Application⚓︎
Configure your application to use Dex as the OIDC provider. The exact configuration depends on your application, but here are the common OIDC endpoints:
| Endpoint | URL |
|---|---|
| Issuer | https://<domain>/dex |
| Discovery | https://<domain>/dex/.well-known/openid-configuration |
| Authorization | https://<domain>/dex/auth |
| Token | https://<domain>/dex/token |
| User Info | https://<domain>/dex/userinfo |
| JWKS | https://<domain>/dex/keys |
Scopes to request:
openid profile email groups offline_access
Example: Reference secrets from your deployment
# In your deployment.yaml
env:
- name: OIDC_ISSUER_URL
value: "https://cluster.example.com/dex"
- name: OIDC_CLIENT_ID
valueFrom:
secretKeyRef:
name: myapp-oidc-secret
key: OIDC_CLIENT_ID
- name: OIDC_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: myapp-oidc-secret
key: OIDC_CLIENT_SECRET
- name: OIDC_REDIRECT_URI
value: "https://cluster.example.com/myapp/callback"
- name: OIDC_SCOPES
value: "openid,profile,email,groups"
The secret keys match what the deploy script creates in Step 2.
Step 4: Exempt from Gateway Authentication⚓︎
Since your application handles its own auth, you need to bypass the gateway's authentication. This is the same process as for SSO with Keycloak—add your path to both AuthorizationPolicy files:
paas/auth-system/oauth2-proxy/patches/authorizationpolicy.istio-ingressgateway-require-jwt.yamlpaas/auth-system/oauth2-proxy/patches/authorizationpolicy.istio-ingressgateway-oauth2-proxy.yaml
See Step 3: Exempt from Gateway Authentication above for the full notPaths list and details.
JWT Token Structure⚓︎
When users authenticate, Dex issues a JWT with this structure:
{
"iss": "https://cluster.example.com/dex",
"sub": "CiRkZThlN2MxOC04YzFmLTQ1MGUtOTE5Mi0...",
"aud": "myapp-client",
"exp": 1737809552,
"iat": 1737723152,
"email": "user@example.com",
"email_verified": true,
"groups": [
"kubeflow-user",
"monitor-viewer",
"myapp-admin"
],
"name": "Test User",
"preferred_username": "test_user"
}
Use the groups claim for authorization decisions in your application.
Adding Custom Groups⚓︎
To use custom groups for your platform component:
- Create the group/role in Keycloak
- Assign users to the group
- The group appears in the JWT
groupsclaim - Your application checks for the group
Groups and Roles Reference⚓︎
User groups are managed in Keycloak. Common platform groups include:
| Group | Purpose |
|---|---|
kubeflow-user |
Basic platform access |
argocd-admin |
ArgoCD administrator |
grafana-admin |
Grafana administrator |
mlflow-admin |
MLflow administrator |
monitor-viewer |
Read-only monitoring access |
monitor-admin |
Full monitoring access |
Group format differs by authentication method
- HTTP headers (Platform Authentication): Groups are comma-separated without prefix:
kubeflow-user,monitor-viewer - Keycloak direct: Groups have a leading
/:/pk-user,/pk-admin - Dex tokens (Platform OIDC): Groups come through without the
/prefix
Example: Complete Platform OIDC Setup⚓︎
Here's a complete example for adding a new platform component with OIDC support:
paas/myapp/
├── base/
│ ├── kustomization.yaml
│ ├── ns.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── virtualservice.yaml
│ └── authorizationpolicy.yaml
Note: OIDC secrets are created by the deploy script, not stored in git.
deployment.yaml (relevant parts):
spec:
template:
spec:
containers:
- name: myapp
env:
- name: OIDC_DISCOVERY_URL
value: "https://cluster.example.com/dex/.well-known/openid-configuration"
- name: OIDC_CLIENT_ID
valueFrom:
secretKeyRef:
name: myapp-oidc-secret
key: OIDC_CLIENT_ID
- name: OIDC_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: myapp-oidc-secret
key: OIDC_CLIENT_SECRET
- name: OIDC_REDIRECT_URI
value: "https://cluster.example.com/myapp/callback"
- name: OIDC_SCOPES
value: "openid,profile,email,groups"
Troubleshooting⚓︎
User groups not appearing in headers⚓︎
Applies to: Platform Authentication (headers)
- Check that the user has groups assigned in Keycloak
- User may need to log out and log back in to refresh their token
- Verify the
kubeflow-groupsheader is being read correctly (it's comma-separated)
"Invalid redirect URI" error⚓︎
Applies to: SSO with Keycloak, Platform OIDC Integration
The redirect URI configured in your application must exactly match one of the URIs registered:
- Keycloak direct: Check the client's "Valid redirect URIs" in Keycloak admin console
- Dex: Check the
redirectURIsin the Dex static client configuration
User groups not appearing in JWT⚓︎
Applies to: SSO with Keycloak, Platform OIDC Integration
- Check that the user has groups assigned in Keycloak
- Verify your application requests the
groupsscope - User may need to log out and log back in to refresh their token
- Keycloak direct: Groups have a leading
/(e.g.,/pk-user)—make sure your app handles this
403 Forbidden after login⚓︎
Applies to: SSO with Keycloak, Platform OIDC Integration
Ensure your application's path is added to the notPaths in the AuthorizationPolicy. Without this, the gateway blocks unauthenticated requests before they reach your app's login flow.
Ask your platform administrator to add your path to:
paas/auth-system/oauth2-proxy/patches/authorizationpolicy.istio-ingressgateway-require-jwt.yaml
Login redirects to wrong URL⚓︎
Applies to: SSO with Keycloak
- Check that your app's redirect URI matches exactly what's registered in Keycloak
- Verify the issuer URL uses the correct realm:
https://<domain>/auth/realms/prokube - If using a path prefix, ensure the redirect URI includes it
Token expired errors⚓︎
Applies to: SSO with Keycloak, Platform OIDC Integration
Tokens have a limited lifetime. Ensure your application:
- Requests the
offline_accessscope for refresh tokens - Implements token refresh logic
- Handles token expiration gracefully