Skip to content

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.

  1. User accesses your application (e.g., https://cluster.example.com/myapp/)
  2. Istio checks for a valid JWT token
  3. If no token: oauth2-proxy redirects to Dex → Keycloak login
  4. After login: Dex issues a JWT, oauth2-proxy sets it as a cookie
  5. 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⚓︎

  1. Access the Keycloak admin console at https://<domain>/auth/admin/
  2. Select the prokube realm
  3. Go to ClientsCreate client
  4. Configure the client:
  5. Client ID: Choose a unique ID (e.g., myapp-client)
  6. Client authentication: On (confidential client)
  7. Valid redirect URIs: Your app's callback URL (e.g., https://<domain>/myapp/callback)
  8. Web origins: https://<domain>
  9. 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.yaml
  • paas/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:

  1. Create the group/role in Keycloak
  2. Assign users to the group
  3. The group appears in the JWT groups claim
  4. 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)

  1. Check that the user has groups assigned in Keycloak
  2. User may need to log out and log back in to refresh their token
  3. Verify the kubeflow-groups header 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 redirectURIs in the Dex static client configuration

User groups not appearing in JWT⚓︎

Applies to: SSO with Keycloak, Platform OIDC Integration

  1. Check that the user has groups assigned in Keycloak
  2. Verify your application requests the groups scope
  3. User may need to log out and log back in to refresh their token
  4. 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

  1. Check that your app's redirect URI matches exactly what's registered in Keycloak
  2. Verify the issuer URL uses the correct realm: https://<domain>/auth/realms/prokube
  3. 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_access scope for refresh tokens
  • Implements token refresh logic
  • Handles token expiration gracefully