Files
dezky/infrastructure/production/fleet/authentik
Ronni Baslund 901cc69ba3
ci / changes (push) Successful in 4s
ci / tc_booking (push) Has been skipped
ci / tc_operator (push) Successful in 20s
ci / tc_website (push) Has been skipped
ci / tc_platform_api (push) Has been skipped
ci / test_platform_api (push) Has been skipped
ci / build_booking (push) Has been skipped
ci / tc_portal (push) Successful in 26s
ci / build_platform_api (push) Has been skipped
ci / build_operator (push) Successful in 31s
ci / build_portal (push) Successful in 39s
ci / deploy (push) Successful in 41s
fix(auth): silent session renewal + 401 auto-recovery
Idle sessions died and left a broken page: when the access token expired,
nuxt-oidc-auth's automatic refresh had no refresh token to use — neither
Authentik provider carried the offline_access scope mapping (and the
operator never requested the scope), so the module cleared the session
and every /api call 401'd until a manual F5 happened to re-auth through
Authentik's still-alive SSO session.

Fix 1: offline_access end to end — scope mapping attached to both live
providers (and blueprints, prod + dev), operator now requests the scope.
Sessions renew server-side for up to 30 days of activity (Redis store +
pinned token key from earlier make the refresh tokens durable).

Fix 2: client plugin in both apps — a 401 from /api sends the browser
through /auth/oidc/login instead of leaving dead buttons; invisible when
Authentik's session is alive, a clean sign-in screen when it isn't.
Loop-guarded. Full sign-out behavior unchanged.
2026-06-11 09:21:15 +02:00
..

fleet/authentik — identity provider (auth.dezky.eu)

Authentik, mirroring the dev docker-compose service but pointed at the in-cluster data tier. Deployed via the k3s Helm controller (helmchart.yaml, which mirrors values.yaml). Live at https://auth.dezky.eu (Let's Encrypt).

  • External Postgres (postgres.dezky-data, db/user authentik) + Redis (redis.dezky-data) — chart's bundled subcharts disabled.
  • Secrets via global.envFrom → the authentik-secret Secret (generated on-box; see secret.example.yaml). DB/Redis passwords match the dezky-data secrets.
  • Ingress: Traefik + cert-manager letsencrypt-prod.
  • error_reporting off, update-check off, bootstrap email admin@dezky.eu.

Deploy

# 1. secret (reads DB/Redis pw from dezky-data so they match; rest generated)
ADB=$(kubectl -n dezky-data get secret postgres-secret -o jsonpath='{.data.AUTHENTIK_DB_PASSWORD}' | base64 -d)
RDB=$(kubectl -n dezky-data get secret redis-secret    -o jsonpath='{.data.REDIS_PASSWORD}'        | base64 -d)
kubectl create namespace dezky-auth --dry-run=client -o yaml | kubectl apply -f -
kubectl -n dezky-auth create secret generic authentik-secret \
  --from-literal=AUTHENTIK_SECRET_KEY=$(openssl rand -hex 50) \
  --from-literal=AUTHENTIK_POSTGRESQL__PASSWORD="$ADB" \
  --from-literal=AUTHENTIK_REDIS__PASSWORD="$RDB" \
  --from-literal=AUTHENTIK_BOOTSTRAP_PASSWORD=$(openssl rand -hex 16) \
  --from-literal=AUTHENTIK_BOOTSTRAP_TOKEN=$(openssl rand -hex 32)
# 2. install
kubectl apply -f helmchart.yaml
kubectl -n dezky-auth rollout status deploy/authentik-server --timeout=300s

First login

# akadmin password (store in Bitwarden):
kubectl -n dezky-auth get secret authentik-secret -o jsonpath='{.data.AUTHENTIK_BOOTSTRAP_PASSWORD}' | base64 -d; echo

Log in at https://auth.dezky.eu as akadmin / that password.

Blueprints + branding (APPLIED)

blueprints/ holds prod blueprints (applied & successful on node1):

  • brand.yaml — dezky branding on the default brand (title + signal-green custom CSS). This is what puts the login page in dezky colors.
  • portal-application.yamldezky-portal OIDC app/provider (https://app.dezky.eu/api/auth/callback).
  • operator-application.yamldezky-operator OIDC app/provider (https://operator.dezky.eu/auth/oidc/callback) + dezky-platform-admins group + an access policy restricting operator login to that group.

Client secrets live in authentik-secret (PORTAL_OIDC_CLIENT_SECRET, OPERATOR_OIDC_CLIENT_SECRET) — the apps must reuse the SAME values.

Applying them (two gotchas, both handled)

  1. invalidation_flow is REQUIRED on OAuth2 providers in Authentik 2026.5 (dev's 2025.10 didn't need it) — both providers set it via !Find.
  2. ConfigMap mounts present files as symlinks, which Authentik's discovery won't read. So the worker uses an initContainer that copies the ConfigMap into an emptyDir as real files at /blueprints/custom:
    kubectl -n dezky-auth create configmap authentik-blueprints \
      --from-file=blueprints/ --dry-run=client -o yaml | kubectl apply -f -
    # patch worker: add bp-src(configMap) + bp-cust(emptyDir) + initContainer
    #   `cp -L /bp-src/*.yaml /bp-cust/`, mount bp-cust at /blueprints/custom
    kubectl -n dezky-auth rollout restart deploy/authentik-worker
    # apply each (or let discovery): ak apply_blueprint custom/<file>.yaml
    

    The chart's worker.volumes value did NOT take effect on this chart version, hence the direct Deployment patch. Caveat: a helm upgrade of Authentik reverts the patch — re-apply it (move it into a custom image or a post-render kustomize patch to make it durable). TODO.

Still deferred

  • Pin the chart version (currently latest → app 2026.5.2).
  • Durability: the server-rebrand Deployment patch + the brand image-field PATCH revert on a helm upgrade of Authentik — re-run them, or bake a custom image / post-render kustomize patch.

Brand custom CSS only reaches shadow DOM via CSS vars (so colors work), not the logo/favicon (deeper shadow root) or the "Powered by authentik" footer (light DOM). Those use dev's mechanism — real files + a bundle sed:

  • web-assets/dezky-logo.svg, dezky-favicon.svg, dezky-bg.svg (carbon).
  • ConfigMap authentik-web-assets is built from web-assets/.
  • server-rebrand.py patches the authentik-server Deployment: an initContainer copies /web/dist into an emptyDir, drops the 3 svgs into /web/dist/assets/icons/, and seds Powered by authentik -> Powered by Dezky. The server then serves the patched bundle.
  • The brand's branding_logo / branding_favicon / branding_default_flow_background point at those served svgs (carried in brand.yaml; if the blueprint leaves them default, PATCH the brand via API).

Apply:

kubectl -n dezky-auth create configmap authentik-web-assets --from-file=web-assets/ --dry-run=client -o yaml | kubectl apply -f -
kubectl -n dezky-auth get deploy authentik-server -o json | python3 server-rebrand.py | kubectl apply -f -

CAVEAT: the server patch + brand PATCH revert on a helm upgrade of Authentik — re-run them (or bake a custom image) for durability.