Authentik setup and integration

This guide describes how to run Authentik as a container and integrate it with Rediflow for OIDC authentication.

Overview

  • Authentik runs as a container (profile authentik) and uses the same PostgreSQL instance as Rediflow, with a separate database per env (AUTHENTIK_POSTGRESQL__NAME: authentik_dev, authentik_test, authentik_qa, authentik_prod).
  • For QA or production with the registry image (registry.gitlab.com/rediflow_eu/rediflow:latest), use compose.deploy-qa-authentik.yml or compose.deploy-prod-authentik.yml. See Deploy QA and production for the full guide.
  • Rediflow acts as an OIDC client: when OIDC_ISSUER and OIDC_CLIENT_ID are set, the app requires login and redirects unauthenticated users to Authentik.
  • After login, Authentik redirects back with an auth code; the app exchanges it for a token, resolves or creates a User by OIDC sub, and sets g.current_user, g.authentik_groups.

1. Start Authentik with Podman Compose

1.1 Generate credentials

Add to .env.dev (or your env file):

# Generate AUTHENTIK_SECRET_KEY (required for authentik profile)
AUTHENTIK_SECRET_KEY=$(openssl rand -base64 60)

Or set manually:

echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 60)" >> .env.dev

1.2 Start services

podman compose --profile authentik --profile dev up -d

This starts:

  • db – PostgreSQL (shared; Authentik uses a separate DB per env)
  • db-init-authentik – one-off that creates the Authentik database (name from AUTHENTIK_POSTGRESQL__NAME)
  • redis – required by Authentik
  • authentik-server – Authentik UI and API (ports 9000, 9443)
  • authentik-worker – background tasks
  • app – Rediflow (if using dev profile)

1.3 Initial Authentik setup

  1. Open http://localhost:9000/if/flow/initial-setup/ (note the trailing slash).
  2. Set a password for the default akadmin user.
  3. Complete the setup wizard if prompted.

2. Create OIDC application in Authentik

2.1 Create an OAuth2/OIDC provider

  1. Go to ApplicationsProvidersCreate.
  2. Choose OAuth2/OIDC Provider.
  3. Configure:
    • Name: e.g. rediflow
    • Redirect URIs: http://localhost:5000/oauth/callback (or your app URL in prod)
    • Client type: Confidential
    • Signing Key: Create or select an RSA key
  4. Save.
  5. Under ProviderAdvanced protocol settings, ensure the Scopes include openid, profile, email, and groups (Authentik may expose groups via a custom scope or attribute).

2.2 Include groups in the token

Authentik sends group names via scope mappings. Configure a scope mapping for the groups scope that returns the user's groups (e.g. via user.ak_groups or user.all_groups()). See Provider property mappings.

Token format: Rediflow reads groups from the OIDC token as follows:

  • userinfo or id_token claims: groups or ak_groups
  • Values: list of strings (e.g. ["rediflow-admins", "rediflow-org-ACR"]) or list of dicts with a name key (e.g. [{"name": "rediflow-editors"}])

Ensure the provider's Scopes include groups so the mapping is applied.

2.3 Create an application

  1. Go to ApplicationsApplicationsCreate.
  2. Name: e.g. Rediflow
  3. Provider: Select the provider created above.
  4. Slug: e.g. rediflow (used in the issuer URL).
  5. Save.

2.4 Get client credentials

From the provider or application settings, copy:

  • Client ID
  • Client Secret

The issuer URL is typically:

http://localhost:9000/application/o/rediflow/

(Replace rediflow with your application slug.)

3. Configure Rediflow

Add to .env.dev:

OIDC_ISSUER=http://localhost:9000/application/o/rediflow/
OIDC_CLIENT_ID=your-client-id
OIDC_CLIENT_SECRET=your-client-secret
# Optional; default is {APP_URL}/oauth/callback
# OIDC_REDIRECT_URI=http://localhost:5000/oauth/callback

Restart the app:

podman compose --profile authentik --profile dev up -d --force-recreate app

4. Authentik groups for Rediflow

Create these groups in Authentik and assign users:

Group Purpose
rediflow-super-admins Solution admins: bypass locked state, edit past months
rediflow-admins Full visibility; manage orgs, role types, calendars
rediflow-editors Edit projects (when visibility allows)
rediflow-viewers View-only access
rediflow-visibility-org Enforce org-scoped visibility
rediflow-visibility-assigned Enforce assignment-only visibility
rediflow-org-<short_name> Org membership (e.g. rediflow-org-ACR)

For a fuller explanation of organisation visibility and when to use each mode, see Organisation visibility. See also Help: Authentik and project visibility for in-app details.

4.1 Rediflow branding

To apply Rediflow branding (title, theme, custom CSS) to the Authentik login screen:

Via script (recommended):

make setup-authentik-brand

Or with OIDC setup: uv run python scripts/setup_authentik_oidc.py --write-env --apply-brand

