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

@@ -29,6 +29,7 @@ DEFAULT_DOCKER_SOCKET="/var/run/docker.sock"
# --- Config values (set by args/env/config/prompts) ---
# Save environment values before initialization (CLI args override these)
_ENV_PUBLIC_HOST="${PUBLIC_HOST:-}"
_ENV_AUTH_HOST="${AUTH_HOST:-}"
_ENV_PUBLIC_PROTOCOL="${PUBLIC_PROTOCOL:-}"
_ENV_POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-}"
_ENV_CLICKHOUSE_PASSWORD="${CLICKHOUSE_PASSWORD:-}"
@@ -48,6 +49,7 @@ _ENV_DEPLOYMENT_MODE="${DEPLOYMENT_MODE:-}"
INSTALL_DIR=""
PUBLIC_HOST=""
AUTH_HOST=""
PUBLIC_PROTOCOL=""
ADMIN_USER=""
ADMIN_PASS=""
@@ -148,6 +150,7 @@ parse_args() {
--config) CONFIG_FILE_PATH="$2"; shift ;;
--install-dir) INSTALL_DIR="$2"; shift ;;
--public-host) PUBLIC_HOST="$2"; shift ;;
--auth-host) AUTH_HOST="$2"; shift ;;
--public-protocol) PUBLIC_PROTOCOL="$2"; shift ;;
--admin-user) ADMIN_USER="$2"; shift ;;
--admin-password) ADMIN_PASS="$2"; shift ;;
@@ -194,6 +197,7 @@ show_help() {
echo "Options:"
echo " --install-dir DIR Install directory (default: ./cameleer)"
echo " --public-host HOST Public hostname (default: auto-detect)"
echo " --auth-host HOST Auth domain for Logto (default: same as public-host)"
echo " --admin-user USER Admin username (default: admin)"
echo " --admin-password PASS Admin password (default: generated)"
echo " --tls-mode MODE self-signed or custom (default: self-signed)"
@@ -231,6 +235,7 @@ load_config_file() {
case "$key" in
install_dir) [ -z "$INSTALL_DIR" ] && INSTALL_DIR="$value" ;;
public_host) [ -z "$PUBLIC_HOST" ] && PUBLIC_HOST="$value" ;;
auth_host) [ -z "$AUTH_HOST" ] && AUTH_HOST="$value" ;;
public_protocol) [ -z "$PUBLIC_PROTOCOL" ] && PUBLIC_PROTOCOL="$value" ;;
admin_user) [ -z "$ADMIN_USER" ] && ADMIN_USER="$value" ;;
admin_password) [ -z "$ADMIN_PASS" ] && ADMIN_PASS="$value" ;;
@@ -257,6 +262,7 @@ load_config_file() {
load_env_overrides() {
[ -z "$INSTALL_DIR" ] && INSTALL_DIR="${CAMELEER_INSTALL_DIR:-}"
[ -z "$PUBLIC_HOST" ] && PUBLIC_HOST="$_ENV_PUBLIC_HOST"
[ -z "$AUTH_HOST" ] && AUTH_HOST="$_ENV_AUTH_HOST"
[ -z "$PUBLIC_PROTOCOL" ] && PUBLIC_PROTOCOL="$_ENV_PUBLIC_PROTOCOL"
[ -z "$ADMIN_USER" ] && ADMIN_USER="${SAAS_ADMIN_USER:-}"
[ -z "$ADMIN_PASS" ] && ADMIN_PASS="${SAAS_ADMIN_PASS:-}"
@@ -463,6 +469,7 @@ run_expert_prompts() {
if [ "$DEPLOYMENT_MODE" = "saas" ]; then
echo ""
echo -e "${BOLD} Logto:${NC}"
prompt AUTH_HOST "Auth domain (Logto) — same as hostname for single-domain" "${AUTH_HOST:-$PUBLIC_HOST}"
if prompt_yesno "Expose Logto admin console externally?" "y"; then
LOGTO_CONSOLE_EXPOSED="true"
else
@@ -493,8 +500,12 @@ merge_config() {
: "${COMPOSE_PROJECT:=$DEFAULT_COMPOSE_PROJECT}"
fi
# Force lowercase hostname — Logto normalizes internally, case mismatch breaks JWT validation
# Default AUTH_HOST to PUBLIC_HOST (single-domain setup)
: "${AUTH_HOST:=$PUBLIC_HOST}"
# Force lowercase hostnames — Logto normalizes internally, case mismatch breaks JWT validation
PUBLIC_HOST=$(echo "$PUBLIC_HOST" | tr '[:upper:]' '[:lower:]')
AUTH_HOST=$(echo "$AUTH_HOST" | tr '[:upper:]' '[:lower:]')
if [ "$DEPLOYMENT_MODE" != "standalone" ]; then
if [ -z "$NODE_TLS_REJECT" ]; then
@@ -636,6 +647,7 @@ VERSION=${VERSION}
# Public access
PUBLIC_HOST=${PUBLIC_HOST}
AUTH_HOST=${AUTH_HOST}
PUBLIC_PROTOCOL=${PUBLIC_PROTOCOL}
# Ports
@@ -841,6 +853,7 @@ write_config_file() {
install_dir=${INSTALL_DIR}
public_host=${PUBLIC_HOST}
auth_host=${AUTH_HOST}
public_protocol=${PUBLIC_PROTOCOL}
admin_user=${ADMIN_USER}
tls_mode=${TLS_MODE}
@@ -902,7 +915,7 @@ ClickHouse: default / ${CLICKHOUSE_PASSWORD}
EOF
if [ "$LOGTO_CONSOLE_EXPOSED" = "true" ]; then
echo "Logto Console: ${PUBLIC_PROTOCOL}://${PUBLIC_HOST}:${LOGTO_CONSOLE_PORT}" >> "$f"
echo "Logto Console: ${PUBLIC_PROTOCOL}://${AUTH_HOST}:${LOGTO_CONSOLE_PORT}" >> "$f"
else
echo "Logto Console: (not exposed)" >> "$f"
fi
@@ -941,7 +954,7 @@ generate_install_doc() {
EOF
if [ "$LOGTO_CONSOLE_EXPOSED" = "true" ]; then
echo "- **Logto Admin Console:** ${PUBLIC_PROTOCOL}://${PUBLIC_HOST}:${LOGTO_CONSOLE_PORT}" >> "$f"
echo "- **Logto Admin Console:** ${PUBLIC_PROTOCOL}://${AUTH_HOST}:${LOGTO_CONSOLE_PORT}" >> "$f"
fi
cat >> "$f" << 'EOF'
@@ -1219,7 +1232,7 @@ print_credentials() {
if [ "$DEPLOYMENT_MODE" = "saas" ]; then
if [ "$LOGTO_CONSOLE_EXPOSED" = "true" ]; then
echo -e " Logto Console: ${BLUE}${PUBLIC_PROTOCOL}://${PUBLIC_HOST}:${LOGTO_CONSOLE_PORT}${NC}"
echo -e " Logto Console: ${BLUE}${PUBLIC_PROTOCOL}://${AUTH_HOST}:${LOGTO_CONSOLE_PORT}${NC}"
echo ""
fi
fi