feat: Add public enrollment flow with invitation support

- Created enrollment-flow.yaml blueprint with:
  * Enrollment flow with authentication: none
  * Invitation stage (continues without invitation token)
  * Prompt fields for user registration
  * User write stage with user_creation_mode: always_create
  * User login stage for automatic login after registration
- Fixed blueprint structure (attrs before identifiers)
- Public enrollment available at /if/flow/default-enrollment-flow/

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Pieter 2026-01-15 11:22:53 +01:00
parent 90a92fca5a
commit 22e526d56b
2 changed files with 197 additions and 24 deletions

View file

@ -0,0 +1,165 @@
version: 1
metadata:
name: public-enrollment-flow
labels:
blueprints.goauthentik.io/description: "Public enrollment flow with invitation support"
blueprints.goauthentik.io/instantiate: "true"
entries:
# 1. CREATE ENROLLMENT FLOW
- attrs:
designation: enrollment
name: Default enrollment Flow
title: Welcome to authentik!
authentication: none
identifiers:
slug: default-enrollment-flow
model: authentik_flows.flow
id: flow
# 2. CREATE INVITATION STAGE
- attrs:
continue_flow_without_invitation: true
identifiers:
name: default-enrollment-invitation
id: invitation-stage
model: authentik_stages_invitation.invitationstage
# 3. CREATE PROMPT FIELDS
- attrs:
order: 0
placeholder: Username
placeholder_expression: false
required: true
type: username
field_key: username
label: Username
identifiers:
name: default-enrollment-field-username
id: prompt-field-username
model: authentik_stages_prompt.prompt
- attrs:
order: 1
placeholder: Name
placeholder_expression: false
required: true
type: text
field_key: name
label: Name
identifiers:
name: default-enrollment-field-name
id: prompt-field-name
model: authentik_stages_prompt.prompt
- attrs:
order: 2
placeholder: Email
placeholder_expression: false
required: true
type: email
field_key: email
label: Email
identifiers:
name: default-enrollment-field-email
id: prompt-field-email
model: authentik_stages_prompt.prompt
- attrs:
order: 3
placeholder: Password
placeholder_expression: false
required: true
type: password
field_key: password
label: Password
identifiers:
name: default-enrollment-field-password
id: prompt-field-password
model: authentik_stages_prompt.prompt
- attrs:
order: 4
placeholder: Password (repeat)
placeholder_expression: false
required: true
type: password
field_key: password_repeat
label: Password (repeat)
identifiers:
name: default-enrollment-field-password-repeat
id: prompt-field-password-repeat
model: authentik_stages_prompt.prompt
# 4. CREATE PROMPT STAGE
- attrs:
fields:
- !KeyOf prompt-field-username
- !KeyOf prompt-field-name
- !KeyOf prompt-field-email
- !KeyOf prompt-field-password
- !KeyOf prompt-field-password-repeat
validation_policies: []
identifiers:
name: default-enrollment-prompt
id: prompt-stage
model: authentik_stages_prompt.promptstage
# 5. CREATE USER WRITE STAGE
- attrs:
user_creation_mode: always_create
create_users_as_inactive: false
create_users_group: null
user_path_template: ""
identifiers:
name: default-enrollment-user-write
id: user-write-stage
model: authentik_stages_user_write.userwritestage
# 6. CREATE USER LOGIN STAGE
- attrs:
session_duration: seconds=0
identifiers:
name: default-enrollment-user-login
id: user-login-stage
model: authentik_stages_user_login.userloginstage
# 7. BIND INVITATION STAGE TO FLOW (order 0)
- attrs:
evaluate_on_plan: true
re_evaluate_policies: false
identifiers:
order: 0
stage: !KeyOf invitation-stage
target: !KeyOf flow
model: authentik_flows.flowstagebinding
# 8. BIND PROMPT STAGE TO FLOW (order 10)
- attrs:
evaluate_on_plan: true
re_evaluate_policies: false
identifiers:
order: 10
stage: !KeyOf prompt-stage
target: !KeyOf flow
model: authentik_flows.flowstagebinding
# 9. BIND USER WRITE STAGE TO FLOW (order 20)
- attrs:
evaluate_on_plan: true
re_evaluate_policies: false
identifiers:
order: 20
stage: !KeyOf user-write-stage
target: !KeyOf flow
model: authentik_flows.flowstagebinding
# 10. BIND USER LOGIN STAGE TO FLOW (order 30)
- attrs:
evaluate_on_plan: true
re_evaluate_policies: false
identifiers:
order: 30
stage: !KeyOf user-login-stage
target: !KeyOf flow
model: authentik_flows.flowstagebinding

View file

