Authentik: Enforcing login (developer guide)

This guide explains how login enforcement works when using Authentik (OIDC) and how to extend or customize it as a developer.

When is login enforced?

Login is enforced automatically when OIDC is enabled. Set these environment variables:

OIDC_ISSUER=http://localhost:9000/application/o/rediflow/
OIDC_CLIENT_ID=your-client-id
OIDC_CLIENT_SECRET=your-client-secret

When OIDC_ISSUER and OIDC_CLIENT_ID are set, the app:

  1. Registers a before_request hook (before_request_oidc in app/auth/oidc.py)
  2. For every request to a non-public path: if the user is not logged in, redirects to /login
  3. /login redirects to Authentik; after successful auth, the callback sets session["oidc_user_id"] and redirects back to the original URL

No extra code is needed to enforce login on new routes. All routes are protected by default; only paths in the public allowlist bypass auth.

Request flow

User requests /project/xxx/capacity
    ↓
before_request_oidc() runs
    ↓
Is path public? (OIDC_PUBLIC_PATHS or OIDC_PUBLIC_PREFIXES)
    ├─ Yes → allow request
    └─ No → Is g.current_user_id set?
              ├─ Yes → allow request
              └─ No → redirect to /login?next=/project/xxx/capacity
                          ↓
                      /login redirects to Authentik
                          ↓
                      User logs in at Authentik
                          ↓
                      /oauth/callback receives code, exchanges for token,
                      creates/loads User, sets session, redirects to next

Public paths (no login required)

Paths that bypass auth are defined in app/auth/oidc.py:

Constant Purpose
OIDC_PUBLIC_PATHS Exact path matches: /login, /oauth/callback, /set-locale, /logout, /static/
OIDC_PUBLIC_PREFIXES Path prefixes: /static/, /s/ (share links)

Adding a new public path

To allow unauthenticated access to a route:

  1. Exact path: Add the path to OIDC_PUBLIC_PATHS in app/auth/oidc.py:

    OIDC_PUBLIC_PATHS = frozenset({
        "/login",
        "/oauth/callback",
        "/set-locale",
        "/logout",
        "/static/",
        "/your-public-path",  # add here
    })
    
  2. Path prefix: Add a prefix to OIDC_PUBLIC_PREFIXES:

    OIDC_PUBLIC_PREFIXES = ("/static/", "/s/", "/public-api/")
    

Security: Only add paths that are intentionally public (e.g. health checks, public landing pages, share links). Do not expose sensitive data.

After login: policy and authorization

Login only proves identity. Authorization (what a user can see or edit) is handled by the Policy module (app/services/policy.py). Routes check policy before rendering or mutating data:

from app.services.policy import Policy

@bp.route("/project/<uuid:project_uuid>/capacity")
def capacity_index(project_uuid):
    project = Project.query.get_or_404(project_uuid)
    policy = Policy.from_request(request)
    if not policy.can_view_project(project):
        abort(403)
    policy.ensure_project_visible(project, request)
    # ... render

Common policy methods:

Method Purpose
can_view_system() Access to home, settings, help
can_view_project(project) View project data
can_edit_project(project) Edit project data
can_manage_calendars() Edit org calendars
projects_visible_filter(request) Filter projects by visibility (org/assigned)

Policy uses g.current_user and g.authentik_groups (set by the auth layer). Group names like rediflow-admins, rediflow-editors, rediflow-viewers determine permissions.

Without OIDC (no Authentik)

When OIDC_ISSUER and OIDC_CLIENT_ID are not set:

  • No login redirect; g.current_user and g.authentik_groups stay empty
  • POLICY_NO_AUTH_DEFAULT controls behaviour: allow_all (default) or view_only
  • Useful for local development without Authentik

Configuration checklist (enforce login)

Step Variable / action
1 Set OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET
2 Ensure redirect URI in Authentik matches app (e.g. http://localhost:5000/oauth/callback)
3 Restart the app

That is all. Login is enforced for all non-public paths.

Code references

File Role
app/auth/oidc.py OIDC client, login/callback routes, before_request_oidc, public path allowlist
app/__init__.py Registers before_request_oidc when OIDC_ENABLED
app/services/policy.py Authorization (can_view, can_edit, visibility filters)
config/base.py oidc_enabled, oidc_issuer, oidc_client_id, oidc_client_secret

See also: Authentik setup for deployment and OIDC provider configuration.