Authentik self-registration enrollment flow for clients #12

Closed
opened 2026-07-07 21:56:58 -06:00 by jhodgkin · 1 comment
Owner

Goal

Allow photography clients to create their own accounts via Authentik, so every booking is tied to a verified identity. Extends #10 with a two-group role model.

Authentik setup (manual, one-time)

Groups

  • Create group lisilou-photographers — add the photographer account manually
  • Create group lisilou-clients — clients auto-join on registration

Enrollment flow

  • In Authentik: Flows > Create > Enrollment
  • Stages: Prompt (first name, last name, email, password) → Email verification → User write → Group membership binding (auto-assign lisilou-clients)
  • Set the enrollment flow on the lisilou-portfolio OIDC provider so the Register link on the login screen activates it
  • Email verification required before account is active (uses Authentik email stage, configured with homelab SMTP)

OIDC provider scope

  • Add a custom property mapping to include group membership in the token:
    Scope name: groups
    Expression: return [group.name for group in request.user.ak_groups.all()]
    
  • Add groups to the lisilou-portfolio provider scopes

API changes (extends #10)

Group claim parsing (api/auth.js)

  • After callback, extract groups array from ID token claims
  • Store in session: req.session.user = { name, email, groups }

Middleware

// Any logged-in user
const requireAuth = (req, res, next) => {
  if (!req.session.user) return res.status(401).json({ loginUrl: "/api/auth/login" });
  next();
};

// Photographer only
const requireAdmin = (req, res, next) => {
  if (!req.session.user?.groups?.includes("lisilou-photographers"))
    return res.status(403).json({ error: "Forbidden" });
  next();
};
  • /api/admin/* uses requireAdmin
  • /api/client/* uses requireAuth
  • Booking form: unauthenticated users are redirected to Authentik login (which shows Register link)

Acceptance

  • A new visitor can click Book a Session, get redirected to Authentik, register with email + password, verify email, and return to the booking form as a logged-in client
  • Newly registered clients are in lisilou-clients group and cannot access /dashboard
  • Photographer account (in lisilou-photographers) can access /dashboard
  • Token group claims flow through to the API session correctly
## Goal Allow photography clients to create their own accounts via Authentik, so every booking is tied to a verified identity. Extends #10 with a two-group role model. ## Authentik setup (manual, one-time) ### Groups - Create group `lisilou-photographers` — add the photographer account manually - Create group `lisilou-clients` — clients auto-join on registration ### Enrollment flow - In Authentik: Flows > Create > Enrollment - Stages: Prompt (first name, last name, email, password) → Email verification → User write → Group membership binding (auto-assign `lisilou-clients`) - Set the enrollment flow on the lisilou-portfolio OIDC provider so the Register link on the login screen activates it - Email verification required before account is active (uses Authentik email stage, configured with homelab SMTP) ### OIDC provider scope - Add a custom property mapping to include group membership in the token: ``` Scope name: groups Expression: return [group.name for group in request.user.ak_groups.all()] ``` - Add `groups` to the lisilou-portfolio provider scopes ## API changes (extends #10) ### Group claim parsing (`api/auth.js`) - After callback, extract `groups` array from ID token claims - Store in session: `req.session.user = { name, email, groups }` ### Middleware ```js // Any logged-in user const requireAuth = (req, res, next) => { if (!req.session.user) return res.status(401).json({ loginUrl: "/api/auth/login" }); next(); }; // Photographer only const requireAdmin = (req, res, next) => { if (!req.session.user?.groups?.includes("lisilou-photographers")) return res.status(403).json({ error: "Forbidden" }); next(); }; ``` - `/api/admin/*` uses `requireAdmin` - `/api/client/*` uses `requireAuth` - Booking form: unauthenticated users are redirected to Authentik login (which shows Register link) ## Acceptance - A new visitor can click Book a Session, get redirected to Authentik, register with email + password, verify email, and return to the booking form as a logged-in client - Newly registered clients are in `lisilou-clients` group and cannot access /dashboard - Photographer account (in `lisilou-photographers`) can access /dashboard - Token group claims flow through to the API session correctly
Author
Owner

Completed and verified end-to-end tonight (2026-07-20).

Ran scripts/authentik-setup.sh against auth.jerodrigged.com for real - it had never actually been executed before. Found and fixed two bugs in the script along the way (commits 2f5607d, 1e1eab3):

  1. `jget's stdin handling was broken (a here-string always overrode the piped input), causing curl to fail on every piped call.
  2. The prompt stage lives at /stages/prompt/stages/, not /stages/prompt/.

A third bug was more serious: the scope-mapping lookup filtered on managed__iexact, which this Authentik version silently ignores (returns the unfiltered list instead of erroring). That meant the OAuth2 provider only ever got the first scope mapping attached (ak_proxy, an unrelated proxy-outpost scope) instead of openid/profile/email - so every real login would have failed at the userinfo step with a 403 scope mismatch, regardless of anything else being correctly configured. Fixed by filtering on scope_name instead, and PATCHed the live provider directly so dev did not need to wait for a fresh run.

Verified for real (not just checking the redirect shape) via a disposable Authentik test user driven through the full authorization-code+PKCE flow: signup through the enrollment flow works, creates a real account, auto-logs in; that account then completes our OIDC callback and gets a correctly-scoped non-admin session (admin:false). Confirmed on both dev-lisilou.jerodrigged.com and lisilou.jerodrigged.com.

Completed and verified end-to-end tonight (2026-07-20). Ran `scripts/authentik-setup.sh` against auth.jerodrigged.com for real - it had never actually been executed before. Found and fixed two bugs in the script along the way (commits 2f5607d, 1e1eab3): 1. `jget's stdin handling was broken (a here-string always overrode the piped input), causing curl to fail on every piped call. 2. The prompt stage lives at `/stages/prompt/stages/`, not `/stages/prompt/`. A third bug was more serious: the scope-mapping lookup filtered on `managed__iexact`, which this Authentik version silently ignores (returns the unfiltered list instead of erroring). That meant the OAuth2 provider only ever got the *first* scope mapping attached (`ak_proxy`, an unrelated proxy-outpost scope) instead of openid/profile/email - so **every real login would have failed** at the userinfo step with a 403 scope mismatch, regardless of anything else being correctly configured. Fixed by filtering on `scope_name` instead, and PATCHed the live provider directly so dev did not need to wait for a fresh run. Verified for real (not just checking the redirect shape) via a disposable Authentik test user driven through the full authorization-code+PKCE flow: signup through the enrollment flow works, creates a real account, auto-logs in; that account then completes our OIDC callback and gets a correctly-scoped non-admin session (`admin:false`). Confirmed on both dev-lisilou.jerodrigged.com and lisilou.jerodrigged.com.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: jhodgkin/lisilou-portfolio#12