feat: split auth domain — Logto gets dedicated AUTH_HOST
All checks were successful
CI / build (push) Successful in 1m22s
CI / docker (push) Successful in 48s

Support separate auth domain (e.g. auth.cameleer.io) for Logto while
keeping the SaaS app on PUBLIC_HOST (e.g. app.cameleer.io). AUTH_HOST
defaults to PUBLIC_HOST for backward-compatible single-domain setups.

- Logto routing: Host(AUTH_HOST) replaces PathPrefix('/') catch-all
- Root redirect moved from traefik-dynamic.yml to Docker labels with
  Host(PUBLIC_HOST) scope so it doesn't intercept auth domain
- Self-signed cert generates SANs for both domains
- Bootstrap Host header uses AUTH_HOST for Logto endpoint validation
- Spring issuer-uri and oidcissueruri use new authhost property
- Both installers (sh + ps1) prompt for AUTH_HOST in expert mode

Local dev: AUTH_HOST=auth.localhost (resolves to 127.0.0.1, no hosts file)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-24 18:11:47 +02:00
parent 1fbafbb16d
commit dc7ac3a1ec
11 changed files with 93 additions and 44 deletions

View File

@@ -17,6 +17,7 @@ param(
[string]$Config,
[string]$InstallDir,
[string]$PublicHost,
[string]$AuthHost,
[string]$PublicProtocol,
[string]$AdminUser,
[string]$AdminPassword,
@@ -66,6 +67,7 @@ $DEFAULT_DOCKER_SOCKET = '/var/run/docker.sock'
# --- Capture env vars before any overrides ---
$_ENV_PUBLIC_HOST = $env:PUBLIC_HOST
$_ENV_AUTH_HOST = $env:AUTH_HOST
$_ENV_PUBLIC_PROTOCOL = $env:PUBLIC_PROTOCOL
$_ENV_POSTGRES_PASSWORD = $env:POSTGRES_PASSWORD
$_ENV_CLICKHOUSE_PASSWORD = $env:CLICKHOUSE_PASSWORD
@@ -88,6 +90,7 @@ $_ENV_DEPLOYMENT_MODE = $env:DEPLOYMENT_MODE
$script:cfg = @{
InstallDir = $InstallDir
PublicHost = $PublicHost
AuthHost = $AuthHost
PublicProtocol = $PublicProtocol
AdminUser = $AdminUser
AdminPass = $AdminPassword
@@ -150,6 +153,7 @@ function Show-Help {
Write-Host 'Options:'
Write-Host ' -InstallDir DIR Install directory (default: ./cameleer)'
Write-Host ' -PublicHost HOST Public hostname (default: auto-detect)'
Write-Host ' -AuthHost HOST Auth domain for Logto (default: same as PublicHost)'
Write-Host ' -AdminUser USER Admin username (default: admin)'
Write-Host ' -AdminPassword PASS Admin password (default: generated)'
Write-Host ' -TlsMode MODE self-signed or custom (default: self-signed)'
@@ -236,6 +240,7 @@ function Load-ConfigFile {
switch ($key) {
'install_dir' { if (-not $script:cfg.InstallDir) { $script:cfg.InstallDir = $val } }
'public_host' { if (-not $script:cfg.PublicHost) { $script:cfg.PublicHost = $val } }
'auth_host' { if (-not $script:cfg.AuthHost) { $script:cfg.AuthHost = $val } }
'public_protocol' { if (-not $script:cfg.PublicProtocol) { $script:cfg.PublicProtocol = $val } }
'admin_user' { if (-not $script:cfg.AdminUser) { $script:cfg.AdminUser = $val } }
'admin_password' { if (-not $script:cfg.AdminPass) { $script:cfg.AdminPass = $val } }
@@ -264,6 +269,7 @@ function Load-EnvOverrides {
$c = $script:cfg
if (-not $c.InstallDir) { $c.InstallDir = $env:CAMELEER_INSTALL_DIR }
if (-not $c.PublicHost) { $c.PublicHost = $_ENV_PUBLIC_HOST }
if (-not $c.AuthHost) { $c.AuthHost = $_ENV_AUTH_HOST }
if (-not $c.PublicProtocol) { $c.PublicProtocol = $_ENV_PUBLIC_PROTOCOL }
if (-not $c.AdminUser) { $c.AdminUser = $env:SAAS_ADMIN_USER }
if (-not $c.AdminPass) { $c.AdminPass = $env:SAAS_ADMIN_PASS }
@@ -474,6 +480,7 @@ function Run-ExpertPrompts {
if ($c.DeploymentMode -eq 'saas') {
Write-Host ''
Write-Host ' Logto:' -ForegroundColor Cyan
$c.AuthHost = Prompt-Value 'Auth domain (Logto) -- same as hostname for single-domain' (Coalesce $c.AuthHost $c.PublicHost)
if (Prompt-YesNo 'Expose Logto admin console externally?' 'y') {
$c.LogtoConsoleExposed = 'true'
} else {
@@ -507,8 +514,12 @@ function Merge-Config {
}
}
# Default AUTH_HOST to PUBLIC_HOST (single-domain setup)
if (-not $c.AuthHost) { $c.AuthHost = $c.PublicHost }
# Force lowercase -- Logto normalises internally; case mismatch breaks JWT validation
$c.PublicHost = $c.PublicHost.ToLower()
$c.AuthHost = $c.AuthHost.ToLower()
if ($c.DeploymentMode -ne 'standalone' -and (-not $c.NodeTlsReject)) {
if ($c.TlsMode -eq 'custom') { $c.NodeTlsReject = '1' } else { $c.NodeTlsReject = '0' }
@@ -636,6 +647,7 @@ POSTGRES_IMAGE=postgres:16-alpine
VERSION=$($c.Version)
PUBLIC_HOST=$($c.PublicHost)
AUTH_HOST=$($c.AuthHost)
PUBLIC_PROTOCOL=$($c.PublicProtocol)
HTTP_PORT=$($c.HttpPort)
@@ -889,6 +901,7 @@ function Write-ConfigFile {
install_dir=$($c.InstallDir)
public_host=$($c.PublicHost)
auth_host=$($c.AuthHost)
public_protocol=$($c.PublicProtocol)
admin_user=$($c.AdminUser)
tls_mode=$($c.TlsMode)
@@ -931,7 +944,7 @@ ClickHouse: default / $($c.ClickhousePassword)
"@
} else {
if ($c.LogtoConsoleExposed -eq 'true') {
$logtoLine = "Logto Console: $($c.PublicProtocol)://$($c.PublicHost):$($c.LogtoConsolePort)"
$logtoLine = "Logto Console: $($c.PublicProtocol)://$($c.AuthHost):$($c.LogtoConsolePort)"
} else {
$logtoLine = 'Logto Console: (not exposed)'
}
@@ -980,7 +993,7 @@ function Generate-InstallDoc {
if ($c.TlsMode -eq 'custom') { $tlsDesc = 'Custom certificate' } else { $tlsDesc = 'Self-signed (auto-generated)' }
if ($c.LogtoConsoleExposed -eq 'true') {
$logtoConsoleRow = "- **Logto Admin Console:** $($c.PublicProtocol)://$($c.PublicHost):$($c.LogtoConsolePort)"
$logtoConsoleRow = "- **Logto Admin Console:** $($c.PublicProtocol)://$($c.AuthHost):$($c.LogtoConsolePort)"
$logtoPortRow = "| $($c.LogtoConsolePort) | Logto Admin Console |"
} else {
$logtoConsoleRow = ''
@@ -1256,7 +1269,7 @@ function Print-Credentials {
Write-Host ''
if ($c.DeploymentMode -eq 'saas' -and $c.LogtoConsoleExposed -eq 'true') {
Write-Host ' Logto Console: ' -NoNewline
Write-Host "$($c.PublicProtocol)://$($c.PublicHost):$($c.LogtoConsolePort)" -ForegroundColor Blue
Write-Host "$($c.PublicProtocol)://$($c.AuthHost):$($c.LogtoConsolePort)" -ForegroundColor Blue
Write-Host ''
}
Write-Host " Credentials saved to: $($c.InstallDir)\credentials.txt"