hsa-app/secret.md
Jean-Michel Tremblay 8b7252dad4 Initial commit: HSA receipt tracker
Go app for capturing and archiving HSA-eligible receipts: OIDC/PKCE auth
against Authelia, SQLite storage with dual-write (filesystem + DB blob),
mobile-first upload, and DB export.

Adds AI receipt classification: a config.json catalog of people and
categories (seeded into the DB on startup), a prompt builder that derives
name-order/initial variants from the data (with same-surname ambiguity
handling), and an Anthropic tool-use client behind POST /classify. Tests
run against a mock endpoint; a live integration test is env-gated to the
cheapest model.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:40:12 -04:00

5.5 KiB

Authelia OIDC Client Setup

One-time setup. Do this before running the app for the first time.


Step 1 — Generate the OIDC client secret

Run secret.sh on your Authelia host (needs openssl + authelia in PATH):

bash secret.sh

Output looks like:

=== Plaintext secret (app .env → OIDC_CLIENT_SECRET) ===
xK9mP2...64chars...

=== Authelia hash (configuration.yml → client_secret) ===
$argon2id$v=19$m=65536,t=3,p=4$...

Step 2 — Save the plaintext secret

The plaintext secret is only shown once. Save it immediately to the app's env file on the LXC where the app will run:

# /etc/hsa/hsa.env  (create this file, chmod 600, owned by the service user)
OIDC_CLIENT_SECRET=xK9mP2...64chars...

Full env file contents are documented in the app's README once we get there. For now just save OIDC_CLIENT_SECRET=<value> somewhere safe.

Never commit this file to git. .gitignore already excludes .env files.


Step 3 — Generate the OIDC signing key (RSA / RS256)

Authelia needs a signing key for the JWTs it issues. It must be RSA (RS256) — OIDC mandates RS256 as the baseline and Authelia rejects Ed25519/EdDSA for the OIDC JWKS.

Generate it and place it in the secrets dir, owned by the Authelia service user so the process can read it (it runs non-root; a root-owned chmod 600 key gives permission denied):

sudo mkdir -p /tmp/oidc-rsa
sudo authelia crypto pair rsa generate --bits 2048 --directory /tmp/oidc-rsa
sudo install -o authelia -g authelia -m 600 /tmp/oidc-rsa/private.pem /etc/authelia/secrets/oidc_jwks.pem
sudo rm -rf /tmp/oidc-rsa

(Confirm the owner matches your other secrets: ls -l /etc/authelia/secrets/. Substitute if it's not authelia:authelia.)


Step 4 — Add the jwks + client block to configuration.yml

The key is referenced from the .pem file via Authelia's template filter, so it never gets copied into the config. This requires enabling the template filter (see the note after the block — it's a one-line systemd env var).

In configuration.yml, the full identity_providers.oidc section should look like this (append the hsa-tracker entry if you already have other clients):

identity_providers:
  oidc:
    jwks:
      - key_id: 'main'
        algorithm: 'RS256'
        use: 'sig'
        key: {{ secret "/etc/authelia/secrets/oidc_jwks.pem" | mindent 10 "|" | msquote }}
    clients:
      - client_id: 'hsa-tracker'
        client_name: 'HSA Receipt Tracker'
        client_secret: '$argon2id$v=19$...'   # the hash from secret.sh
        public: false
        authorization_policy: 'two_factor'
        require_pkce: true
        pkce_challenge_method: 'S256'
        redirect_uris:
          - 'https://hsa.maisym.com/callback'
          - 'http://localhost:8080/callback'          # for local dev testing
        scopes:
          - 'openid'
          - 'profile'
          - 'email'
          - 'groups'
        response_types:
          - 'code'
        grant_types:
          - 'authorization_code'
        token_endpoint_auth_method: 'client_secret_basic'
        userinfo_signed_response_alg: 'none'

Required: enable the template config filter

The {{ secret ... }} syntax above only works when Authelia's template config filter is enabled — it's OFF by default. If you skip this, the {{ ... }} is passed to the YAML parser raw and breaks the entire config file (symptom: yaml: invalid map key plus a cascade of "X not configured" errors — those cascade errors are a red herring caused by the file failing to parse).

Enable the filter on the service:

sudo systemctl edit authelia

Add, save, exit:

[Service]
Environment=X_AUTHELIA_CONFIG_FILTERS=template

Confirm it registered before restarting:

systemctl cat authelia | grep -i filter   # should show your Environment line

Then restart (env changes need a full restart, not reload):

sudo systemctl restart authelia

Gotchas worth knowing

  • The key must be RSA and readable by the Authelia service user. Ed25519 is rejected by the OIDC JWKS (RS256 is mandatory). A root-owned chmod 600 key gives the process permission denied — keep it in /etc/authelia/secrets/ owned by authelia.
  • authelia config validate run manually shows false positives for jwt_secret and storage encryption_key, because the *_FILE secrets are injected by systemd and aren't present in a manual shell. Under the running service they're fine — only trust the journalctl output after a real restart for those two.

Step 5 — Create the hsa-users group and add both users

In your Authelia users file (typically users_database.yml), add hsa-users to the groups list for each user:

users:
  jm:
    disabled: false
    displayname: 'JM'
    password: '$argon2id$...'
    email: 'jm@jmopines.com'
    groups:
      - 'admins'
      - 'hsa-users'

Repeat for the second user.


Step 6 — Restart Authelia

Use restart, not reload — config-file and environment changes are only picked up on a full restart. (The users/groups file in Step 5 is auto-reloaded by Authelia, but a restart covers everything at once.)

sudo systemctl restart authelia

Verify it came up clean (no level=error or level=fatal lines):

journalctl -u authelia -n 20

Verification

Once the app is running, opening http://localhost:8080 (or https://hsa.maisym.com) should redirect you to auth.maisym.com. After login + YubiKey 2FA it should land on a page showing your identity and [hsa-users] group.