# Noble — **lab** hostname authentication: operator-only, stricter password checks, MFA required (no WebAuthn-PWL skip). version: 1 metadata: name: noble-lab-operator-authentication labels: blueprints.goauthentik.io/instantiate: "true" entries: - model: authentik_blueprints.metaapplyblueprint attrs: 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: slug: {{ noble_authentik_blueprint_lab_flow_slug | trim | to_json }} attrs: name: Noble lab (operators) title: Noble lab — operators only designation: authentication authentication: none - id: noble-lab-identification-binding model: authentik_flows.flowstagebinding identifiers: order: 10 stage: !Find [authentik_stages_identification.identificationstage, [name, default-authentication-identification]] target: !KeyOf flow - id: noble-lab-password-binding model: authentik_flows.flowstagebinding identifiers: order: 20 stage: !KeyOf noble-lab-password-stage target: !KeyOf flow attrs: re_evaluate_policies: true - id: noble-lab-authenticator-binding model: authentik_flows.flowstagebinding identifiers: order: 30 stage: !KeyOf noble-lab-authenticator-validate-strict 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-lab-password-optional identifiers: name: noble-lab-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-lab-password-strength identifiers: name: noble-lab-password-strength attrs: expression: | 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 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: name: {{ noble_authentik_blueprint_operator_policy_name | trim | to_json }} attrs: expression: | u = context.get("pending_user") if u is None: return False {% for g in noble_authentik_blueprint_lab_operator_groups | default([]) %} if ak_is_group_member(u, name={{ g | trim | to_json }}): return True {% endfor %} ak_message("This login URL is for administrators only. Use the public Authentik hostname instead.") return False - model: authentik_policies.policybinding identifiers: order: 5 target: !KeyOf noble-lab-password-binding policy: !KeyOf noble-lab-operators-only - model: authentik_policies.policybinding identifiers: order: 10 target: !KeyOf noble-lab-password-binding policy: !KeyOf noble-lab-password-optional attrs: failure_result: true - model: authentik_policies.policybinding identifiers: order: 15 target: !KeyOf noble-lab-password-binding policy: !KeyOf noble-lab-password-strength