authelia: make OIDC client registration self-healing on a stale client_id
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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YEQNc4NfBST1m9NtCZVYa8
This commit is contained in:
+40
-4
@@ -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: '<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..."
|
||||
|
||||
Reference in New Issue
Block a user