From 90a92fca5a4354049c6a0f22657d330aa1a6866a Mon Sep 17 00:00:00 2001 From: Pieter Date: Wed, 14 Jan 2026 16:17:44 +0100 Subject: [PATCH] feat: Add automated invitation stage configuration for Authentik MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements automatic invitation stage creation and enrollment flow binding: **Features:** - Creates invitation stage via YAML blueprint - Binds stage to enrollment flow (designation: enrollment) - Allows enrollment to proceed without invitation token - Fully automated via Ansible deployment **Implementation:** - New blueprint: ansible/roles/authentik/files/invitation-flow.yaml - New task file: ansible/roles/authentik/tasks/invitation.yml - Blueprint creates invitationstage model - Binds stage to enrollment flow at order=0 **Blueprint Configuration:** ```yaml model: authentik_stages_invitation.invitationstage name: default-enrollment-invitation continue_flow_without_invitation: true ``` **Testing:** ✅ Deployed to dev server successfully ✅ Invitation stage created and verified ✅ Stage bound to default-source-enrollment flow ✅ Verification: {"found": true, "count": 1} Resolves Authentik warning: "No invitation stage is bound to any flow" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../authentik/files/invitation-flow.yaml | 25 +++++ ansible/roles/authentik/tasks/invitation.yml | 94 +++++++++++++++++++ ansible/roles/authentik/tasks/main.yml | 5 + 3 files changed, 124 insertions(+) create mode 100644 ansible/roles/authentik/files/invitation-flow.yaml create mode 100644 ansible/roles/authentik/tasks/invitation.yml diff --git a/ansible/roles/authentik/files/invitation-flow.yaml b/ansible/roles/authentik/files/invitation-flow.yaml new file mode 100644 index 0000000..3a60de0 --- /dev/null +++ b/ansible/roles/authentik/files/invitation-flow.yaml @@ -0,0 +1,25 @@ +version: 1 +metadata: + name: invitation-flow-configuration + labels: + blueprints.goauthentik.io/description: "Configure invitation stage for enrollment" + blueprints.goauthentik.io/instantiate: "true" + +entries: + # 1. CREATE INVITATION STAGE + - model: authentik_stages_invitation.invitationstage + identifiers: + name: default-enrollment-invitation + id: invitation-stage + attrs: + continue_flow_without_invitation: true + + # 2. BIND INVITATION STAGE TO ENROLLMENT FLOW + - model: authentik_flows.flowstagebinding + identifiers: + target: !Find [authentik_flows.flow, [designation, enrollment]] + stage: !KeyOf invitation-stage + order: 0 + attrs: + evaluate_on_plan: true + re_evaluate_policies: false diff --git a/ansible/roles/authentik/tasks/invitation.yml b/ansible/roles/authentik/tasks/invitation.yml new file mode 100644 index 0000000..0975879 --- /dev/null +++ b/ansible/roles/authentik/tasks/invitation.yml @@ -0,0 +1,94 @@ +--- +# Configure invitation stage for enrollment flow + +- name: Use bootstrap token for API access + set_fact: + authentik_api_token: "{{ client_secrets.authentik_bootstrap_token }}" + +- name: Wait for Authentik API to be ready + uri: + url: "https://{{ authentik_domain }}/api/v3/root/config/" + method: GET + validate_certs: no + status_code: 200 + register: api_result + until: api_result.status == 200 + retries: 12 + delay: 5 + +- name: Create blueprints directory on server + file: + path: /opt/config/authentik/blueprints + state: directory + mode: '0755' + +- name: Copy invitation flow blueprint to server + copy: + src: invitation-flow.yaml + dest: /opt/config/authentik/blueprints/invitation-flow.yaml + mode: '0644' + register: invitation_blueprint_copied + +- name: Copy blueprint into authentik-worker container + shell: | + docker cp /opt/config/authentik/blueprints/invitation-flow.yaml authentik-worker:/blueprints/invitation-flow.yaml + +- name: Copy blueprint into authentik-server container + shell: | + docker cp /opt/config/authentik/blueprints/invitation-flow.yaml authentik-server:/blueprints/invitation-flow.yaml + +- name: Wait for blueprint to be discovered and applied + shell: | + echo "Waiting for invitation blueprint to be discovered and applied..." + sleep 10 + # Check if blueprint instance was created + i=1 + while [ $i -le 24 ]; do + result=$(docker exec authentik-server curl -sf -H 'Authorization: Bearer {{ authentik_api_token }}' \ + 'http://localhost:9000/api/v3/managed/blueprints/' 2>/dev/null || echo '') + if echo "$result" | grep -q 'invitation-flow-configuration'; then + echo "Blueprint instance found" + if echo "$result" | grep -A 10 'invitation-flow-configuration' | grep -q 'successful'; then + echo "Blueprint applied successfully" + exit 0 + fi + fi + sleep 5 + i=$((i+1)) + done + echo "Blueprint deployment in progress (may take 1-2 minutes)" + register: invitation_blueprint_result + changed_when: false + +- name: Verify invitation stage was created + shell: | + docker exec authentik-server curl -sf -H 'Authorization: Bearer {{ authentik_api_token }}' \ + 'http://localhost:9000/api/v3/stages/all/?name=default-enrollment-invitation' | \ + python3 -c "import sys, json; d = json.load(sys.stdin); print(json.dumps({'found': len(d.get('results', [])) > 0, 'count': len(d.get('results', []))}))" + register: invitation_stage_check + changed_when: false + failed_when: false + +- name: Display invitation stage configuration status + debug: + msg: | + ======================================== + Authentik Invitation Stage Configuration + ======================================== + + Configuration Method: YAML Blueprints + Blueprint File: /blueprints/invitation-flow.yaml + + ✓ Blueprint Deployed: {{ invitation_blueprint_copied.changed | default(false) }} + ✓ Blueprint Applied: {{ 'In Progress' if invitation_blueprint_result.rc != 0 else 'Complete' }} + + Verification: {{ invitation_stage_check.stdout | default('{}') }} + + Note: Authentik applies blueprints asynchronously. + Changes should be visible within 1-2 minutes. + + To verify manually: + - Login to https://{{ authentik_domain }} + - Check Admin > Flows > Stages for "default-enrollment-invitation" + - Check Admin > Flows > default-source-enrollment for invitation binding + ======================================== diff --git a/ansible/roles/authentik/tasks/main.yml b/ansible/roles/authentik/tasks/main.yml index b385cbc..7800355 100644 --- a/ansible/roles/authentik/tasks/main.yml +++ b/ansible/roles/authentik/tasks/main.yml @@ -26,3 +26,8 @@ include_tasks: mfa.yml when: authentik_bootstrap | default(true) tags: ['authentik', 'mfa', '2fa'] + +- name: Include invitation stage configuration + include_tasks: invitation.yml + when: authentik_bootstrap | default(true) + tags: ['authentik', 'invitation']