Fix Homebox OIDC invalid_scope error by allowing per-client extra scopes
_authelia_provision_oidc_client() hardcoded openid/profile/email as the
only scopes a registered client could ever request, but Homebox's own
Authelia integration needs 'groups' too — requesting it without it being
in the client's own scopes allowlist made Authelia reject every login
with invalid_scope, even though the server supports 'groups' generally.
Add an EXTRA_SCOPES positional arg (space-separated, right after
REQUIRE_PKCE) that only Homebox's caller populates ("groups"); every
other existing caller passes "" and gets a byte-for-byte unchanged
client registration.
This commit is contained in:
@@ -493,7 +493,7 @@ right (Portainer, ntfy), not general familiarity with the product:
|
|||||||
| Service | Native OIDC? | Notes |
|
| Service | Native OIDC? | Notes |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `mealie` | Yes — wired up | Pure env vars (`OIDC_AUTH_ENABLED`, `OIDC_CLIENT_ID/SECRET`, `OIDC_CONFIGURATION_URL`), see `_mealie_offer_authelia_oidc()`. Redirect URI is `<BASE_URL>/login`. Needs a `--forwarded-allow-ips` entrypoint override when Caddy-fronted, or the generated redirect URI comes out `http://` even when actually served over `https://` — see the function's own comment. |
|
| `mealie` | Yes — wired up | Pure env vars (`OIDC_AUTH_ENABLED`, `OIDC_CLIENT_ID/SECRET`, `OIDC_CONFIGURATION_URL`), see `_mealie_offer_authelia_oidc()`. Redirect URI is `<BASE_URL>/login`. Needs a `--forwarded-allow-ips` entrypoint override when Caddy-fronted, or the generated redirect URI comes out `http://` even when actually served over `https://` — see the function's own comment. |
|
||||||
| `homebox` | Yes — wired up | Pure env vars (`HBOX_OIDC_ENABLED`, `HBOX_OIDC_ISSUER_URL`, `HBOX_OIDC_CLIENT_ID/SECRET`, `HBOX_OIDC_SCOPE`), see `_homebox_offer_authelia_oidc()`. Confirmed against homebox.software's own OIDC docs and authelia.com's Homebox integration page — needs PKCE (unlike Mealie/ActualBudget). Redirect path is `/api/v1/users/login/oidc/callback`; issuer URL is reportedly sensitive to a trailing slash (a real upstream bug), so it's written from this repo's own portal-URL value as-is, never with one appended. The stock compose template didn't have `env_file: .env` (vars were listed individually in `environment:` instead) — added to the template, and patched onto any pre-existing install's compose file the first time this offer runs, or the written `.env` additions would silently never reach the container. `HBOX_OPTIONS_ALLOW_LOCAL_LOGIN=false`/`HBOX_OIDC_AUTO_REDIRECT=true` are real, documented env vars for fully replacing local login, offered as a separate step gated behind the same "have you tested the button first" confirmation as Mealie/Beszel. |
|
| `homebox` | Yes — wired up | Pure env vars (`HBOX_OIDC_ENABLED`, `HBOX_OIDC_ISSUER_URL`, `HBOX_OIDC_CLIENT_ID/SECRET`, `HBOX_OIDC_SCOPE`), see `_homebox_offer_authelia_oidc()`. Confirmed against homebox.software's own OIDC docs and authelia.com's Homebox integration page — needs PKCE (unlike Mealie/ActualBudget). Redirect path is `/api/v1/users/login/oidc/callback`; issuer URL is reportedly sensitive to a trailing slash (a real upstream bug), so it's written from this repo's own portal-URL value as-is, never with one appended. The stock compose template didn't have `env_file: .env` (vars were listed individually in `environment:` instead) — added to the template, and patched onto any pre-existing install's compose file the first time this offer runs, or the written `.env` additions would silently never reach the container. `HBOX_OPTIONS_ALLOW_LOCAL_LOGIN=false`/`HBOX_OIDC_AUTO_REDIRECT=true` are real, documented env vars for fully replacing local login, offered as a separate step gated behind the same "have you tested the button first" confirmation as Mealie/Beszel. Unlike every other native-OIDC integration in this table, `HBOX_OIDC_SCOPE` needs a fourth scope, `groups`, alongside the usual `openid profile email` — Authelia's own Homebox integration page documents this. Confirmed live: requesting it without also granting it broke login outright (`invalid_scope: "The OAuth 2.0 Client is not allowed to request scope 'groups'"`), because Authelia enforces a per-client scopes allowlist independent of what the server supports overall — `_authelia_provision_oidc_client()` used to hardcode `openid`/`profile`/`email` for every caller with no way to add more. Fixed by giving it a 6th positional arg, `EXTRA_SCOPES` (space-separated, inserted right after `REQUIRE_PKCE`), that every other existing caller passes as `""` — Homebox's is the only caller that passes `"groups"`. |
|
||||||
| `actualbudget` | Yes — wired up | Pure env vars (`ACTUAL_OPENID_DISCOVERY_URL`, `ACTUAL_OPENID_CLIENT_ID/SECRET`, `ACTUAL_OPENID_SERVER_HOSTNAME`), see `_actualbudget_offer_authelia_oidc()`. Redirect path `/openid/callback` (matches the existing preset in `_authelia_add_oidc_client()`'s menu). First OIDC login becomes the server owner if none is set yet — Actual's own behavior. |
|
| `actualbudget` | Yes — wired up | Pure env vars (`ACTUAL_OPENID_DISCOVERY_URL`, `ACTUAL_OPENID_CLIENT_ID/SECRET`, `ACTUAL_OPENID_SERVER_HOSTNAME`), see `_actualbudget_offer_authelia_oidc()`. Redirect path `/openid/callback` (matches the existing preset in `_authelia_add_oidc_client()`'s menu). First OIDC login becomes the server owner if none is set yet — Actual's own behavior. |
|
||||||
| `immich` | Yes — wired up | Real OAuth2/OIDC settings under Administration → Settings, backed by `GET`/`PUT /api/system-config` — confirmed the exact JSON field names against Immich's own `config-file.md` and source directly (the `oauth` sub-object: `enabled`/`issuerUrl`/`clientId`/`clientSecret`/`scope`/`buttonText`, etc.), not guessed. See `_immich_offer_authelia_oidc()`. GET/PUT exchange the *whole* config object (no partial-patch endpoint), so it round-trips everything else — storage template, library settings — completely unchanged; the same shape already proven by `import-photos.sh`'s own storage-template step in this file. Needs an admin API key, which doesn't exist until the user creates their account on first web visit — this offer runs from both the fresh-install path (usually a no-op that first time) and the "update" rerun path, which is the realistic way most people finish this. |
|
| `immich` | Yes — wired up | Real OAuth2/OIDC settings under Administration → Settings, backed by `GET`/`PUT /api/system-config` — confirmed the exact JSON field names against Immich's own `config-file.md` and source directly (the `oauth` sub-object: `enabled`/`issuerUrl`/`clientId`/`clientSecret`/`scope`/`buttonText`, etc.), not guessed. See `_immich_offer_authelia_oidc()`. GET/PUT exchange the *whole* config object (no partial-patch endpoint), so it round-trips everything else — storage template, library settings — completely unchanged; the same shape already proven by `import-photos.sh`'s own storage-template step in this file. Needs an admin API key, which doesn't exist until the user creates their account on first web visit — this offer runs from both the fresh-install path (usually a no-op that first time) and the "update" rerun path, which is the realistic way most people finish this. |
|
||||||
| `audiobookshelf` | Yes — wired up (Authelia side only) | Checked against audiobookshelf.org's own OIDC docs: config is UI-only (Settings → Authentication), no env var or config API — so `_audiobookshelf_offer_authelia_oidc()` registers the Authelia client (needs PKCE, confirmed via authelia.com's own integration page for it) and prints the exact individual-endpoint values to paste in, since Audiobookshelf wants those rather than a discovery URL. Three redirect URIs: web callback, mobile-redirect, and the `audiobookshelf://oauth` app-scheme callback. |
|
| `audiobookshelf` | Yes — wired up (Authelia side only) | Checked against audiobookshelf.org's own OIDC docs: config is UI-only (Settings → Authentication), no env var or config API — so `_audiobookshelf_offer_authelia_oidc()` registers the Authelia client (needs PKCE, confirmed via authelia.com's own integration page for it) and prints the exact individual-endpoint values to paste in, since Audiobookshelf wants those rather than a discovery URL. Three redirect URIs: web callback, mobile-redirect, and the `audiobookshelf://oauth` app-scheme callback. |
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ _actualbudget_offer_authelia_oidc() {
|
|||||||
prompt_yn " Require two-factor for Actual Budget logins via Authelia too? (y/n):" "y" _2fa
|
prompt_yn " Require two-factor for Actual Budget logins via Authelia too? (y/n):" "y" _2fa
|
||||||
[[ "$_2fa" =~ ^[Yy]$ ]] || AUTH_POLICY="one_factor"
|
[[ "$_2fa" =~ ^[Yy]$ ]] || AUTH_POLICY="one_factor"
|
||||||
|
|
||||||
if ! _authelia_provision_oidc_client "ActualBudget" "actualbudget" "$AUTH_POLICY" "y" "n" \
|
if ! _authelia_provision_oidc_client "ActualBudget" "actualbudget" "$AUTH_POLICY" "y" "n" "" \
|
||||||
"https://${AB_OIDC_DOMAIN}/openid/callback"; then
|
"https://${AB_OIDC_DOMAIN}/openid/callback"; then
|
||||||
log_warning "Couldn't register Actual Budget as an OIDC client in Authelia — skipping SSO setup."
|
log_warning "Couldn't register Actual Budget as an OIDC client in Authelia — skipping SSO setup."
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ _audiobookshelf_offer_authelia_oidc() {
|
|||||||
prompt_yn " Require two-factor for Audiobookshelf logins via Authelia too? (y/n):" "y" _2fa
|
prompt_yn " Require two-factor for Audiobookshelf logins via Authelia too? (y/n):" "y" _2fa
|
||||||
[[ "$_2fa" =~ ^[Yy]$ ]] || AUTH_POLICY="one_factor"
|
[[ "$_2fa" =~ ^[Yy]$ ]] || AUTH_POLICY="one_factor"
|
||||||
|
|
||||||
if ! _authelia_provision_oidc_client "Audiobookshelf" "audiobookshelf" "$AUTH_POLICY" "y" "y" \
|
if ! _authelia_provision_oidc_client "Audiobookshelf" "audiobookshelf" "$AUTH_POLICY" "y" "y" "" \
|
||||||
"https://${APP_DOMAIN}/auth/openid/callback" "https://${APP_DOMAIN}/auth/openid/mobile-redirect" "audiobookshelf://oauth"; then
|
"https://${APP_DOMAIN}/auth/openid/callback" "https://${APP_DOMAIN}/auth/openid/mobile-redirect" "audiobookshelf://oauth"; then
|
||||||
log_warning "Couldn't register Audiobookshelf as an OIDC client in Authelia — skipping SSO setup."
|
log_warning "Couldn't register Audiobookshelf as an OIDC client in Authelia — skipping SSO setup."
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
+23
-4
@@ -2985,7 +2985,13 @@ _authelia_remove_oidc_client() {
|
|||||||
# Guard every cross-file call with `declare -F` per this repo's chaining
|
# Guard every cross-file call with `declare -F` per this repo's chaining
|
||||||
# convention (services/gitea.sh does).
|
# convention (services/gitea.sh does).
|
||||||
#
|
#
|
||||||
# Args: APP_NAME CLIENT_ID AUTH_POLICY RESTART_AUTH(y/n) <redirect_uri> [<redirect_uri> ...]
|
# Args: APP_NAME CLIENT_ID AUTH_POLICY RESTART_AUTH(y/n) REQUIRE_PKCE(y/n) EXTRA_SCOPES <redirect_uri> [<redirect_uri> ...]
|
||||||
|
# EXTRA_SCOPES — space-separated scope names to add on top of the
|
||||||
|
# always-included openid/profile/email (e.g. "groups" for an app whose own
|
||||||
|
# OIDC settings request group membership, like Homebox). Pass "" when the
|
||||||
|
# app only needs the three defaults — every existing caller before this
|
||||||
|
# parameter was added does exactly that, so their registered client is
|
||||||
|
# byte-for-byte unchanged.
|
||||||
# Out-params (not `local` — read them after the call returns):
|
# Out-params (not `local` — read them after the call returns):
|
||||||
# OIDC_CLIENT_SECRET_PLAIN the plaintext secret. Shown once — Authelia's
|
# OIDC_CLIENT_SECRET_PLAIN the plaintext secret. Shown once — Authelia's
|
||||||
# config only ever stores the hash — so the
|
# config only ever stores the hash — so the
|
||||||
@@ -3010,7 +3016,7 @@ _authelia_remove_oidc_client() {
|
|||||||
# that's already registered is NOT a failure — it gets replaced (see the
|
# that's already registered is NOT a failure — it gets replaced (see the
|
||||||
# comment at that check below).
|
# comment at that check below).
|
||||||
_authelia_provision_oidc_client() {
|
_authelia_provision_oidc_client() {
|
||||||
local APP_NAME="$1" CLIENT_ID="$2" AUTH_POLICY="$3" RESTART_AUTH="$4" REQUIRE_PKCE="$5"; shift 5
|
local APP_NAME="$1" CLIENT_ID="$2" AUTH_POLICY="$3" RESTART_AUTH="$4" REQUIRE_PKCE="$5" EXTRA_SCOPES="$6"; shift 6
|
||||||
local -a REDIRECT_URIS=("$@")
|
local -a REDIRECT_URIS=("$@")
|
||||||
|
|
||||||
OIDC_CLIENT_SECRET_PLAIN=""
|
OIDC_CLIENT_SECRET_PLAIN=""
|
||||||
@@ -3121,6 +3127,19 @@ _authelia_provision_oidc_client() {
|
|||||||
pkce_challenge_method: 'S256'"
|
pkce_challenge_method: 'S256'"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# EXTRA_SCOPES is space-separated (e.g. "groups") and additive to the
|
||||||
|
# three always-included scopes below — Authelia rejects a callback
|
||||||
|
# requesting any scope not in this exact per-client allowlist, even one
|
||||||
|
# the server otherwise supports (confirmed live: Homebox's own
|
||||||
|
# HBOX_OIDC_SCOPE=openid profile email groups was rejected with
|
||||||
|
# invalid_scope until 'groups' was added here too).
|
||||||
|
local EXTRA_SCOPES_YAML=""
|
||||||
|
local _scope
|
||||||
|
for _scope in $EXTRA_SCOPES; do
|
||||||
|
EXTRA_SCOPES_YAML="${EXTRA_SCOPES_YAML}
|
||||||
|
- '${_scope}'"
|
||||||
|
done
|
||||||
|
|
||||||
local CLIENT_BLOCK=" - client_id: '${CLIENT_ID}'
|
local CLIENT_BLOCK=" - client_id: '${CLIENT_ID}'
|
||||||
client_name: '${APP_NAME}'
|
client_name: '${APP_NAME}'
|
||||||
client_secret: '${CLIENT_SECRET_HASH}'
|
client_secret: '${CLIENT_SECRET_HASH}'
|
||||||
@@ -3131,7 +3150,7 @@ ${REDIRECT_URIS_YAML}
|
|||||||
scopes:
|
scopes:
|
||||||
- 'openid'
|
- 'openid'
|
||||||
- 'profile'
|
- 'profile'
|
||||||
- 'email'
|
- 'email'${EXTRA_SCOPES_YAML}
|
||||||
grant_types:
|
grant_types:
|
||||||
- 'authorization_code'
|
- 'authorization_code'
|
||||||
response_types:
|
response_types:
|
||||||
@@ -3287,7 +3306,7 @@ _authelia_add_oidc_client() {
|
|||||||
local RESTART_AUTH=""
|
local RESTART_AUTH=""
|
||||||
prompt_yn " Restart Authelia to apply? (y/n):" "y" RESTART_AUTH
|
prompt_yn " Restart Authelia to apply? (y/n):" "y" RESTART_AUTH
|
||||||
|
|
||||||
_authelia_provision_oidc_client "$APP_NAME" "$CLIENT_ID" "$AUTH_POLICY" "$RESTART_AUTH" "$REQUIRE_PKCE" "${REDIRECT_URIS[@]}" \
|
_authelia_provision_oidc_client "$APP_NAME" "$CLIENT_ID" "$AUTH_POLICY" "$RESTART_AUTH" "$REQUIRE_PKCE" "" "${REDIRECT_URIS[@]}" \
|
||||||
|| return 1
|
|| return 1
|
||||||
local CLIENT_SECRET_PLAIN="$OIDC_CLIENT_SECRET_PLAIN"
|
local CLIENT_SECRET_PLAIN="$OIDC_CLIENT_SECRET_PLAIN"
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -286,7 +286,7 @@ _beszel_offer_authelia_oidc() {
|
|||||||
prompt_yn " Require two-factor for Beszel logins via Authelia too? (y/n):" "y" _2fa
|
prompt_yn " Require two-factor for Beszel logins via Authelia too? (y/n):" "y" _2fa
|
||||||
[[ "$_2fa" =~ ^[Yy]$ ]] || AUTH_POLICY="one_factor"
|
[[ "$_2fa" =~ ^[Yy]$ ]] || AUTH_POLICY="one_factor"
|
||||||
|
|
||||||
if ! _authelia_provision_oidc_client "Beszel" "beszel" "$AUTH_POLICY" "y" "y" \
|
if ! _authelia_provision_oidc_client "Beszel" "beszel" "$AUTH_POLICY" "y" "y" "" \
|
||||||
"https://${APP_DOMAIN}/api/oauth2-redirect"; then
|
"https://${APP_DOMAIN}/api/oauth2-redirect"; then
|
||||||
log_warning "Couldn't register Beszel as an OIDC client in Authelia — skipping SSO setup."
|
log_warning "Couldn't register Beszel as an OIDC client in Authelia — skipping SSO setup."
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
+1
-1
@@ -219,7 +219,7 @@ _gitea_offer_authelia_sso() {
|
|||||||
prompt_yn " Require two-factor for Gitea logins via Authelia too? (y/n):" "y" _2fa
|
prompt_yn " Require two-factor for Gitea logins via Authelia too? (y/n):" "y" _2fa
|
||||||
[[ "$_2fa" =~ ^[Yy]$ ]] || AUTH_POLICY="one_factor"
|
[[ "$_2fa" =~ ^[Yy]$ ]] || AUTH_POLICY="one_factor"
|
||||||
|
|
||||||
if ! _authelia_provision_oidc_client "Gitea" "gitea" "$AUTH_POLICY" "y" "n" \
|
if ! _authelia_provision_oidc_client "Gitea" "gitea" "$AUTH_POLICY" "y" "n" "" \
|
||||||
"https://${GITEA_OIDC_DOMAIN}/user/oauth2/authelia/callback"; then
|
"https://${GITEA_OIDC_DOMAIN}/user/oauth2/authelia/callback"; then
|
||||||
log_warning "Couldn't register Gitea as an OIDC client in Authelia — skipping SSO setup."
|
log_warning "Couldn't register Gitea as an OIDC client in Authelia — skipping SSO setup."
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
+1
-1
@@ -249,7 +249,7 @@ _homebox_offer_authelia_oidc() {
|
|||||||
prompt_yn " Require two-factor for Homebox logins via Authelia too? (y/n):" "y" _2fa
|
prompt_yn " Require two-factor for Homebox logins via Authelia too? (y/n):" "y" _2fa
|
||||||
[[ "$_2fa" =~ ^[Yy]$ ]] || AUTH_POLICY="one_factor"
|
[[ "$_2fa" =~ ^[Yy]$ ]] || AUTH_POLICY="one_factor"
|
||||||
|
|
||||||
if ! _authelia_provision_oidc_client "Homebox" "homebox" "$AUTH_POLICY" "y" "y" \
|
if ! _authelia_provision_oidc_client "Homebox" "homebox" "$AUTH_POLICY" "y" "y" "groups" \
|
||||||
"https://${APP_DOMAIN}/api/v1/users/login/oidc/callback"; then
|
"https://${APP_DOMAIN}/api/v1/users/login/oidc/callback"; then
|
||||||
log_warning "Couldn't register Homebox as an OIDC client in Authelia — skipping SSO setup."
|
log_warning "Couldn't register Homebox as an OIDC client in Authelia — skipping SSO setup."
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
+1
-1
@@ -274,7 +274,7 @@ _immich_offer_authelia_oidc() {
|
|||||||
# Same three redirect URIs as the "Immich" preset in authelia.sh's own
|
# Same three redirect URIs as the "Immich" preset in authelia.sh's own
|
||||||
# generic OIDC menu (web login, account-linking, mobile app callback) —
|
# generic OIDC menu (web login, account-linking, mobile app callback) —
|
||||||
# kept identical on purpose so either path produces the same client.
|
# kept identical on purpose so either path produces the same client.
|
||||||
if ! _authelia_provision_oidc_client "Immich" "immich" "$AUTH_POLICY" "y" "n" \
|
if ! _authelia_provision_oidc_client "Immich" "immich" "$AUTH_POLICY" "y" "n" "" \
|
||||||
"https://${APP_DOMAIN}/auth/login" "https://${APP_DOMAIN}/user-settings" "app.immich:///oauth-callback"; then
|
"https://${APP_DOMAIN}/auth/login" "https://${APP_DOMAIN}/user-settings" "app.immich:///oauth-callback"; then
|
||||||
log_warning "Couldn't register Immich as an OIDC client in Authelia — skipping SSO setup."
|
log_warning "Couldn't register Immich as an OIDC client in Authelia — skipping SSO setup."
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
+1
-1
@@ -259,7 +259,7 @@ _mealie_offer_authelia_oidc() {
|
|||||||
prompt_yn " Require two-factor for Mealie logins via Authelia too? (y/n):" "y" _2fa
|
prompt_yn " Require two-factor for Mealie logins via Authelia too? (y/n):" "y" _2fa
|
||||||
[[ "$_2fa" =~ ^[Yy]$ ]] || AUTH_POLICY="one_factor"
|
[[ "$_2fa" =~ ^[Yy]$ ]] || AUTH_POLICY="one_factor"
|
||||||
|
|
||||||
if ! _authelia_provision_oidc_client "Mealie" "mealie" "$AUTH_POLICY" "y" "n" "${BASE_URL}/login"; then
|
if ! _authelia_provision_oidc_client "Mealie" "mealie" "$AUTH_POLICY" "y" "n" "" "${BASE_URL}/login"; then
|
||||||
log_warning "Couldn't register Mealie as an OIDC client in Authelia — skipping SSO setup."
|
log_warning "Couldn't register Mealie as an OIDC client in Authelia — skipping SSO setup."
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user