Enhance Authentik configuration by introducing dedicated authentication flows for public and lab brands, including stricter password policies and MFA requirements. Update README to clarify flow distinctions and invitation enrollment processes. Improve validation in Ansible tasks to ensure all necessary blueprint variables are set, enhancing deployment robustness.
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
# Noble — authentication flow for the **lab** hostname Brand: only members of operator groups may continue.
|
||||
# Reuses default identification / password / MFA / login stages; adds a policy on the password stage binding.
|
||||
# Noble — **lab** hostname authentication: operator-only, stricter password checks, MFA required (no WebAuthn-PWL skip).
|
||||
version: 1
|
||||
metadata:
|
||||
name: noble-lab-operator-authentication
|
||||
@@ -11,6 +10,28 @@ entries:
|
||||
identifiers:
|
||||
name: Default - Password change flow
|
||||
required: false
|
||||
- model: authentik_stages_password.passwordstage
|
||||
id: noble-lab-password-stage
|
||||
identifiers:
|
||||
name: noble-lab-authentication-password
|
||||
attrs:
|
||||
backends:
|
||||
- authentik.core.auth.InbuiltBackend
|
||||
- authentik.sources.kerberos.auth.KerberosBackend
|
||||
- authentik.sources.ldap.auth.LDAPBackend
|
||||
- authentik.core.auth.TokenBackend
|
||||
configure_flow: !Find [authentik_flows.flow, [slug, default-password-change]]
|
||||
failed_attempts_before_cancel: {{ noble_authentik_blueprint_lab_password_failed_attempts | int }}
|
||||
- model: authentik_stages_authenticator_validate.authenticatorvalidatestage
|
||||
id: noble-lab-authenticator-validate-strict
|
||||
identifiers:
|
||||
name: noble-lab-authenticator-validate-strict
|
||||
attrs:
|
||||
not_configured_action: {{ noble_authentik_blueprint_lab_mfa_not_configured_action | trim | lower | to_json }}
|
||||
{% if noble_authentik_blueprint_lab_mfa_not_configured_action | trim | lower == 'configure' %}
|
||||
configuration_stages:
|
||||
- !Find [authentik_stages_authenticator_totp.authenticatortotpstage, [name, default-authenticator-totp-setup]]
|
||||
{% endif %}
|
||||
- model: authentik_flows.flow
|
||||
id: flow
|
||||
identifiers:
|
||||
@@ -30,7 +51,7 @@ entries:
|
||||
model: authentik_flows.flowstagebinding
|
||||
identifiers:
|
||||
order: 20
|
||||
stage: !Find [authentik_stages_password.passwordstage, [name, default-authentication-password]]
|
||||
stage: !KeyOf noble-lab-password-stage
|
||||
target: !KeyOf flow
|
||||
attrs:
|
||||
re_evaluate_policies: true
|
||||
@@ -38,7 +59,7 @@ entries:
|
||||
model: authentik_flows.flowstagebinding
|
||||
identifiers:
|
||||
order: 30
|
||||
stage: !Find [authentik_stages_authenticator_validate.authenticatorvalidatestage, [name, default-authentication-mfa-validation]]
|
||||
stage: !KeyOf noble-lab-authenticator-validate-strict
|
||||
target: !KeyOf flow
|
||||
- model: authentik_flows.flowstagebinding
|
||||
identifiers:
|
||||
@@ -56,15 +77,43 @@ entries:
|
||||
return True
|
||||
return not hasattr(flow_plan.context.get("pending_user"), "backend")
|
||||
- model: authentik_policies_expression.expressionpolicy
|
||||
id: noble-lab-authenticator-validate-optional
|
||||
id: noble-lab-password-strength
|
||||
identifiers:
|
||||
name: noble-lab-authenticator-validate-optional
|
||||
name: noble-lab-password-strength
|
||||
attrs:
|
||||
expression: |
|
||||
flow_plan = request.context.get("flow_plan")
|
||||
if not flow_plan:
|
||||
import re
|
||||
_zxc = {{ noble_authentik_blueprint_lab_password_policy_check_zxcvbn | bool }}
|
||||
_zxc_thr = {{ noble_authentik_blueprint_lab_password_policy_zxcvbn_score_threshold | int }}
|
||||
pwd = request.context.get("password")
|
||||
if not pwd:
|
||||
return True
|
||||
return not (flow_plan.context.get("auth_method") == "auth_webauthn_pwl")
|
||||
msg = {{ noble_authentik_blueprint_lab_password_policy_error_message | trim | to_json }}
|
||||
if len(pwd) < {{ noble_authentik_blueprint_lab_password_policy_length_min | int }}:
|
||||
ak_message(msg)
|
||||
return False
|
||||
if len(re.findall(r"[A-Z]", pwd)) < {{ noble_authentik_blueprint_lab_password_policy_amount_uppercase | int }}:
|
||||
ak_message(msg)
|
||||
return False
|
||||
if len(re.findall(r"[a-z]", pwd)) < {{ noble_authentik_blueprint_lab_password_policy_amount_lowercase | int }}:
|
||||
ak_message(msg)
|
||||
return False
|
||||
if len(re.findall(r"[0-9]", pwd)) < {{ noble_authentik_blueprint_lab_password_policy_amount_digits | int }}:
|
||||
ak_message(msg)
|
||||
return False
|
||||
sym = sum(1 for c in pwd if (not c.isalnum()) and (not c.isspace()))
|
||||
if sym < {{ noble_authentik_blueprint_lab_password_policy_amount_symbols | int }}:
|
||||
ak_message(msg)
|
||||
return False
|
||||
if _zxc:
|
||||
try:
|
||||
from zxcvbn import zxcvbn
|
||||
if zxcvbn(pwd[:72])["score"] <= _zxc_thr:
|
||||
ak_message("Password is too weak for the lab policy.")
|
||||
return False
|
||||
except Exception:
|
||||
pass
|
||||
return True
|
||||
- model: authentik_policies_expression.expressionpolicy
|
||||
id: noble-lab-operators-only
|
||||
identifiers:
|
||||
@@ -94,8 +143,6 @@ entries:
|
||||
failure_result: true
|
||||
- model: authentik_policies.policybinding
|
||||
identifiers:
|
||||
order: 10
|
||||
target: !KeyOf noble-lab-authenticator-binding
|
||||
policy: !KeyOf noble-lab-authenticator-validate-optional
|
||||
attrs:
|
||||
failure_result: true
|
||||
order: 15
|
||||
target: !KeyOf noble-lab-password-binding
|
||||
policy: !KeyOf noble-lab-password-strength
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
# Noble — **public** hostname(s): same behaviour as stock **default-authentication-flow** (optional password / MFA skip for WebAuthn PWL), isolated slug for Brand binding.
|
||||
version: 1
|
||||
metadata:
|
||||
name: noble-public-authentication-flow
|
||||
labels:
|
||||
blueprints.goauthentik.io/instantiate: "true"
|
||||
entries:
|
||||
- model: authentik_blueprints.metaapplyblueprint
|
||||
attrs:
|
||||
identifiers:
|
||||
name: Default - Password change flow
|
||||
required: false
|
||||
- model: authentik_flows.flow
|
||||
id: flow
|
||||
identifiers:
|
||||
slug: {{ noble_authentik_blueprint_public_auth_flow_slug | trim | to_json }}
|
||||
attrs:
|
||||
name: Noble public sign-in
|
||||
title: Sign in
|
||||
designation: authentication
|
||||
authentication: none
|
||||
- id: noble-public-identification-binding
|
||||
model: authentik_flows.flowstagebinding
|
||||
identifiers:
|
||||
order: 10
|
||||
stage: !Find [authentik_stages_identification.identificationstage, [name, default-authentication-identification]]
|
||||
target: !KeyOf flow
|
||||
- id: noble-public-password-binding
|
||||
model: authentik_flows.flowstagebinding
|
||||
identifiers:
|
||||
order: 20
|
||||
stage: !Find [authentik_stages_password.passwordstage, [name, default-authentication-password]]
|
||||
target: !KeyOf flow
|
||||
attrs:
|
||||
re_evaluate_policies: true
|
||||
- id: noble-public-authenticator-binding
|
||||
model: authentik_flows.flowstagebinding
|
||||
identifiers:
|
||||
order: 30
|
||||
stage: !Find [authentik_stages_authenticator_validate.authenticatorvalidatestage, [name, default-authentication-mfa-validation]]
|
||||
target: !KeyOf flow
|
||||
- model: authentik_flows.flowstagebinding
|
||||
identifiers:
|
||||
order: 100
|
||||
stage: !Find [authentik_stages_user_login.userloginstage, [name, default-authentication-login]]
|
||||
target: !KeyOf flow
|
||||
- model: authentik_policies_expression.expressionpolicy
|
||||
id: noble-public-password-optional
|
||||
identifiers:
|
||||
name: noble-public-password-optional
|
||||
attrs:
|
||||
expression: |
|
||||
flow_plan = request.context.get("flow_plan")
|
||||
if not flow_plan:
|
||||
return True
|
||||
return not hasattr(flow_plan.context.get("pending_user"), "backend")
|
||||
- model: authentik_policies_expression.expressionpolicy
|
||||
id: noble-public-authenticator-validate-optional
|
||||
identifiers:
|
||||
name: noble-public-authenticator-validate-optional
|
||||
attrs:
|
||||
expression: |
|
||||
flow_plan = request.context.get("flow_plan")
|
||||
if not flow_plan:
|
||||
return True
|
||||
return not (flow_plan.context.get("auth_method") == "auth_webauthn_pwl")
|
||||
- model: authentik_policies.policybinding
|
||||
identifiers:
|
||||
order: 10
|
||||
target: !KeyOf noble-public-password-binding
|
||||
policy: !KeyOf noble-public-password-optional
|
||||
attrs:
|
||||
failure_result: true
|
||||
- model: authentik_policies.policybinding
|
||||
identifiers:
|
||||
order: 10
|
||||
target: !KeyOf noble-public-authenticator-binding
|
||||
policy: !KeyOf noble-public-authenticator-validate-optional
|
||||
attrs:
|
||||
failure_result: true
|
||||
@@ -0,0 +1,227 @@
|
||||
# Noble — two **enrollment** flows (public vs lab) with separate **Invitation** stages (invitation token required).
|
||||
# Create rows under **Directory → Invitations** in the admin UI and pick the matching flow; share links with the
|
||||
# correct **Host** so the right Brand applies. Does **not** ship example **Invitation** objects (no prefilled emails).
|
||||
version: 1
|
||||
metadata:
|
||||
name: noble-invitation-enrollment-flows
|
||||
labels:
|
||||
blueprints.goauthentik.io/instantiate: "true"
|
||||
entries:
|
||||
- model: authentik_core.group
|
||||
id: noble-lab-invited-group
|
||||
identifiers:
|
||||
name: {{ noble_authentik_blueprint_lab_invitee_group_name | trim | to_json }}
|
||||
attrs:
|
||||
is_superuser: false
|
||||
attributes:
|
||||
"noble.ak/audience": lab
|
||||
"noble.ak/role": lab-invited
|
||||
|
||||
- model: authentik_flows.flow
|
||||
id: noble-inv-flow-public
|
||||
identifiers:
|
||||
slug: {{ noble_authentik_blueprint_public_invitation_enrollment_flow_slug | trim | to_json }}
|
||||
attrs:
|
||||
name: {{ noble_authentik_blueprint_public_invitation_flow_name | trim | to_json }}
|
||||
title: {{ noble_authentik_blueprint_public_invitation_flow_title | trim | to_json }}
|
||||
designation: enrollment
|
||||
authentication: require_unauthenticated
|
||||
|
||||
- model: authentik_flows.flow
|
||||
id: noble-inv-flow-lab
|
||||
identifiers:
|
||||
slug: {{ noble_authentik_blueprint_lab_invitation_enrollment_flow_slug | trim | to_json }}
|
||||
attrs:
|
||||
name: {{ noble_authentik_blueprint_lab_invitation_flow_name | trim | to_json }}
|
||||
title: {{ noble_authentik_blueprint_lab_invitation_flow_title | trim | to_json }}
|
||||
designation: enrollment
|
||||
authentication: require_unauthenticated
|
||||
|
||||
- model: authentik_stages_invitation.invitationstage
|
||||
id: noble-inv-stage-public
|
||||
identifiers:
|
||||
name: noble-invitation-enrollment-invitation-public
|
||||
attrs:
|
||||
continue_flow_without_invitation: false
|
||||
|
||||
- model: authentik_stages_invitation.invitationstage
|
||||
id: noble-inv-stage-lab
|
||||
identifiers:
|
||||
name: noble-invitation-enrollment-invitation-lab
|
||||
attrs:
|
||||
continue_flow_without_invitation: false
|
||||
|
||||
- id: noble-inv-prompt-field-username
|
||||
model: authentik_stages_prompt.prompt
|
||||
identifiers:
|
||||
name: noble-inv-enroll-field-username
|
||||
attrs:
|
||||
field_key: username
|
||||
label: Username
|
||||
type: username
|
||||
required: true
|
||||
placeholder: Username
|
||||
placeholder_expression: false
|
||||
order: 0
|
||||
|
||||
- id: noble-inv-prompt-field-password
|
||||
model: authentik_stages_prompt.prompt
|
||||
identifiers:
|
||||
name: noble-inv-enroll-field-password
|
||||
attrs:
|
||||
field_key: password
|
||||
label: Password
|
||||
type: password
|
||||
required: true
|
||||
placeholder: Password
|
||||
placeholder_expression: false
|
||||
order: 1
|
||||
|
||||
- id: noble-inv-prompt-field-password-repeat
|
||||
model: authentik_stages_prompt.prompt
|
||||
identifiers:
|
||||
name: noble-inv-enroll-field-password-repeat
|
||||
attrs:
|
||||
field_key: password_repeat
|
||||
label: Password (repeat)
|
||||
type: password
|
||||
required: true
|
||||
placeholder: Password (repeat)
|
||||
placeholder_expression: false
|
||||
order: 2
|
||||
|
||||
- id: noble-inv-prompt-field-name
|
||||
model: authentik_stages_prompt.prompt
|
||||
identifiers:
|
||||
name: noble-inv-enroll-field-name
|
||||
attrs:
|
||||
field_key: name
|
||||
label: Name
|
||||
type: text
|
||||
required: true
|
||||
placeholder: Name
|
||||
placeholder_expression: false
|
||||
order: 0
|
||||
|
||||
- id: noble-inv-prompt-field-email
|
||||
model: authentik_stages_prompt.prompt
|
||||
identifiers:
|
||||
name: noble-inv-enroll-field-email
|
||||
attrs:
|
||||
field_key: email
|
||||
label: Email
|
||||
type: email
|
||||
required: true
|
||||
placeholder: Email
|
||||
placeholder_expression: false
|
||||
order: 1
|
||||
|
||||
- id: noble-inv-prompt-stage-credentials
|
||||
model: authentik_stages_prompt.promptstage
|
||||
identifiers:
|
||||
name: noble-inv-enroll-prompt-credentials
|
||||
attrs:
|
||||
fields:
|
||||
- !KeyOf noble-inv-prompt-field-username
|
||||
- !KeyOf noble-inv-prompt-field-password
|
||||
- !KeyOf noble-inv-prompt-field-password-repeat
|
||||
|
||||
- id: noble-inv-prompt-stage-details
|
||||
model: authentik_stages_prompt.promptstage
|
||||
identifiers:
|
||||
name: noble-inv-enroll-prompt-details
|
||||
attrs:
|
||||
fields:
|
||||
- !KeyOf noble-inv-prompt-field-name
|
||||
- !KeyOf noble-inv-prompt-field-email
|
||||
|
||||
- id: noble-inv-user-write-public
|
||||
model: authentik_stages_user_write.userwritestage
|
||||
identifiers:
|
||||
name: noble-inv-enroll-user-write-public
|
||||
attrs:
|
||||
user_creation_mode: always_create
|
||||
user_type: {{ noble_authentik_blueprint_public_invitation_user_type | trim | lower | to_json }}
|
||||
user_path_template: {{ noble_authentik_blueprint_public_invitation_user_path | trim | to_json }}
|
||||
create_users_group: !Find [authentik_core.group, [name, {{ noble_authentik_blueprint_public_invitation_user_group | trim | to_json }}]]
|
||||
|
||||
- id: noble-inv-user-write-lab
|
||||
model: authentik_stages_user_write.userwritestage
|
||||
identifiers:
|
||||
name: noble-inv-enroll-user-write-lab
|
||||
attrs:
|
||||
user_creation_mode: always_create
|
||||
user_type: {{ noble_authentik_blueprint_lab_invitation_user_type | trim | lower | to_json }}
|
||||
user_path_template: {{ noble_authentik_blueprint_lab_invitation_user_path | trim | to_json }}
|
||||
create_users_group: !KeyOf noble-lab-invited-group
|
||||
|
||||
- id: noble-inv-user-login
|
||||
model: authentik_stages_user_login.userloginstage
|
||||
identifiers:
|
||||
name: noble-inv-enroll-user-login
|
||||
|
||||
- model: authentik_flows.flowstagebinding
|
||||
identifiers:
|
||||
target: !KeyOf noble-inv-flow-public
|
||||
stage: !KeyOf noble-inv-stage-public
|
||||
order: 5
|
||||
attrs:
|
||||
evaluate_on_plan: true
|
||||
re_evaluate_policies: true
|
||||
|
||||
- model: authentik_flows.flowstagebinding
|
||||
identifiers:
|
||||
target: !KeyOf noble-inv-flow-public
|
||||
stage: !KeyOf noble-inv-prompt-stage-credentials
|
||||
order: 10
|
||||
|
||||
- model: authentik_flows.flowstagebinding
|
||||
identifiers:
|
||||
target: !KeyOf noble-inv-flow-public
|
||||
stage: !KeyOf noble-inv-prompt-stage-details
|
||||
order: 15
|
||||
|
||||
- model: authentik_flows.flowstagebinding
|
||||
identifiers:
|
||||
target: !KeyOf noble-inv-flow-public
|
||||
stage: !KeyOf noble-inv-user-write-public
|
||||
order: 20
|
||||
|
||||
- model: authentik_flows.flowstagebinding
|
||||
identifiers:
|
||||
target: !KeyOf noble-inv-flow-public
|
||||
stage: !KeyOf noble-inv-user-login
|
||||
order: 100
|
||||
|
||||
- model: authentik_flows.flowstagebinding
|
||||
identifiers:
|
||||
target: !KeyOf noble-inv-flow-lab
|
||||
stage: !KeyOf noble-inv-stage-lab
|
||||
order: 5
|
||||
attrs:
|
||||
evaluate_on_plan: true
|
||||
re_evaluate_policies: true
|
||||
|
||||
- model: authentik_flows.flowstagebinding
|
||||
identifiers:
|
||||
target: !KeyOf noble-inv-flow-lab
|
||||
stage: !KeyOf noble-inv-prompt-stage-credentials
|
||||
order: 10
|
||||
|
||||
- model: authentik_flows.flowstagebinding
|
||||
identifiers:
|
||||
target: !KeyOf noble-inv-flow-lab
|
||||
stage: !KeyOf noble-inv-prompt-stage-details
|
||||
order: 15
|
||||
|
||||
- model: authentik_flows.flowstagebinding
|
||||
identifiers:
|
||||
target: !KeyOf noble-inv-flow-lab
|
||||
stage: !KeyOf noble-inv-user-write-lab
|
||||
order: 20
|
||||
|
||||
- model: authentik_flows.flowstagebinding
|
||||
identifiers:
|
||||
target: !KeyOf noble-inv-flow-lab
|
||||
stage: !KeyOf noble-inv-user-login
|
||||
order: 100
|
||||
@@ -1,4 +1,4 @@
|
||||
# Noble — Brands so **Host** selects authentication flow: lab hostname → operator-only flow; extra hosts → default login.
|
||||
# Noble — Brands so **Host** selects authentication flow: lab hostname → operator-only hardened flow; extra hosts → public flow (**21**).
|
||||
version: 1
|
||||
metadata:
|
||||
name: noble-brands-domain-split
|
||||
@@ -21,7 +21,7 @@ entries:
|
||||
attrs:
|
||||
default: false
|
||||
title: {{ ((noble_authentik_blueprint_public_brand_title_prefix | default('Noble public')) ~ ' (' ~ (host | trim) ~ ')') | to_json }}
|
||||
flow_authentication: !Find [authentik_flows.flow, [slug, default-authentication-flow]]
|
||||
flow_authentication: !Find [authentik_flows.flow, [slug, {{ noble_authentik_blueprint_public_auth_flow_slug | trim | to_json }}]]
|
||||
flow_invalidation: !Find [authentik_flows.flow, [slug, default-invalidation-flow]]
|
||||
flow_user_settings: !Find [authentik_flows.flow, [slug, default-user-settings-flow]]
|
||||
{% endfor %}
|
||||
|
||||
Reference in New Issue
Block a user