From ca85fbd7063f6e81f28ab518617d079689785fec Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 17 Aug 2026 05:06:59 +0000 Subject: [PATCH 1/2] Document ActualBudget OpenID user-invite step ActualBudget requires inviting additional OpenID users from its own "Server Online" screen before their login is accepted, separate from Authelia authenticating them successfully. Companion doc gets appended to the generated README automatically (write_readme convention). --- services/actualbudget.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 services/actualbudget.md diff --git a/services/actualbudget.md b/services/actualbudget.md new file mode 100644 index 0000000..081c704 --- /dev/null +++ b/services/actualbudget.md @@ -0,0 +1,20 @@ +## Inviting additional OpenID users + +Enabling OpenID lets any Authelia user on the instance *authenticate*, but +ActualBudget still gates actual access separately: only the account that +completes the very first OpenID login becomes the server owner. Every other +Authelia user has to be invited from inside ActualBudget before their login +is accepted — Authelia successfully authenticating them isn't enough on its +own, and the failure looks the same as a config problem if you don't know +this step exists. + +To invite one: +1. Log in as the owner account (the one that did the first OpenID login). +2. Click the **"Server Online"** indicator — the user-management screen only + opens from there, not from the general Settings page, which is easy to + miss. +3. Invite the new user by the **exact email address** set for them in + Authelia's `config/users.yml` — ActualBudget matches the OpenID identity + to an invited member by email, so any mismatch (typo, casing, different + domain) fails silently the same way as not being invited at all. +4. Have them log in via OpenID again. From 8203851049eba629c7062d9ccc1cfa87b662fc6e Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 17 Aug 2026 05:09:34 +0000 Subject: [PATCH 2/2] Make authelia new-user temp passwords longer and more complex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 30 characters, guaranteed at least 5 uppercase, 5 digits, and 5 special characters, shuffled. Scoped to add_authelia_user() only via a small local generator — deliberately not routed through lib/common.sh's shared generate_password, since that one is alphanumeric-only by design (its paired validate_password rejects special characters) and plenty of other services embed its output unescaped into .env/YAML/URLs. --- services/authelia.sh | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/services/authelia.sh b/services/authelia.sh index 1af2c9a..dd7674a 100644 --- a/services/authelia.sh +++ b/services/authelia.sh @@ -712,6 +712,45 @@ CADDY_AUTH_BLOCK2 echo "" } +# Picks "count" random characters from "charset" using an unbiased-enough +# per-byte modulo draw from /dev/urandom. Not part of lib/common.sh's shared +# generate_password (that one is deliberately alphanumeric-only — see its +# paired validate_password, which rejects special characters outright, since +# plenty of other services embed its output directly into .env/YAML/URLs +# without escaping). This one is scoped to add_authelia_user()'s temp +# password only, which is never written to disk in plaintext, so the wider +# character set is safe here without becoming a repo-wide convention change. +_authelia_rand_chars() { + local charset="$1" count="$2" out="" idx byte clen + clen=${#charset} + while [ "${#out}" -lt "$count" ]; do + byte=$(od -An -N1 -tu1 /dev/urandom | tr -d ' ') + idx=$(( byte % clen )) + out+="${charset:idx:1}" + done + printf '%s' "$out" +} + +# 30 chars, at least 5 each of uppercase/digit/special, rest a random mix — +# then shuffled so the guaranteed characters aren't clustered at the front. +_authelia_gen_temp_password() { + local length=30 min_upper=5 min_digit=5 min_special=5 + local upper_set="ABCDEFGHIJKLMNOPQRSTUVWXYZ" + local digit_set="0123456789" + local special_set='!@#%^&*()_+=-[]{}:,.?~' + local mixed_set="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789${special_set}" + + local part_upper part_digit part_special part_rest + part_upper="$(_authelia_rand_chars "$upper_set" "$min_upper")" + part_digit="$(_authelia_rand_chars "$digit_set" "$min_digit")" + part_special="$(_authelia_rand_chars "$special_set" "$min_special")" + local rest_len=$(( length - min_upper - min_digit - min_special )) + part_rest="$(_authelia_rand_chars "$mixed_set" "$rest_len")" + + printf '%s%s%s%s' "$part_upper" "$part_digit" "$part_special" "$part_rest" \ + | fold -w1 | shuf | tr -d '\n' +} + # Adds a new user to an EXISTING Authelia instance's users.yml — the scripted # version of the manual "generate a hash, paste a users.yml block, restart" # steps this file's own generated README already documents. Non-destructive: @@ -757,7 +796,7 @@ add_authelia_user() { log_info "Generating temporary password + hash..." local TEMP_PASS NEW_HASH - TEMP_PASS="$(generate_password 16)" + TEMP_PASS="$(_authelia_gen_temp_password)" NEW_HASH=$(docker run --rm authelia/authelia:4.39.20 \ authelia crypto hash generate argon2 --password "$TEMP_PASS" 2>/dev/null \ | grep -oP '(?<=Digest: ).*')