Optional env vars: REDIFLOW_LOGO_URL, REDIFLOW_FAVICON_URL (URLs for logo and favicon; e.g. https://your-app/static/img/logo.svg).

Via blueprint: Mount authentik-rediflow-brand/ into the Authentik container and run ak apply_blueprint /blueprints/rediflow-brand/blueprint.yaml. See authentik-rediflow-brand/README.md for details.

Manual: In Authentik Admin → SystemBrands → Edit default brand. Set title to "Rediflow", upload or link logo/favicon, and add custom CSS if desired.

5. Test environment and seeded users

For local testing with containerised Authentik and pre-seeded users:

  1. Copy .env.test.example to .env.test and adjust if needed.

  2. Create the test database: createdb -h localhost -p 15433 -U rediflow rediflow_test (or let compose create it).

  3. Run migrations: APP_ENV=test uv run python main.py migrate (with DATABASE_URL from .env.test).

  4. Start the stack:

    podman compose -f compose.yml -f compose.authentik-test.yml --profile authentik --profile dev up -d
    
  5. Open http://localhost:9001/if/flow/initial-setup/ and set the akadmin password (or use AUTHENTIK_BOOTSTRAP_PASSWORD from .env.test).

  6. Create the OIDC provider and application (automated or manual):

    • Automated: Run make setup-authentik-oidc to create the provider and application via API and update .env.test with credentials.
    • Manual: Create the OIDC provider and application in Authentik (see section 2). Use redirect URI http://localhost:5002/oauth/callback (app runs on port 5002 in test). Copy the client ID and secret into .env.test (OIDC_CLIENT_ID, OIDC_CLIENT_SECRET).
  7. Restart the app if needed.

  8. Seed users and pair them with Rediflow:

    uv run python scripts/seed_authentik_test_users.py
    uv run python scripts/seed_authentik_test_users.py --link-persons  # optional: link Person to User
    

    ODS-based seed (project + people from ODS, matches sane defaults):

    make build-authentik-test-ods   # creates docs/example_authentik_test_people.ods
    make seed-authentik-test-ods    # seeds from docs/example.ods + example_authentik_test_people.ods, links Person to User
    

    The people ODS has persons with codes admin@test, editor@test, viewer@test, orguser@test (matching Authentik usernames). Requires docs/example.ods for the project.

The seed script creates:

User Groups Rights
admin@test rediflow-admins Full access
editor@test rediflow-editors Edit projects
viewer@test rediflow-viewers View only
orguser@test rediflow-editors, rediflow-org-EXAMPLE Editor + org EXAMPLE

All seeded users have password test-password. Rediflow User records are created with external_id set to the Authentik user UUID (OIDC sub), so login automatically pairs them.

6. Without Authentik (no OIDC)

If OIDC_ISSUER and OIDC_CLIENT_ID are not set, the app behaves as before: no login redirect, g.current_user and g.authentik_groups stay empty. POLICY_NO_AUTH_DEFAULT controls behaviour (allow_all or view_only).

For developers: How login enforcement works, public paths, and how to add routes to the allowlist: see Authentik: Enforcing login (developer guide).

7. Row Level Security (RLS)

With Authentik, the app sets the PostgreSQL session variable app.current_user_id per request. RLS policies on user_settings and shared_views restrict access by user. When no user is logged in (or OIDC is disabled), the variable is empty and policies allow full access for backward compatibility.

RLS policies apply only when the app connects as a non-owner role. By default, the app uses the same role that runs migrations (table owner), so RLS is bypassed. To enable RLS, use a two-role setup: migrations run as owner; the app connects as a separate role with grants.

7.1 Enabling RLS in .env.test

  1. Copy .env.test.example to .env.test.
  2. Ensure POSTGRES_APP_USER, POSTGRES_APP_PASSWORD, and DATABASE_URL_APP are set (they are in the example).
  3. Set RLS_ENABLED=true.
  4. Start the stack and run migrations (as owner):
    make up-authentik-test
    set -a; . ./.env.test; set +a; uv run python main.py migrate head
    
  5. Create the app role and grant permissions:
    make setup-rls-app-role
    
    Or: uv run rediflow setup-rls-app-role (with .env.test loaded).
  6. Restart the app so it connects as rediflow_app:
    podman compose -f compose.yml -f compose.authentik-test.yml --profile authentik --profile dev up -d --force-recreate app
    

Migrations always use DATABASE_URL (owner). The app uses DATABASE_URL_APP when RLS_ENABLED=true. See docs/plans/2026-02-16_232813-rls-authentik.md for details.

8. Troubleshooting

  • Initial setup /if/flow/initial-setup/ shows "Not Found": The initial-setup flow only exists when Authentik has not been configured. If you see "Not Found", either:

    • Already set up: Try http://localhost:9001/ and log in with akadmin and your password.
    • Bootstrap failed: Default flows were not created. Restart Authentik and wait 2–3 minutes for it to finish booting. If it still fails, apply blueprints manually:
      podman exec rediflow-authentik-server ak apply_blueprint /blueprints/default/flow-default-authentication-flow.yaml
      podman exec rediflow-authentik-server ak apply_blueprint /blueprints/default/flow-default-invalidation-flow.yaml
      podman exec rediflow-authentik-server ak apply_blueprint /blueprints/default/flow-default-user-settings-flow.yaml
      podman exec rediflow-authentik-server ak apply_blueprint /blueprints/default/default-brand.yaml
      
      Then create the admin user if needed: podman exec -it rediflow-authentik-server ak shell and run from authentik.core.models import User; user = User.objects.create(username='akadmin', name='Admin', email='admin@localhost'); user.set_password('YourPassword'); user.is_active = True; user.save(), then podman exec rediflow-authentik-server ak create_admin_group akadmin.
  • Authentik cannot connect to PostgreSQL: Ensure the db service is healthy and reachable from the authentik-server container. Check AUTHENTIK_POSTGRESQL__HOST=db, AUTHENTIK_POSTGRESQL__NAME, and credentials.

  • Redirect URI mismatch: The redirect URI in Authentik must exactly match what the app uses (e.g. http://localhost:5000/oauth/callback). No trailing slash.

  • Groups not in token: Ensure Authentik is configured to include groups in the groups claim or userinfo. Check the provider’s scope mapping.

  • RLS not applying: RLS policies are bypassed by the table owner. Use a two-role setup (app role with grants, migrator role as owner) for RLS to take effect.