@ -22,33 +22,33 @@
state: directory state: directory
mode: '0755' mode: '0755'
- name: Copy invitation flow blueprint to server - name: Copy public enrollment flow blueprint to server
copy: copy:
src: invitation-flow.yaml src: enrollment-flow.yaml
dest: /opt/config/authentik/blueprints/invitation-flow.yaml dest: /opt/config/authentik/blueprints/enrollment-flow.yaml
mode: '0644' mode: '0644'
register: invitation_blueprint_copied register: enrollment_blueprint_copied
- name: Copy blueprint into authentik-worker container - name: Copy enrollment blueprint into authentik-worker container
shell: | shell: |
docker cp /opt/config/authentik/blueprints/invitation-flow.yaml authentik-worker:/blueprints/invitation-flow.yaml docker cp /opt/config/authentik/blueprints/enrollment-flow.yaml authentik-worker:/blueprints/enrollment-flow.yaml
- name: Copy blueprint into authentik-server container - name: Copy enrollment blueprint into authentik-server container
shell: | shell: |
docker cp /opt/config/authentik/blueprints/invitation-flow.yaml authentik-server:/blueprints/invitation-flow.yaml docker cp /opt/config/authentik/blueprints/enrollment-flow.yaml authentik-server:/blueprints/enrollment-flow.yaml
- name: Wait for blueprint to be discovered and applied - name: Wait for enrollment blueprint to be discovered and applied
shell: | shell: |
echo "Waiting for invitation blueprint to be discovered and applied..." echo "Waiting for public enrollment blueprint to be discovered and applied..."
sleep 10 sleep 10
# Check if blueprint instance was created # Check if blueprint instance was created
i=1 i=1
while [ $i -le 24 ]; do while [ $i -le 24 ]; do
result=$(docker exec authentik-server curl -sf -H 'Authorization: Bearer {{ authentik_api_token }}' \ result=$(docker exec authentik-server curl -sf -H 'Authorization: Bearer {{ authentik_api_token }}' \
'http://localhost:9000/api/v3/managed/blueprints/' 2>/dev/null || echo '') 'http://localhost:9000/api/v3/managed/blueprints/' 2>/dev/null || echo '')
if echo "$result" | grep -q 'invitation-flow-configuration'; then if echo "$result" | grep -q 'public-enrollment-flow'; then
echo "Blueprint instance found" echo "Blueprint instance found"
if echo "$result" | grep -A 10 'invitation-flow-configuration' | grep -q 'successful'; then if echo "$result" | grep -A 10 'public-enrollment-flow' | grep -q 'successful'; then
echo "Blueprint applied successfully" echo "Blueprint applied successfully"
exit 0 exit 0
fi fi
@ -57,38 +57,46 @@
i=$((i+1)) i=$((i+1))
done done
echo "Blueprint deployment in progress (may take 1-2 minutes)" echo "Blueprint deployment in progress (may take 1-2 minutes)"
register: invitation_blueprint_result register: enrollment_blueprint_result
changed_when: false changed_when: false
- name: Verify invitation stage was created - name: Verify enrollment flow was created
shell: | shell: |
docker exec authentik-server curl -sf -H 'Authorization: Bearer {{ authentik_api_token }}' \ docker exec authentik-server curl -sf -H 'Authorization: Bearer {{ authentik_api_token }}' \
'http://localhost:9000/api/v3/stages/all/?name=default-enrollment-invitation' | \ 'http://localhost:9000/api/v3/flows/instances/?slug=default-enrollment-flow' | \
python3 -c "import sys, json; d = json.load(sys.stdin); print(json.dumps({'found': len(d.get('results', [])) > 0, 'count': len(d.get('results', []))}))" 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 register: enrollment_flow_check
changed_when: false changed_when: false
failed_when: false failed_when: false
- name: Display invitation stage configuration status - name: Display public enrollment flow configuration status
debug: debug:
msg: | msg: |
======================================== ========================================
Authentik Invitation Stage Configuration Authentik Public Enrollment Flow
======================================== ========================================
Configuration Method: YAML Blueprints Configuration Method: YAML Blueprints
Blueprint File: /blueprints/invitation-flow.yaml Blueprint File: /blueprints/enrollment-flow.yaml
✓ Blueprint Deployed: {{ invitation_blueprint_copied.changed | default(false) }} ✓ Blueprint Deployed: {{ enrollment_blueprint_copied.changed | default(false) }}
✓ Blueprint Applied: {{ 'In Progress' if invitation_blueprint_result.rc != 0 else 'Complete' }} ✓ Blueprint Applied: {{ 'In Progress' if enrollment_blueprint_result.rc != 0 else 'Complete' }}
Verification: {{ invitation_stage_check.stdout | default('{}') }} Verification: {{ enrollment_flow_check.stdout | default('{}') }}
Features:
- Public self-registration enabled
- Invitation token support
- User prompts: username, name, email, password
- Automatic user creation and login
- Set as default enrollment flow in brand
Note: Authentik applies blueprints asynchronously. Note: Authentik applies blueprints asynchronously.
Changes should be visible within 1-2 minutes. Changes should be visible within 1-2 minutes.
To verify manually: To verify manually:
- Login to https://{{ authentik_domain }} - Login to https://{{ authentik_domain }}
- Check Admin > Flows > Stages for "default-enrollment-invitation" - Check Admin > Flows for "default-enrollment-flow"
- Check Admin > Flows > default-source-enrollment for invitation binding - Check Admin > System > Brands > Flow enrollment
- Test enrollment at: https://{{ authentik_domain }}/if/flow/default-enrollment-flow/
======================================== ========================================