Fix Authentik WIF client credentials
Some checks failed
Cache: Publish Nix / Publish Nix Cache (push) Successful in 2m46s
Build Rust / Cargo Test (push) Successful in 4m1s
Build Site / Next.js Build (push) Failing after 4s
Lint Governance / BEP Metadata (push) Successful in 1s

This commit is contained in:
Conrad Kramer 2026-06-07 13:07:34 -07:00
parent 8180c3c35a
commit e7c0442a12
6 changed files with 107 additions and 35 deletions

View file

@ -11,7 +11,7 @@ client_id="${AUTHENTIK_GOOGLE_WIF_CLIENT_ID:-google-wif.burrow.net}"
client_secret="${AUTHENTIK_GOOGLE_WIF_CLIENT_SECRET:-}"
launch_url="${AUTHENTIK_GOOGLE_WIF_LAUNCH_URL:-https://console.cloud.google.com/}"
redirect_uris_json="${AUTHENTIK_GOOGLE_WIF_REDIRECT_URIS_JSON:-[]}"
access_group="${AUTHENTIK_GOOGLE_WIF_ACCESS_GROUP:-burrow-automation}"
access_group="${AUTHENTIK_GOOGLE_WIF_ACCESS_GROUP:-}"
usage() {
cat <<'EOF'
@ -33,7 +33,11 @@ Optional environment:
AUTHENTIK_GOOGLE_WIF_CLIENT_ID
AUTHENTIK_GOOGLE_WIF_LAUNCH_URL
AUTHENTIK_GOOGLE_WIF_REDIRECT_URIS_JSON
AUTHENTIK_GOOGLE_WIF_ACCESS_GROUP
AUTHENTIK_GOOGLE_WIF_ACCESS_GROUP (optional; leave empty for client credentials)
AUTHENTIK_GOOGLE_WIF_CLIENT_SECRET is the Authentik client-credentials secret
used by Forgejo runners. For service-account authentication, store
base64("<service-account-username>:<app-password-token>").
EOF
}
@ -214,9 +218,18 @@ existing_application="$(
| head -n1
)"
if [[ -z "$existing_application" ]]; then
direct_application_result="$(api_with_status GET "/api/v3/core/applications/${application_slug}/")"
direct_application_status="$(printf '%s\n' "$direct_application_result" | sed -n '1p')"
direct_application_body="$(printf '%s\n' "$direct_application_result" | sed '1d')"
if [[ "$direct_application_status" == "200" ]]; then
existing_application="$direct_application_body"
fi
fi
if [[ -n "$existing_application" ]]; then
application_pk="$(printf '%s\n' "$existing_application" | jq -r '.pk')"
api PATCH "/api/v3/core/applications/${application_pk}/" "$application_payload" >/dev/null
api PATCH "/api/v3/core/applications/${application_slug}/" "$application_payload" >/dev/null
else
create_application_result="$(
api_with_status POST "/api/v3/core/applications/" "$application_payload"
@ -235,6 +248,14 @@ else
| jq -r --arg slug "$application_slug" '.results[]? | select(.slug == $slug) | .pk // empty' \
| head -n1
)"
if [[ -z "$application_pk" ]]; then
direct_application_result="$(api_with_status GET "/api/v3/core/applications/${application_slug}/")"
direct_application_status="$(printf '%s\n' "$direct_application_result" | sed -n '1p')"
direct_application_body="$(printf '%s\n' "$direct_application_result" | sed '1d')"
if [[ "$direct_application_status" == "200" ]]; then
application_pk="$(printf '%s\n' "$direct_application_body" | jq -r '.pk // empty')"
fi
fi
else
printf '%s\n' "$create_application_body" >&2
echo "error: could not reconcile Authentik application ${application_slug}" >&2
@ -247,6 +268,32 @@ if [[ -z "${application_pk:-}" ]]; then
exit 1
fi
delete_binding() {
local binding_pk="$1"
api DELETE "/api/v3/policies/bindings/${binding_pk}/" >/dev/null || true
}
delete_application_group_bindings_except() {
local keep_group_pk="${1:-}"
local binding_pks
binding_pks="$(
api GET "/api/v3/policies/bindings/?page_size=500" \
| jq -r \
--arg target "$application_pk" \
--arg keep_group "$keep_group_pk" \
'.results[]?
| select((.target | tostring) == $target and .group != null)
| select(($keep_group == "") or ((.group | tostring) != $keep_group))
| .pk'
)"
while IFS= read -r binding_pk; do
[[ -n "$binding_pk" ]] || continue
delete_binding "$binding_pk"
done <<< "$binding_pks"
}
if [[ -n "$access_group" ]]; then
group_pk="$(
api GET "/api/v3/core/groups/?page_size=200" \
@ -256,21 +303,36 @@ if [[ -n "$access_group" ]]; then
if [[ -z "$group_pk" ]]; then
echo "warning: Authentik Google WIF access group ${access_group} was not found; application policy left unchanged." >&2
else
api POST "/api/v3/policies/bindings/" "$(
jq -n \
--arg target "$application_pk" \
--arg group "$group_pk" \
'{
target: $target,
group: $group,
negate: false,
order: 0,
enabled: true,
timeout: 30,
failure_result: false
}'
)" >/dev/null || true
delete_application_group_bindings_except "$group_pk"
existing_binding="$(
api GET "/api/v3/policies/bindings/?page_size=500" \
| jq -r \
--arg target "$application_pk" \
--arg group "$group_pk" \
'.results[]?
| select((.target | tostring) == $target and (.group | tostring) == $group)
| .pk' \
| head -n1
)"
if [[ -z "$existing_binding" ]]; then
api POST "/api/v3/policies/bindings/" "$(
jq -n \
--arg target "$application_pk" \
--arg group "$group_pk" \
'{
target: $target,
group: $group,
negate: false,
order: 0,
enabled: true,
timeout: 30,
failure_result: false
}'
)" >/dev/null || true
fi
fi
else
delete_application_group_bindings_except ""
fi
for _ in $(seq 1 30); do

View file

@ -115,12 +115,13 @@ else
fi
token_endpoint="$(resolve_token_endpoint)"
curl -fsS \
-u "${authentik_client_id}:${authentik_client_secret}" \
-d grant_type=client_credentials \
-d client_id="$authentik_client_id" \
-d client_secret="$authentik_client_secret" \
-d scope="openid profile email groups" \
-d audience="$authentik_audience" \
"$token_endpoint" \
| jq -r '.id_token // empty' > "$token_path"
| jq -r '.id_token // .access_token // empty' > "$token_path"
fi
chmod 0600 "$token_path"