Merge pull request #394 from outis1one/claude/frigate-authelia-openid-0l1htj

Claude/frigate authelia openid 0l1htj
This commit is contained in:
Outis
2026-08-25 13:27:36 -04:00
committed by GitHub
4 changed files with 317 additions and 83 deletions
+42 -15
View File
@@ -422,22 +422,49 @@ existing login page. Reuse `_authelia_provision_oidc_client()` (guarded by
instead of duplicating Authelia's client-secret-generation/config-patching
logic again.
**Scoping a domain to specific users instead of every Authelia user.**
By default, any domain with an `access_control` rule at all is reachable by
every Authelia user (the existing catch-all `*.${AUTHELIA_DOMAIN}` rule).
`services/authelia.sh`'s `_authelia_scope_access(SERVICE_ID, DOMAIN)` is a
generic, reusable opt-in on top of that — call it right after *any* service
finishes being protected by Authelia, forward_auth gate or native OIDC
alike (it only cares about the domain, not the gating mechanism; see
`_gitea_offer_authelia_sso()` for the reference caller). Asks whether
access should stay universal or be scoped to specific usernames; if scoped,
creates a dedicated `<service_id>-only` group, adds every listed username
to it (creating accounts on the fly via
**Internal vs. outside access — named, reusable groups, not one group per
service.** By default, any domain with an `access_control` rule at all is
"internal": reachable by every Authelia user (the existing catch-all
`*.${AUTHELIA_DOMAIN}` rule) — admins included automatically, since the
admin-bypass rule (below) always outranks it anyway. `services/authelia.sh`'s
`_authelia_scope_access(SERVICE_ID, DOMAIN)` is the generic, reusable opt-in
on top of that for "outside access" — call it right after *any* service
finishes being protected by Authelia, forward_auth gate or native OIDC alike
(it only cares about the domain, not the gating mechanism; see
`_gitea_offer_authelia_sso()` for the reference caller). Asks "Internal
(default) or Outside access", and if outside access, lets the admin pick an
*existing* named group (by number, so e.g. "customer1" can be attached to a
second, third, unrelated site later) or type a new one — `service_id` is
only the suggested default name, never forced. Creates the group if new
(adding every listed username to it, creating accounts on the fly via
`_authelia_create_user_noninteractive()` for names that don't exist yet,
printing their temp password), and inserts two rules *above* the general
catch-all — allow that group on this domain, deny that group on every
other protected domain. Idempotent: reruns against an already-scoped
domain just report the existing group instead of duplicating rules.
printing their temp password) and inserts two rules *above* the general
catch-all but *below* the admin-bypass rule — allow that group on this
domain, deny that group on every other protected domain. The already-scoped
check is keyed to the (domain, group) pair, not the group name alone, so
reusing a group on a second site correctly adds that site's own rule instead
of a false "already scoped" no-op (a real bug in an earlier version of this
function, since fixed).
Three more menu options round this out: **13** backfills the admin-bypass
rule (below) onto any apex domain missing it; **14** renames a group
everywhere it's referenced (rules + every member); **15** lists every
group's sites and members in one place; **16** is the reverse of the
scoping prompt's own member-picker — pick a group first, then toggle which
users are in it, for adding members without re-touching a site.
**Admins always match first, on every domain — old sites and new.**
`_authelia_ensure_admin_bypass(config_file, domain)` inserts
`- domain: "*.${domain}" / subject: "group:admins" / policy: two_factor` as
literally the first rule under `rules:`, and every insertion point that
adds new rules (`add_authelia_domain`, `_authelia_scope_access`) inserts
*after* this block rather than at the literal top of `rules:`, so a later
scoping action can never accidentally outrank it. Without this, a group's
deny-elsewhere rule (above) would deny an admin who's ever added to that
group on every OTHER domain — this rule exists specifically so that can't
happen. `install_authelia()`/`add_authelia_domain()` bake it in for
anything created from here on; menu option 13 backfills it onto an
instance that predates the feature.
Guard every cross-file call with `declare -F`, same convention as the OIDC
helper above — a service can run standalone with authelia.sh never sourced.
+166 -11
View File
@@ -250,10 +250,13 @@ install_authelia() {
echo " 15) Show every group's sites and users (site groups + user groups overview)"
echo " 16) Add/remove users from a group (pick the group, then toggle members —"
echo " the reverse of option 4's per-user group toggle)"
echo " 17) Bulk-assign several users to one group at once (e.g. \"1 4 5\" ->"
echo " internal, then \"2 3 7\" -> a named group), with each user's"
echo " current access shown alongside their name"
echo " 0) Leave as-is / exit"
echo ""
local EXISTING_CHOICE=""
prompt_text " Choice [1-16, 0 to exit]:" "0" EXISTING_CHOICE
prompt_text " Choice [1-17, 0 to exit]:" "0" EXISTING_CHOICE
case "$EXISTING_CHOICE" in
1)
add_authelia_domain
@@ -318,6 +321,10 @@ install_authelia() {
_authelia_manage_group_membership
return 0
;;
17)
_authelia_bulk_assign_group
return 0
;;
0|*)
echo " Keeping existing Authelia. (Edit config/users.yml then: cd $AUTHELIA_DIR && docker compose restart authelia)"
return 0
@@ -1689,11 +1696,11 @@ _authelia_scope_access() {
echo ""
echo " Who should be able to reach $domain via Authelia?"
echo " 0) Native — your own users, no extra restriction (default — same"
echo " 0) Internal — your own users, no extra restriction (default — same"
echo " access as everything else)"
echo " 1) Outside access — a named group of specific users only"
local scope_choice=""
prompt_text " Choice [0 for native, 1 for outside access]:" "0" scope_choice
prompt_text " Choice [0 for internal, 1 for outside access]:" "0" scope_choice
[ "$scope_choice" = "1" ] || return 0
local -a existing_groups
@@ -1936,7 +1943,7 @@ _authelia_report_groups() {
local -a groups
mapfile -t groups < <(_authelia_list_scoped_groups "$users_file")
if [ "${#groups[@]}" -eq 0 ]; then
log_info "No outside-access groups exist yet — every site is native (open to any Authelia user, admins always included)."
log_info "No outside-access groups exist yet — every site is internal (open to any Authelia user, admins always included)."
return 0
fi
@@ -1995,7 +2002,7 @@ _authelia_manage_group_membership() {
if [ "${#groups[@]}" -eq 0 ]; then
log_info "No outside-access groups exist yet. A group is created the first time you"
log_info "protect a site (option 10, or a service's own \"Add Sign in with Authelia\""
log_info "offer) and choose \"Outside access\" instead of \"Native\" — name it there"
log_info "offer) and choose \"Outside access\" instead of \"Internal\" — name it there"
log_info "(e.g. \"customer1\"), and it'll show up here afterward to manage its members."
return 0
fi
@@ -2075,6 +2082,129 @@ _authelia_manage_group_membership() {
fi
}
# Bulk version spanning BOTH axes at once — several users, one target group,
# in a single step (e.g. "1 4 5 6 -> internal", then "2 3 7 8 -> external1"),
# repeatable for as many user/group batches as needed in one menu visit.
# Complements the two single-axis tools above: option 6 (per-user menu) is
# one user, many groups to toggle; option 16 is one group, many users to
# toggle; this is many users, one group, picked together. "Internal" isn't
# a real group — picking it clears every outside-access group membership
# for the selected users, since internal access is the absence of a
# restricting group, not a group of its own.
_authelia_bulk_assign_group() {
local users_file="$DOCKER_DIR/authelia/config/users.yml"
[ -f "$users_file" ] || { log_warning "No users.yml found — install Authelia first."; return 1; }
local KEEP_GOING="y"
while [[ "$KEEP_GOING" =~ ^[Yy]$ ]]; do
local -a all_users
mapfile -t all_users < <(_authelia_list_usernames "$users_file")
if [ "${#all_users[@]}" -eq 0 ]; then
log_info "No users exist yet — add one first (this menu's \"Add a new user\")."
return 0
fi
echo ""
echo " Existing users:"
local i start_end start end
for i in "${!all_users[@]}"; do
start_end="$(_authelia_user_line_range "$users_file" "${all_users[$i]}")"
start="${start_end% *}"; end="${start_end#* }"
echo " $((i + 1))) ${all_users[$i]} [$(_authelia_describe_user_access "$users_file" "$start" "$end")]"
done
echo ""
echo " Select one or more users by number (space-separated), or 0 to cancel."
local USEL=""
prompt_text " User number(s) [0]:" "0" USEL
if [ -z "$USEL" ] || [ "$USEL" = "0" ]; then
log_info "Cancelled."
return 0
fi
local -a usel_tokens targets=()
read -ra usel_tokens <<< "$USEL"
local tok
for tok in "${usel_tokens[@]}"; do
if [[ "$tok" =~ ^[0-9]+$ ]] && [ "$tok" -ge 1 ] && [ "$tok" -le "${#all_users[@]}" ]; then
targets+=("${all_users[$((tok - 1))]}")
else
log_warning "Skipping invalid selection: $tok"
fi
done
if [ "${#targets[@]}" -eq 0 ]; then
log_warning "No valid users selected."
prompt_yn " Try again? (y/n):" "n" KEEP_GOING
continue
fi
local -a existing_groups
mapfile -t existing_groups < <(_authelia_list_scoped_groups "$users_file")
echo ""
echo " Assign ${#targets[@]} user(s) to:"
echo " 0) Internal — remove from every outside-access group"
local gi
for gi in "${!existing_groups[@]}"; do
echo " $((gi + 1))) ${existing_groups[$gi]%-only}"
done
echo " Or type a new group name to create one."
local GSEL=""
prompt_text " Group [0 for internal]:" "0" GSEL
if [ -z "$GSEL" ] || [ "$GSEL" = "0" ]; then
local t t_start_end t_start t_end g
for t in "${targets[@]}"; do
t_start_end="$(_authelia_user_line_range "$users_file" "$t")"
t_start="${t_start_end% *}"; t_end="${t_start_end#* }"
for g in "${existing_groups[@]}"; do
sed -n "${t_start},${t_end}p" "$users_file" | grep -qF " - ${g}" \
&& _authelia_toggle_group "$users_file" "$t_start" "$t_end" "$g" "false"
done
log_success "${t} set to internal (removed from every outside-access group)"
done
else
local group=""
if [[ "$GSEL" =~ ^[0-9]+$ ]] && [ "$GSEL" -ge 1 ] && [ "$GSEL" -le "${#existing_groups[@]}" ]; then
group="${existing_groups[$((GSEL - 1))]}"
else
local clean_name
clean_name="$(echo "$GSEL" | tr -cs 'a-zA-Z0-9_-' '-' | sed 's/^-*//;s/-*$//')"
if [ -z "$clean_name" ]; then
log_warning "Invalid group name — nothing changed."
prompt_yn " Try again? (y/n):" "n" KEEP_GOING
continue
fi
group="${clean_name}-only"
local is_new="true" eg
for eg in "${existing_groups[@]}"; do [ "$eg" = "$group" ] && is_new="false"; done
if [ "$is_new" = "true" ]; then
log_warning "'$clean_name' isn't attached to any site yet — membership alone won't grant"
log_warning "access to anything until a site is scoped to it (site protection's"
log_warning "\"Outside access\" choice, or re-running a service's own SSO offer)."
fi
fi
local t t_start_end2 t_start2 t_end2
for t in "${targets[@]}"; do
t_start_end2="$(_authelia_user_line_range "$users_file" "$t")"
t_start2="${t_start_end2% *}"; t_end2="${t_start_end2#* }"
_authelia_toggle_group "$users_file" "$t_start2" "$t_end2" "$group" "true"
log_success "Added ${t} to '${group%-only}'"
done
fi
chown 1000:1000 "$users_file" 2>/dev/null || true
local RESTART_AUTH=""
prompt_yn " Restart Authelia to apply? (y/n):" "y" RESTART_AUTH
if [[ "$RESTART_AUTH" =~ ^[Yy]$ ]]; then
(cd "$DOCKER_DIR/authelia" && docker compose restart authelia 2>/dev/null) \
&& log_success "Authelia restarted" \
|| log_warning "Restart failed — check: docker compose logs authelia"
fi
echo ""
prompt_yn " Assign another batch (different users and/or a different group)? (y/n):" "n" KEEP_GOING
done
}
# Menu-driven, idempotent bulk version of _authelia_ensure_admin_bypass —
# backfills the admin-bypass rule for every apex domain currently on this
# instance in one pass. install_authelia and add_authelia_domain bake the
@@ -2147,7 +2277,7 @@ _authelia_report_access_scope() {
fi
echo ""
echo " Native (universal — every protected domain, no outside-access group):"
echo " Internal (universal — every protected domain, no outside-access group):"
local -a universal=() restricted=()
local u start_end start end groups_in_range
for u in "${all_users[@]}"; do
@@ -2452,6 +2582,23 @@ _authelia_set_2fa_exempt() {
chown 1000:1000 "$config_file" 2>/dev/null || true
}
# One-line access summary for a user, e.g. "admin" / "internal" /
# "customer1, customer2" / "admin, customer1" — used by both
# edit_authelia_user()'s listing and _authelia_bulk_assign_group() so a
# user's current privileges are visible right where you're about to change
# them, not something you have to cross-check against option 15 first.
_authelia_describe_user_access() {
local users_file="$1" start="$2" end="$3"
local -a tags=()
sed -n "${start},${end}p" "$users_file" | grep -q '^ - admins$' && tags+=("admin")
local -a groups
mapfile -t groups < <(sed -n "${start},${end}p" "$users_file" | grep -oE '^ - [a-zA-Z0-9_-]+-only$' | sed 's/^ - //; s/-only$//')
tags+=("${groups[@]}")
[ "${#tags[@]}" -eq 0 ] && tags=("internal")
local IFS=", "
echo "${tags[*]}"
}
# Interactive: pick an existing user from users.yml, then act on them —
# edit email/display name, force a password reset, reset their 2FA device,
# toggle whether they need 2FA at all, or toggle admin group membership.
@@ -2481,9 +2628,11 @@ edit_authelia_user() {
echo ""
echo " Existing users:"
local i
local i u_start_end u_start u_end
for i in "${!USERNAMES[@]}"; do
echo " $((i + 1))) ${USERNAMES[$i]}"
u_start_end="$(_authelia_user_line_range "$USERS_FILE" "${USERNAMES[$i]}")"
u_start="${u_start_end% *}"; u_end="${u_start_end#* }"
echo " $((i + 1))) ${USERNAMES[$i]} [$(_authelia_describe_user_access "$USERS_FILE" "$u_start" "$u_end")]"
done
echo ""
echo " Select one or more by number (space-separated, e.g. \"2 4\"),"
@@ -3200,14 +3349,20 @@ _authelia_add_oidc_client() {
echo ""
;;
6)
echo " Beszel is PocketBase-based — its OAuth2 provider is a PocketBase admin"
echo " setting, not an API this script can write (checked against beszel.dev's"
echo " own docs). In the hub: Settings -> Auth providers -> OpenID Connect:"
echo " Beszel is PocketBase-based — its OAuth2 provider lives in PocketBase's"
echo " own admin panel underneath the hub, not the hub's own Settings page and"
echo " not an API this script can write (checked against beszel.dev's own docs):"
echo " 1) https://<your-beszel-domain>/_/#/settings -> toggle OFF \"Hide"
echo " collection create and edit controls\""
echo " 2) Collections -> edit the \"users\" collection"
echo " 3) Options tab -> enable OAuth2 -> Add provider, fill in:"
echo " Client ID: ${CLIENT_ID}"
echo " Client Secret: ${CLIENT_SECRET_PLAIN}"
echo " Auth URL: https://${AUTHELIA_PORTAL_DOMAIN}/api/oidc/authorization"
echo " Token URL: https://${AUTHELIA_PORTAL_DOMAIN}/api/oidc/token"
echo " User Info URL: https://${AUTHELIA_PORTAL_DOMAIN}/api/oidc/userinfo"
echo " 4) Save, then toggle \"Hide collection create and edit controls\" back"
echo " ON — leaving it off is its own exposure once you're done"
echo " Register your first Beszel account with a password BEFORE touching"
echo " DISABLE_PASSWORD_AUTH/USER_CREATION in its .env — flipping those before"
echo " a working login exists risks locking the hub's UI out entirely."
+56 -23
View File
@@ -288,41 +288,73 @@ _beszel_offer_authelia_oidc() {
fi
echo ""
echo " In the hub: Settings -> Auth providers -> OpenID Connect:"
echo " This lives in PocketBase's own admin panel underneath the hub, not the"
echo " hub's own Settings page — checked against beszel.dev's OAuth guide"
echo " directly, exact steps:"
echo " 1) Go to https://<your-beszel-domain>/_/#/settings and toggle OFF"
echo " \"Hide collection create and edit controls\""
echo " 2) Go to Collections, edit the \"users\" collection"
echo " 3) Options tab -> enable OAuth2 -> Add provider, fill in:"
echo " Client ID: beszel"
echo " Client Secret: $OIDC_CLIENT_SECRET_PLAIN"
echo " Auth URL: ${OIDC_AUTHELIA_PORTAL_URL}/api/oidc/authorization"
echo " Token URL: ${OIDC_AUTHELIA_PORTAL_URL}/api/oidc/token"
echo " User Info URL: ${OIDC_AUTHELIA_PORTAL_URL}/api/oidc/userinfo"
echo " 4) Save, then toggle \"Hide collection create and edit controls\" back ON"
echo " at /_/#/settings — leaving it off is its own exposure once you're done"
echo ""
log_warning "The Client Secret above is shown once — save it now."
declare -F _authelia_scope_access >/dev/null 2>&1 && _authelia_scope_access "beszel" "$APP_DOMAIN"
# DISABLE_PASSWORD_AUTH/USER_CREATION are real, documented env vars
# (beszel.dev's own OAuth guide) — but Beszel has no default account:
# the FIRST person to register becomes admin (see this file's own
# README section), and there's no signup-fallback like Mealie's
# OIDC_SIGNUP_ENABLED convenience wording to lean on if that hasn't
# happened yet. Off by default and gated behind an explicit warning —
# flipping this before a working login exists risks locking the hub's
# UI out with no account able to reach it at all.
echo ""
local _disable_local=""
prompt_yn " Also disable Beszel's own password login, so Authelia is the only way in? Only do this AFTER you've registered a working account. (y/n):" "n" _disable_local
if [[ "$_disable_local" =~ ^[Yy]$ ]]; then
local _auto_register=""
prompt_yn " Auto-create Beszel accounts for new Authelia logins? (y/n):" "n" _auto_register
sed -i '/^DISABLE_PASSWORD_AUTH=/d; /^USER_CREATION=/d' "$dir/.env"
{
echo "DISABLE_PASSWORD_AUTH=true"
[[ "$_auto_register" =~ ^[Yy]$ ]] && echo "USER_CREATION=true"
} >> "$dir/.env"
chown "$ACTUAL_USER:$ACTUAL_USER" "$dir/.env" 2>/dev/null || true
( cd "$dir" && docker compose up -d beszel ) \
&& log_success "Beszel's own password login is now disabled — Authelia is the only way in." \
|| log_warning "Restart failed — check: docker compose -f $dir/docker-compose.yml logs beszel"
log_info "Paste those values into Beszel's Settings -> Auth providers -> OpenID"
log_info "Connect page now, then log out and click through the Authelia login"
log_info "button to confirm it actually works — BEFORE going any further here."
echo ""
log_warning "The next step can disable Beszel's password login entirely. Confirmed"
log_warning "live: saying yes here before actually testing the button leaves NEITHER"
log_warning "login path working — the password form is gone, and the OAuth provider"
log_warning "was never actually finished on Beszel's side, so its button never"
log_warning "appears either. Re-run 'sudo ./setup.sh beszel' (choose update) any time"
log_warning "later to come back to this once you've verified the button works."
}
# Split out from _beszel_offer_authelia_oidc so it can also be re-reached on
# its own via a later "update" rerun, once the admin has actually gone and
# tested the Authelia login button — see that function's own warning for
# why this can't be offered in the same breath as printing the paste-in
# values. DISABLE_PASSWORD_AUTH/USER_CREATION are real, documented env vars
# (beszel.dev's own OAuth guide).
_beszel_offer_disable_password_auth() {
local dir="$1"
[ -f "$dir/.env" ] || return 0
grep -qF "client_id: 'beszel'" "$DOCKER_DIR/authelia/config/configuration.yml" 2>/dev/null || return 0
grep -q '^DISABLE_PASSWORD_AUTH=true' "$dir/.env" 2>/dev/null && return 0
echo ""
local _tested=""
prompt_yn " Have you ALREADY logged into Beszel successfully using the Authelia button (not just pasted the values)? (y/n):" "n" _tested
if [[ ! "$_tested" =~ ^[Yy]$ ]]; then
log_info "Skipped. Test the Authelia login button first, then re-run 'sudo ./setup.sh beszel' (choose update) to come back to this."
return 0
fi
local _disable_local=""
prompt_yn " Disable Beszel's own password login now, so Authelia is the only way in? (y/n):" "n" _disable_local
[[ "$_disable_local" =~ ^[Yy]$ ]] || return 0
local _auto_register=""
prompt_yn " Auto-create Beszel accounts for new Authelia logins? (y/n):" "n" _auto_register
sed -i '/^DISABLE_PASSWORD_AUTH=/d; /^USER_CREATION=/d' "$dir/.env"
{
echo "DISABLE_PASSWORD_AUTH=true"
[[ "$_auto_register" =~ ^[Yy]$ ]] && echo "USER_CREATION=true"
} >> "$dir/.env"
chown "$ACTUAL_USER:$ACTUAL_USER" "$dir/.env" 2>/dev/null || true
( cd "$dir" && docker compose up -d beszel ) \
&& log_success "Beszel's own password login is now disabled — Authelia is the only way in." \
|| log_warning "Restart failed — check: docker compose -f $dir/docker-compose.yml logs beszel"
}
install_beszel() {
@@ -361,6 +393,7 @@ install_beszel() {
[[ "$FINISH_AGENT" =~ ^[Yy]$ ]] && _beszel_configure_agent "$DIR" "http://localhost:${_WP} (or its Caddy domain, once configured)"
fi
_beszel_offer_authelia_oidc "$DIR"
_beszel_offer_disable_password_auth "$DIR"
return 0
;;
cancel)
+53 -34
View File
@@ -228,7 +228,10 @@ _mealie_offer_authelia_oidc() {
log_info "Authelia SSO is already configured for Mealie (OIDC_* already set in $DIR/.env)."
local RECONFIGURE=""
prompt_yn " Reconfigure it (registers a fresh Authelia client + secret)? (y/n):" "n" RECONFIGURE
[[ "$RECONFIGURE" =~ ^[Yy]$ ]] || return 0
if [[ ! "$RECONFIGURE" =~ ^[Yy]$ ]]; then
_mealie_offer_disable_password_login "$DIR"
return 0
fi
# ALLOW_PASSWORD_LOGIN isn't OIDC_-prefixed but is written by this
# same step (see below) — strip it too so reconfiguring doesn't
# leave a stale duplicate line if it's set again.
@@ -251,27 +254,6 @@ _mealie_offer_authelia_oidc() {
prompt_yn " Require two-factor for Mealie logins via Authelia too? (y/n):" "y" _2fa
[[ "$_2fa" =~ ^[Yy]$ ]] || AUTH_POLICY="one_factor"
# Mealie's own docs document ALLOW_PASSWORD_LOGIN (hides the
# username+password fields entirely) and OIDC_AUTO_REDIRECT (skips the
# login page and jumps straight to the identity provider) as the real,
# supported way to make Authelia the only way in — confirmed against
# docs.mealie.io's OIDC and backend-config pages directly, not assumed.
# Off by default: this is a real access-control change (anyone who
# only has a local Mealie account, not an Authelia one, loses their
# login path), not just an additive convenience like the SSO button
# above. Recoverable any time by flipping these back and restarting —
# worth saying so, since a locked-out admin's first instinct otherwise
# is to worry the account itself is gone.
echo ""
local _disable_local=""
prompt_yn " Also disable Mealie's own username/password login, so Authelia is the only way in? (y/n):" "n" _disable_local
local _auto_redirect=""
if [[ "$_disable_local" =~ ^[Yy]$ ]]; then
log_warning "Anyone without an Authelia account (only a local Mealie one) will no longer be able to log in."
log_info "Reversible any time: set ALLOW_PASSWORD_LOGIN back to true in $DIR/.env and 'docker compose up -d'."
prompt_yn " Skip Mealie's login page entirely and jump straight to Authelia? (y/n):" "y" _auto_redirect
fi
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."
return 0
@@ -289,15 +271,6 @@ OIDC_CLIENT_SECRET=$OIDC_CLIENT_SECRET_PLAIN
OIDC_CONFIGURATION_URL=$_discovery_url
OIDC_PROVIDER_NAME=Authelia
ENV
if [[ "$_disable_local" =~ ^[Yy]$ ]]; then
echo "ALLOW_PASSWORD_LOGIN=false" >> "$DIR/.env"
fi
if [[ "$_auto_redirect" =~ ^[Yy]$ ]]; then
{
echo "OIDC_AUTO_REDIRECT=true"
echo "OIDC_REMEMBER_ME=true"
} >> "$DIR/.env"
fi
chown "$ACTUAL_USER:$ACTUAL_USER" "$DIR/.env" 2>/dev/null || true
# Mealie's OIDC redirect URI generation trusts X-Forwarded-* only from
@@ -312,13 +285,59 @@ ENV
sed -i "/container_name: ${CONTAINER}\$/a\\ entrypoint: [\"uvicorn\", \"mealie.app:app\", \"--host\", \"0.0.0.0\", \"--port\", \"9000\", \"--forwarded-allow-ips=*\"]" "$DIR/docker-compose.yml"
fi
local _success_msg="\"Sign in with Authelia\" added to Mealie — local login still works too."
[[ "$_disable_local" =~ ^[Yy]$ ]] && _success_msg="\"Sign in with Authelia\" added to Mealie — local username/password login is now disabled."
(cd "$DIR" && docker compose up -d) \
&& log_success "$_success_msg" \
&& log_success "\"Sign in with Authelia\" added to Mealie — local login still works too." \
|| log_warning "Restart failed — check: docker compose -f $DIR/docker-compose.yml logs"
declare -F _authelia_scope_access >/dev/null 2>&1 && _authelia_scope_access "mealie" "${BASE_URL#*://}"
echo ""
log_info "Test the \"Login with Authelia\" button on Mealie's own login page before"
log_info "disabling local login — re-run 'sudo ./setup.sh mealie' (choose update,"
log_info "then \"Reconfigure? n\") once you've confirmed it works, and you'll be"
log_info "offered that as a separate step."
}
# Split out from _mealie_offer_authelia_oidc so disabling local login is
# never offered in the same breath as first setting SSO up — confirmed
# live (on Beszel, same risk class) that saying yes before actually testing
# the Authelia button leaves both login paths broken at once. Only reached
# from a later "update" rerun once OIDC is already configured and the admin
# declines to reconfigure — i.e. after they've had a real chance to test it.
_mealie_offer_disable_password_login() {
local DIR="$1"
grep -q '^ALLOW_PASSWORD_LOGIN=false' "$DIR/.env" 2>/dev/null && return 0
echo ""
local _tested=""
prompt_yn " Have you ALREADY logged into Mealie successfully using the Authelia button (not just enabled it)? (y/n):" "n" _tested
if [[ ! "$_tested" =~ ^[Yy]$ ]]; then
log_info "Skipped. Test the Authelia login button first, then re-run 'sudo ./setup.sh mealie' (choose update) to come back to this."
return 0
fi
local _disable_local=""
prompt_yn " Also disable Mealie's own username/password login, so Authelia is the only way in? (y/n):" "n" _disable_local
[[ "$_disable_local" =~ ^[Yy]$ ]] || return 0
log_warning "Anyone without an Authelia account (only a local Mealie one) will no longer be able to log in."
log_info "Reversible any time: set ALLOW_PASSWORD_LOGIN back to true in $DIR/.env and 'docker compose up -d'."
local _auto_redirect=""
prompt_yn " Skip Mealie's login page entirely and jump straight to Authelia? (y/n):" "y" _auto_redirect
sed -i '/^ALLOW_PASSWORD_LOGIN=/d; /^OIDC_AUTO_REDIRECT=/d; /^OIDC_REMEMBER_ME=/d' "$DIR/.env"
{
echo "ALLOW_PASSWORD_LOGIN=false"
if [[ "$_auto_redirect" =~ ^[Yy]$ ]]; then
echo "OIDC_AUTO_REDIRECT=true"
echo "OIDC_REMEMBER_ME=true"
fi
} >> "$DIR/.env"
chown "$ACTUAL_USER:$ACTUAL_USER" "$DIR/.env" 2>/dev/null || true
(cd "$DIR" && docker compose up -d) \
&& log_success "Local username/password login is now disabled — Authelia is the only way in." \
|| log_warning "Restart failed — check: docker compose -f $DIR/docker-compose.yml logs"
}
install_mealie() {