Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions apps/opencase/src/infrastructure/config/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ export interface AppConfig {
smtpHost?: string;
smtpPort?: string;
smtpFrom?: string;

// Keycloak realm SSL enforcement ('none' for HTTP dev, 'external' for production HTTPS)
keycloakRealmSslRequired: 'none' | 'external' | 'all';
}

export function loadConfig(): AppConfig {
Expand Down Expand Up @@ -78,6 +81,8 @@ export function loadConfig(): AppConfig {
smtpHost: process.env.SMTP_HOST ?? (isProduction ? undefined : 'mailpit'),
smtpPort: process.env.SMTP_PORT ?? '1025',
smtpFrom: process.env.SMTP_FROM ?? 'noreply@opencase.local',

keycloakRealmSslRequired: (process.env.KEYCLOAK_SSL_REQUIRED ?? (isProduction ? 'external' : 'none')) as 'none' | 'external' | 'all',
};
}

Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ export class KeycloakAdminClient {
logger.info({ realm }, 'Configured realm settings (resetPassword, loginWithEmail, SMTP, CASE scopes)')
}

async setRealmSslRequired (realm: string, sslRequired: string): Promise<void> {
await this.requestJson('PUT', `/admin/realms/${encodeURIComponent(realm)}`, { sslRequired })
logger.info({ realm, sslRequired }, 'Set realm sslRequired')
}

async ensureClient (client: {
clientId: string
publicClient: boolean
Expand Down
2 changes: 2 additions & 0 deletions apps/opencase/src/wiring/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,8 @@ export async function buildContainer(): Promise<Container> {
try {
await keycloakAdmin.ensureRealmExists()
await keycloakTenantProvisioner.bootstrapSystemAdmin()
await keycloakAdmin.setRealmSslRequired(config.keycloakRealm, config.keycloakRealmSslRequired)
await keycloakAdmin.setRealmSslRequired(config.keycloakAdminRealm, config.keycloakRealmSslRequired)
keycloakReady = true
logger.info('Keycloak bootstrap completed successfully')
} catch (error: any) {
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ services:
# Hostname configuration - tell Keycloak its external URL
- KC_HOSTNAME=${OPENCASE_HOSTNAME:-localhost}
- KC_HOSTNAME_STRICT=false
- KC_HOSTNAME_STRICT_HTTPS=${KC_HOSTNAME_STRICT_HTTPS:-false}
# Proxy settings for running behind Traefik
- KC_PROXY_HEADERS=xforwarded
- KC_HTTP_RELATIVE_PATH=/
Expand Down Expand Up @@ -124,6 +123,7 @@ services:
- SMTP_HOST=${SMTP_HOST:-mailpit}
- SMTP_PORT=${SMTP_PORT:-1025}
- SMTP_FROM=noreply@${OPENCASE_HOSTNAME:-opencase.local}
- KEYCLOAK_SSL_REQUIRED=${KEYCLOAK_SSL_REQUIRED:-none}
restart: on-failure
volumes:
- ./apps/opencase/data:/app/data
Expand Down
4 changes: 2 additions & 2 deletions docs/env.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@
TRAEFIK_CONFIG=traefik/traefik-https.yml
TRAEFIK_PORTS_WEB=443:443
TRAEFIK_PORTS_ALT=80:80
KC_HOSTNAME_STRICT_HTTPS=true
KEYCLOAK_SSL_REQUIRED=external
OPENCASE_SCHEME=https
OPENCASE_PORT_SUFFIX=

# --- HTTP (local development) — plain HTTP on :3000, dashboard on :8080 ---
# TRAEFIK_CONFIG=traefik/traefik-http.yml
# TRAEFIK_PORTS_WEB=3000:3000
# TRAEFIK_PORTS_ALT=8080:8080
# KC_HOSTNAME_STRICT_HTTPS=false
# KEYCLOAK_SSL_REQUIRED=none
# OPENCASE_SCHEME=http
# OPENCASE_PORT_SUFFIX=:3000

Expand Down