diff --git a/docker/cameleer-logto/logto-entrypoint.sh b/docker/cameleer-logto/logto-entrypoint.sh index d900d1b..bf02b58 100644 --- a/docker/cameleer-logto/logto-entrypoint.sh +++ b/docker/cameleer-logto/logto-entrypoint.sh @@ -32,10 +32,12 @@ for i in $(seq 1 120); do sleep 1 done -# Run bootstrap if not already done — use localhost since we're inside the container +# Run bootstrap — use localhost endpoints, skip Host headers (BOOTSTRAP_LOCAL flag) +# PUBLIC_HOST and PUBLIC_PROTOCOL stay real for redirect URI generation BOOTSTRAP_FILE="/data/logto-bootstrap.json" export LOGTO_ENDPOINT="http://localhost:3001" export LOGTO_ADMIN_ENDPOINT="http://localhost:3002" +export BOOTSTRAP_LOCAL="true" if [ -f "$BOOTSTRAP_FILE" ]; then CACHED_SECRET=$(jq -r '.m2mClientSecret // empty' "$BOOTSTRAP_FILE" 2>/dev/null) diff --git a/docker/logto-bootstrap.sh b/docker/logto-bootstrap.sh index 97891db..8760e51 100644 --- a/docker/logto-bootstrap.sh +++ b/docker/logto-bootstrap.sh @@ -47,6 +47,16 @@ TRAD_POST_LOGOUT_URIS="[\"${PROTO}://${HOST}\",\"${PROTO}://${HOST}/server\",\"$ log() { echo "[bootstrap] $1"; } pgpass() { PGPASSWORD="${PG_PASSWORD:-cameleer_dev}"; export PGPASSWORD; } +# When BOOTSTRAP_LOCAL=true (running inside Logto container with localhost endpoints), +# skip Host/X-Forwarded-Proto headers — they cause issuer mismatches with localhost +if [ "$BOOTSTRAP_LOCAL" = "true" ]; then + HOST_ARGS="" + ADMIN_HOST_ARGS="" +else + HOST_ARGS="-H Host:${HOST}" + ADMIN_HOST_ARGS="-H Host:${HOST}:3002 -H X-Forwarded-Proto:https" +fi + # Install jq + curl if not already available (deps are baked into cameleer-logto image) if ! command -v jq >/dev/null 2>&1 || ! command -v curl >/dev/null 2>&1; then if command -v apk >/dev/null 2>&1; then @@ -97,15 +107,14 @@ M_DEFAULT_SECRET=$(psql -h "$PG_HOST" -U "$PG_USER" -d "$PG_DB_LOGTO" -t -A -c \ get_admin_token() { curl -s -X POST "${LOGTO_ADMIN_ENDPOINT}/oidc/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ - -H "Host: ${HOST}:3002" \ - -H "X-Forwarded-Proto: https" \ + $ADMIN_HOST_ARGS \ -d "grant_type=client_credentials&client_id=${1}&client_secret=${2}&resource=${MGMT_API_RESOURCE}&scope=all" } get_default_token() { curl -s -X POST "${LOGTO_ENDPOINT}/oidc/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ - -H "Host: ${HOST}" \ + $HOST_ARGS \ -d "grant_type=client_credentials&client_id=${1}&client_secret=${2}&resource=${MGMT_API_RESOURCE}&scope=all" } @@ -118,7 +127,7 @@ log "Got Management API token." # Verify Management API is fully ready (Logto may still be initializing internally) log "Verifying Management API is responsive..." for i in $(seq 1 30); do - VERIFY_RESPONSE=$(curl -s -H "Authorization: Bearer $TOKEN" -H "Host: ${HOST}" "${LOGTO_ENDPOINT}/api/roles" 2>/dev/null) + VERIFY_RESPONSE=$(curl -s -H "Authorization: Bearer $TOKEN" $HOST_ARGS "${LOGTO_ENDPOINT}/api/roles" 2>/dev/null) if echo "$VERIFY_RESPONSE" | jq -e 'type == "array"' >/dev/null 2>&1; then log "Management API is ready." break @@ -129,21 +138,21 @@ done # --- Helper: Logto API calls --- api_get() { - curl -s -H "Authorization: Bearer $TOKEN" -H "Host: ${HOST}" "${LOGTO_ENDPOINT}${1}" 2>/dev/null || echo "[]" + curl -s -H "Authorization: Bearer $TOKEN" $HOST_ARGS "${LOGTO_ENDPOINT}${1}" 2>/dev/null || echo "[]" } api_post() { - curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -H "Host: ${HOST}" \ + curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" $HOST_ARGS \ -d "$2" "${LOGTO_ENDPOINT}${1}" 2>/dev/null || true } api_put() { - curl -s -X PUT -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -H "Host: ${HOST}" \ + curl -s -X PUT -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" $HOST_ARGS \ -d "$2" "${LOGTO_ENDPOINT}${1}" 2>/dev/null || true } api_delete() { - curl -s -X DELETE -H "Authorization: Bearer $TOKEN" -H "Host: ${HOST}" "${LOGTO_ENDPOINT}${1}" 2>/dev/null || true + curl -s -X DELETE -H "Authorization: Bearer $TOKEN" $HOST_ARGS "${LOGTO_ENDPOINT}${1}" 2>/dev/null || true } api_patch() { - curl -s -X PATCH -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -H "Host: ${HOST}" \ + curl -s -X PATCH -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" $HOST_ARGS \ -d "$2" "${LOGTO_ENDPOINT}${1}" 2>/dev/null || true } @@ -416,8 +425,7 @@ if [ -z "$M_ADMIN_SECRET" ]; then else ADMIN_TOKEN_RESPONSE=$(curl -s -X POST "${LOGTO_ADMIN_ENDPOINT}/oidc/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ - -H "Host: ${HOST}:3002" \ - -H "X-Forwarded-Proto: https" \ + $ADMIN_HOST_ARGS \ -d "grant_type=client_credentials&client_id=m-admin&client_secret=${M_ADMIN_SECRET}&resource=${ADMIN_MGMT_RESOURCE}&scope=all") ADMIN_TOKEN=$(echo "$ADMIN_TOKEN_RESPONSE" | jq -r '.access_token' 2>/dev/null) @@ -429,14 +437,14 @@ else # Admin-tenant API helpers (port 3002, admin token) admin_api_get() { - curl -s -H "Authorization: Bearer $ADMIN_TOKEN" -H "Host: ${HOST}:3002" -H "X-Forwarded-Proto: https" "${LOGTO_ADMIN_ENDPOINT}${1}" 2>/dev/null || echo "[]" + curl -s -H "Authorization: Bearer $ADMIN_TOKEN" $ADMIN_HOST_ARGS "${LOGTO_ADMIN_ENDPOINT}${1}" 2>/dev/null || echo "[]" } admin_api_post() { - curl -s -X POST -H "Authorization: Bearer $ADMIN_TOKEN" -H "Content-Type: application/json" -H "Host: ${HOST}:3002" -H "X-Forwarded-Proto: https" \ + curl -s -X POST -H "Authorization: Bearer $ADMIN_TOKEN" -H "Content-Type: application/json" $ADMIN_HOST_ARGS \ -d "$2" "${LOGTO_ADMIN_ENDPOINT}${1}" 2>/dev/null || true } admin_api_patch() { - curl -s -X PATCH -H "Authorization: Bearer $ADMIN_TOKEN" -H "Content-Type: application/json" -H "Host: ${HOST}:3002" -H "X-Forwarded-Proto: https" \ + curl -s -X PATCH -H "Authorization: Bearer $ADMIN_TOKEN" -H "Content-Type: application/json" $ADMIN_HOST_ARGS \ -d "$2" "${LOGTO_ADMIN_ENDPOINT}${1}" 2>/dev/null || true }