diff --git a/ansible/playbooks/update-recovery-flow.yml b/ansible/playbooks/update-recovery-flow.yml new file mode 100644 index 0000000..9575387 --- /dev/null +++ b/ansible/playbooks/update-recovery-flow.yml @@ -0,0 +1,63 @@ +--- +# Deploy password recovery flow with email notifications +- name: Deploy password recovery flow + hosts: all + gather_facts: no + become: yes + + vars: + authentik_api_token: "ak_DtA2LG1Z9shl-tw9r0cs34B1G9l8Lpz76GxLf-4OBiUWbiHbAVJ04GYLcZ30" + client_domain: "dev.vrije.cloud" + + tasks: + - name: Create blueprints directory + file: + path: /opt/config/authentik/blueprints + state: directory + mode: '0755' + + - name: Copy recovery flow blueprint + copy: + src: ../roles/authentik/files/recovery-flow.yaml + dest: /opt/config/authentik/blueprints/recovery-flow.yaml + mode: '0644' + register: blueprint_copied + + - name: Copy blueprint into authentik-worker container + shell: | + docker cp /opt/config/authentik/blueprints/recovery-flow.yaml authentik-worker:/blueprints/recovery-flow.yaml + when: blueprint_copied.changed + + - name: Copy blueprint into authentik-server container + shell: | + docker cp /opt/config/authentik/blueprints/recovery-flow.yaml authentik-server:/blueprints/recovery-flow.yaml + when: blueprint_copied.changed + + - name: Restart authentik-worker to force blueprint discovery + shell: docker restart authentik-worker + when: blueprint_copied.changed + + - name: Wait for blueprint to be applied + shell: | + sleep 30 + docker exec authentik-server curl -sf -H 'Authorization: Bearer {{ authentik_api_token }}' \ + 'http://localhost:9000/api/v3/flows/instances/?slug=default-recovery-flow' + register: flow_check + retries: 6 + delay: 10 + until: flow_check.rc == 0 + no_log: true + + - name: Display success message + debug: + msg: | + ✓ Password recovery flow deployed successfully! + + Users can now reset their passwords by: + 1. Going to https://auth.{{ client_domain }}/if/flow/default-recovery-flow/ + 2. Entering their email address + 3. Receiving a recovery link via email + 4. Clicking the link and setting a new password + + The recovery link expires in 30 minutes. + Emails are sent via Mailgun SMTP (noreply@mg.vrije.cloud) diff --git a/ansible/roles/authentik/files/recovery-flow.yaml b/ansible/roles/authentik/files/recovery-flow.yaml new file mode 100644 index 0000000..7cb3e0a --- /dev/null +++ b/ansible/roles/authentik/files/recovery-flow.yaml @@ -0,0 +1,166 @@ +version: 1 +metadata: + name: password-recovery-flow + labels: + blueprints.goauthentik.io/description: "Password recovery flow with email link" + blueprints.goauthentik.io/instantiate: "true" + +entries: + # 1. CREATE RECOVERY FLOW + - attrs: + designation: recovery + name: Password Recovery + title: Reset your password + authentication: none + denied_action: message_continue + layout: stacked + identifiers: + slug: default-recovery-flow + model: authentik_flows.flow + id: recovery-flow + + # 2. CREATE IDENTIFICATION STAGE + # Asks user for their email address + - attrs: + user_fields: + - email + case_insensitive_matching: true + show_matched_user: false + password_stage: null + enrollment_flow: null + recovery_flow: !KeyOf recovery-flow + identifiers: + name: default-recovery-identification + id: identification-stage + model: authentik_stages_identification.identificationstage + + # 3. CREATE EMAIL STAGE + # Sends recovery link via email + - attrs: + use_global_settings: true + host: smtp.mailgun.org + port: 587 + username: "" + password: "" + use_tls: true + use_ssl: false + timeout: 30 + from_address: "noreply@mg.vrije.cloud" + token_expiry: 30 + subject: "Password Reset Request" + template: | + Hello, + + You have requested to reset your password. Click the link below to continue: + + {{ recovery_link }} + + This link will expire in 30 minutes. + + If you did not request this password reset, please ignore this email. + + Best regards, + The Team + activate_user_on_success: true + identifiers: + name: default-recovery-email + id: email-stage + model: authentik_stages_email.emailstage + + # 4. CREATE PROMPT STAGE FOR NEW PASSWORD + # Collects new password from user + - attrs: + order: 0 + placeholder: "New Password" + placeholder_expression: false + required: true + type: password + field_key: password + label: "New Password" + identifiers: + name: default-recovery-field-password + id: prompt-field-password + model: authentik_stages_prompt.prompt + + - attrs: + order: 1 + placeholder: "Confirm New Password" + placeholder_expression: false + required: true + type: password + field_key: password_repeat + label: "Confirm New Password" + identifiers: + name: default-recovery-field-password-repeat + id: prompt-field-password-repeat + model: authentik_stages_prompt.prompt + + - attrs: + fields: + - !KeyOf prompt-field-password + - !KeyOf prompt-field-password-repeat + validation_policies: [] + identifiers: + name: default-recovery-prompt + id: prompt-stage + model: authentik_stages_prompt.promptstage + + # 5. CREATE USER WRITE STAGE + # Updates user's password + - attrs: + user_creation_mode: never_create + create_users_as_inactive: false + create_users_group: null + user_path_template: "" + identifiers: + name: default-recovery-user-write + id: user-write-stage + model: authentik_stages_user_write.userwritestage + + # 6. BIND IDENTIFICATION STAGE TO FLOW (order 10) + - attrs: + evaluate_on_plan: true + re_evaluate_policies: false + invalid_response_action: retry + identifiers: + order: 10 + stage: !KeyOf identification-stage + target: !KeyOf recovery-flow + model: authentik_flows.flowstagebinding + + # 7. BIND EMAIL STAGE TO FLOW (order 20) + - attrs: + evaluate_on_plan: true + re_evaluate_policies: false + identifiers: + order: 20 + stage: !KeyOf email-stage + target: !KeyOf recovery-flow + model: authentik_flows.flowstagebinding + + # 8. BIND PROMPT STAGE TO FLOW (order 30) + - attrs: + evaluate_on_plan: true + re_evaluate_policies: false + identifiers: + order: 30 + stage: !KeyOf prompt-stage + target: !KeyOf recovery-flow + model: authentik_flows.flowstagebinding + + # 9. BIND USER WRITE STAGE TO FLOW (order 40) + - attrs: + evaluate_on_plan: true + re_evaluate_policies: false + identifiers: + order: 40 + stage: !KeyOf user-write-stage + target: !KeyOf recovery-flow + model: authentik_flows.flowstagebinding + + # 10. SET AS DEFAULT RECOVERY FLOW IN BRAND + - attrs: + flow_recovery: !KeyOf recovery-flow + identifiers: + domain: authentik-default + model: authentik_tenants.tenant