From 2f5607df82dc1f401b2048a79df2250b58ec52e6 Mon Sep 17 00:00:00 2001 From: Jerod Hodgkin Date: Mon, 20 Jul 2026 06:15:41 +0000 Subject: [PATCH] Fix authentik-setup.sh: broken jget stdin handling, wrong stage path Two bugs found running this against the live Authentik instance for the first time: - jget's <<<"$1" here-string always overrode stdin, so `api ... | jget - "expr"` never actually read curl's piped output - it fed the literal string "-" to json.load() instead, and the abandoned pipe made curl fail with "Failed writing body". Fixed by branching on $1 == "-" to read the real stdin in that case. - The prompt stage lives at /stages/prompt/stages/, not /stages/prompt/ (that path is prompt *fields*). Wrong path 404'd. Also switched `python` -> `python3` throughout for portability. Verified idempotent end-to-end against auth.jerodrigged.com: provider, application, lisilou-admin group, and the full enrollment flow (prompt -> write -> login stages, bound and set as the brand's enrollment flow) all created successfully, second run reports everything as already existing. --- scripts/authentik-setup.sh | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/scripts/authentik-setup.sh b/scripts/authentik-setup.sh index 4090626..d6e4429 100644 --- a/scripts/authentik-setup.sh +++ b/scripts/authentik-setup.sh @@ -39,7 +39,13 @@ api() { # method path [json-body] fi } -jget() { python -c "import sys,json;d=json.load(sys.stdin);print(eval(sys.argv[1]))" "$2" <<<"$1"; } +jget() { # jget ('-' reads JSON from stdin, e.g. a pipe) + if [ "$1" = "-" ]; then + python3 -c "import sys,json;d=json.load(sys.stdin);print(eval(sys.argv[1]))" "$2" + else + python3 -c "import sys,json;d=json.load(sys.stdin);print(eval(sys.argv[1]))" "$2" <<<"$1" + fi +} echo "── Checking API access…" VERSION=$(api GET /admin/version/ | jget - "d['version_current']" 2>/dev/null || true) @@ -54,7 +60,7 @@ SCOPES=$(api GET "/propertymappings/provider/scope/?managed__iexact=goauthentik. for s in profile email; do SCOPES="$SCOPES,$(api GET "/propertymappings/provider/scope/?managed__iexact=goauthentik.io/providers/oauth2/scope-$s" | jget - "d['results'][0]['pk']")" done -SCOPES_JSON=$(python -c "import sys;print(__import__('json').dumps(sys.argv[1].split(',')))" "$SCOPES") +SCOPES_JSON=$(python3 -c "import sys;print(__import__('json').dumps(sys.argv[1].split(',')))" "$SCOPES") # ── 2. OAuth2 provider ──────────────────────────────────────────────────────── echo "── Provider…" @@ -63,7 +69,7 @@ if [ "$EXISTING" -gt 0 ]; then PROVIDER_PK=$(api GET "/providers/oauth2/?name=$CLIENT_ID" | jget - "d['results'][0]['pk']") echo " exists (pk=$PROVIDER_PK)" else - BODY=$(python - "$AUTHZ_FLOW" "$INVALIDATION_FLOW" "$SCOPES_JSON" <<'PY' + BODY=$(python3 - "$AUTHZ_FLOW" "$INVALIDATION_FLOW" "$SCOPES_JSON" <<'PY' import json, sys authz, inval, scopes = sys.argv[1], sys.argv[2], json.loads(sys.argv[3]) p = { @@ -85,7 +91,7 @@ PY ) RESP=$(api POST /providers/oauth2/ "$BODY" 2>&1) || { # Older Authentik (<2024.2) wants redirect_uris as a newline-joined string - BODY=$(python -c " + BODY=$(python3 -c " import json,sys p=json.loads(sys.argv[1]); p['redirect_uris']='\n'.join(u['url'] for u in p['redirect_uris']); print(json.dumps(p))" "$BODY") RESP=$(api POST /providers/oauth2/ "$BODY") @@ -155,7 +161,7 @@ else make_field password "Password" password 3 "" make_field password_repeat "Confirm password" password 4 "" - PROMPT_STAGE=$(api POST /stages/prompt/ "{ + PROMPT_STAGE=$(api POST /stages/prompt/stages/ "{ \"name\": \"lisilou-enrollment-prompt\", \"fields\": [\"${FIELD_PKS[username]}\",\"${FIELD_PKS[name]}\",\"${FIELD_PKS[email]}\",\"${FIELD_PKS[password]}\",\"${FIELD_PKS[password_repeat]}\"] }" | jget - "d['pk']") @@ -191,7 +197,7 @@ OIDC_CLIENT_ID=$CLIENT_ID OIDC_CLIENT_SECRET=$CLIENT_SECRET OIDC_REDIRECT_URI=$PROD_REDIRECT OIDC_ADMIN_GROUP=lisilou-admin -SESSION_SECRET=$(openssl rand -hex 32 2>/dev/null || python -c "import secrets;print(secrets.token_hex(32))") +SESSION_SECRET=$(openssl rand -hex 32 2>/dev/null || python3 -c "import secrets;print(secrets.token_hex(32))") Then: docker compose restart api EOF