Keycloak Configuration - bcgov/eagle-dev-guides GitHub Wiki

Keycloak Configuration

Authentication configuration for EPIC applications using BC Government's Common Sign-In Service (CSS).

Overview

EPIC applications use Keycloak for authentication:

Application Client ID Authentication
eagle-api eagle-api-console Backend JWT validation
eagle-admin eagle-admin-console Staff authentication (IDIR/BCeID)
eagle-public None No authentication (public website)

Keycloak Endpoints:

  • Dev/Test: https://dev.loginproxy.gov.bc.ca/auth
  • Prod: https://loginproxy.gov.bc.ca/auth
  • Realm: eao-epic (shared across all EPIC applications)

Client ID Preservation Pattern

Problem

The /api/config endpoint returns the API's client ID (eagle-api-console), but eagle-admin needs its own (eagle-admin-console).

Solution

eagle-admin's ConfigService preserves KEYCLOAK_CLIENT_ID from env.js:

// eagle-admin/src/app/services/config.service.ts
const preservedClientId = this._config().KEYCLOAK_CLIENT_ID;
this._config.set({ 
  ...this._config(), 
  ...apiConfig, 
  KEYCLOAK_CLIENT_ID: preservedClientId  // Don't use API's value
});

Note: eagle-public does NOT use Keycloak - it's a public website with no authentication.


Helm Configuration

eagle-api Helm Values

# helm/eagle-api/values-dev.yaml
keycloak:
  enabled: true
  url: "https://dev.loginproxy.gov.bc.ca/auth"
  realm: eao-epic
  clientId: eagle-api-console

Deployment Template Injection

Keycloak variables are set directly in deployment (NOT via ConfigMap):

# helm/eagle-api/templates/deployment.yaml
{{- if .Values.keycloak.enabled }}
- name: SSO_ISSUER
  value: "{{ .Values.keycloak.url }}/realms/{{ .Values.keycloak.realm }}"
- name: SSO_JWKSURI
  value: "{{ .Values.keycloak.url }}/realms/{{ .Values.keycloak.realm }}/protocol/openid-connect/certs"
- name: KEYCLOAK_CLIENT_ID
  value: {{ .Values.keycloak.clientId }}
- name: KEYCLOAK_URL
  value: {{ .Values.keycloak.url }}
- name: KEYCLOAK_REALM
  value: {{ .Values.keycloak.realm }}
- name: KEYCLOAK_ENABLED
  value: "true"
{{- end }}

Production Configuration Override (eagle-admin)

Production Issue: Built-in env.js with Incorrect URL

Problem: eagle-admin container image built with dev Keycloak URL hardcoded:

// Wrong dev URL in built-in env.js
KEYCLOAK_URL: "https://dev.loginproxy.gov.bc.ca/auth"

Solution: Mount production env.js via ConfigMap

Create ConfigMap

oc create configmap eagle-admin-env-js \
  --from-literal=env.js='window.globalThis = {
  KEYCLOAK_URL: "https://loginproxy.gov.bc.ca/auth",
  KEYCLOAK_CLIENT_ID: "eagle-admin-console",
  API_LOCATION: "https://projects.eao.gov.bc.ca"
};' \
  -n 6cdc9e-prod

Mount in DeploymentConfig

# Edit eagle-admin DeploymentConfig
oc edit dc eagle-admin -n 6cdc9e-prod

Add volume and volumeMount:

spec:
  template:
    spec:
      containers:
      - name: eagle-admin
        volumeMounts:
        - name: env-js-override
          mountPath: /app/env.js
          subPath: env.js
      volumes:
      - name: env-js-override
        configMap:
          name: eagle-admin-env-js

Result: Container loads production Keycloak configuration, ConfigService preserves KEYCLOAK_CLIENT_ID.


Troubleshooting

"Client not found" Error

Cause: Frontend is using wrong client ID

Diagnosis:

// In browser console (eagle-admin)
ConfigService.config().KEYCLOAK_CLIENT_ID
// Should be: "eagle-admin-console"
// If showing: "eagle-api-console" → Client ID preservation is broken

Fixes:

  1. Check ConfigService: Verify eagle-admin's ConfigService preserves KEYCLOAK_CLIENT_ID from env.js
  2. Check Image Version: Older images may lack ConfigService fix - redeploy with latest test image
  3. Check ConfigMap: In production, verify eagle-admin-env-js ConfigMap exists and is mounted

"Page not found on Red Hat Single Sign-On"

Cause: Realm doesn't exist or incorrect URL

Diagnosis:

# Verify realm exists
curl https://dev.loginproxy.gov.bc.ca/auth/realms/eao-epic/.well-known/openid-configuration

Token Validation Failures

Diagnosis:

# Check eagle-api logs for JWT errors
oc logs -n 6cdc9e-dev deployment/eagle-api | grep -i jwt

# Verify JWKS endpoint
curl https://dev.loginproxy.gov.bc.ca/auth/realms/eao-epic/protocol/openid-connect/certs

All Admin Users Redirected to "Email for access" Page

Symptom: Every staff member (regardless of their actual roles) is shown the "You need to provide your email address for access" page immediately after login. The browser URL shows /not-authorized.

Root cause: CSS (BC Government Common Sign-In Service) removed roles from the Keycloak realm's default client scopes. As a result, JWTs are issued without realm_access.roles, so isAuthorized() in keycloak.service.ts always evaluates to false and every user is redirected.

Diagnosis:

# Decode the JWT a staff user receives and look for realm_access
# (Use jwt.io or browser DevTools → Application → Local Storage → keycloak token)
# Missing: "realm_access": {"roles": [...]} means roles scope was removed

Fix applied in eagle-admin v2.4.3: Explicitly request the roles scope in the Keycloak init call so it is always included regardless of CSS default scope configuration:

// eagle-admin/src/app/services/keycloak.service.ts
this.keycloakAuth.init({
  onLoad: 'login-required',
  pkceMethod: 'S256',
  checkLoginIframe: false,
  scope: 'openid roles'   // ← explicit scope prevents CSS scope changes from locking out all users
});

If this symptom recurs after a CSS platform change, verify:

  1. The scope: 'openid roles' line is present in keycloakAuth.init()
  2. The eagle-admin-console Keycloak client still has the roles scope available (not deleted from the realm)
  3. Contact CSS team if the roles scope has been removed at the realm level

Redirect URI Mismatch

Cause: Keycloak client doesn't have correct redirect URI configured

Fix: Contact CSS team to add redirect URI to client configuration:

  • Dev: https://eagle-dev.apps.silver.devops.gov.bc.ca/admin/*
  • Prod: https://projects.eao.gov.bc.ca/admin/*

Related Pages