From 699be5b4e3cb5d9c6f54e0cdf0673e47d01eb9dd Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 19 Aug 2026 19:11:34 +0000 Subject: [PATCH] authelia: make OIDC client registration self-healing on a stale client_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Confirmed live: ActualBudget's new automated "Sign in with Authelia" offer hit a client_id ("actualbudget") already registered from an earlier use of the interactive "Register an app" menu — that older flow only registers the client in Authelia and prints instructions to paste the secret into the app's own settings manually; if that paste step never happened, ActualBudget's .env never got the OIDC vars, but Authelia still considered the client_id taken. _authelia_provision_oidc_client's duplicate check just warned and returned 1, permanently blocking the automated offer with no path forward — the stale registration's secret was shown once and already gone, so there was nothing to recover, only reasons to replace it. Added _authelia_remove_oidc_client() (tested against a synthetic multi-client config, both mid-list and last-in-list removal) and changed the duplicate-client_id check to remove-and-replace instead of failing. Every automated caller (gitea/mealie/actualbudget's SSO offers) uses a fixed, service-specific client_id, so a collision there means "this same service was already registered," not a different app's ID being clobbered. The interactive menu's own earlier duplicate check (a distinct code path, one step before this one) is untouched — it still warns and stops before prompting further, since a human-typed ID colliding with an unrelated app is a different, more ambiguous situation. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01YEQNc4NfBST1m9NtCZVYa8 --- services/authelia.sh | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/services/authelia.sh b/services/authelia.sh index babcd1c..1bd8542 100644 --- a/services/authelia.sh +++ b/services/authelia.sh @@ -1441,6 +1441,30 @@ _authelia_ensure_oidc_provider() { log_success "OIDC provider enabled (signing key + HMAC secret generated)" } +# Deletes one OIDC client block (matched by client_id) from +# identity_providers.oidc.clients in configuration.yml. Used by +# _authelia_provision_oidc_client below to make re-registering a client_id +# idempotent instead of a dead end — see that function's own comment on +# why a stale registration is safe to just replace. A client block starts +# at its own " - client_id: ''" line (6-space indent) and runs +# until either the next such line or a line indented less than 6 spaces +# (end of the clients list) — deleting stops exactly there so a sibling +# client's block, or whatever config section follows, is untouched. +_authelia_remove_oidc_client() { + local config_file="$1" client_id="$2" + awk -v target="'${client_id}'" ' + { + if ($0 ~ /^ - client_id: /) { + skip = ($0 ~ target) ? 1 : 0 + } else if (skip && $0 !~ /^ /) { + skip = 0 + } + if (!skip) print + } + ' "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file" + chown 1000:1000 "$config_file" 2>/dev/null || true +} + # Non-interactive core of _authelia_add_oidc_client() below — generates a # client secret, patches it into identity_providers.oidc.clients, and # (optionally) restarts Authelia. Fully self-contained (re-validates @@ -1458,8 +1482,10 @@ _authelia_ensure_oidc_provider() { # caller must capture and use/display it now. # OIDC_AUTHELIA_DOMAIN this Authelia instance's apex domain, for # building discovery/authorization/token URLs. -# Returns 1 on failure (Authelia not installed, client ID already taken, -# secret generation failed) with the reason already logged. +# Returns 1 on failure (Authelia not installed, domain undeterminable, +# secret generation failed) with the reason already logged. A client_id +# that's already registered is NOT a failure — it gets replaced (see the +# comment at that check below). _authelia_provision_oidc_client() { local APP_NAME="$1" CLIENT_ID="$2" AUTH_POLICY="$3" RESTART_AUTH="$4"; shift 4 local -a REDIRECT_URIS=("$@") @@ -1490,9 +1516,19 @@ _authelia_provision_oidc_client() { return 1 fi + # A stale registration (e.g. from the interactive "Register an app" menu + # run previously without ever finishing — its plaintext secret was shown + # once and is gone, so the registration is dead weight either way) would + # otherwise permanently block this exact service's automated SSO offer + # with nothing but a warning. Confirmed live: this is what happened to + # ActualBudget the first time its own offer ran, against a client_id the + # menu had already registered in an earlier session. Safe to just + # replace — every automated caller here uses a fixed, service-specific + # client_id, so a collision means "this same service, already + # registered" rather than someone else's app using the same ID. if grep -qF "client_id: '${CLIENT_ID}'" "$CONFIG_FILE" 2>/dev/null; then - log_warning "A client with ID '$CLIENT_ID' is already registered in $CONFIG_FILE." - return 1 + log_warning "A client with ID '$CLIENT_ID' is already registered — replacing it with a fresh one (its old secret was never recoverable anyway)." + _authelia_remove_oidc_client "$CONFIG_FILE" "$CLIENT_ID" fi log_info "Generating client secret..."