prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Using 'Have-I-been-pwned' policy breaks flows in Authentik 2022.4.1
  **Describe the bug**
  Using a 'Have-I-been-pwned' policy on a password prompt within a flow breaks the flow.
  
  **To Reproduce**
  Steps to reproduce the behavior:
  1. Use Authentik 2022.3.3
  2. Use all the default settings/flows, so a clean install
  3. Add a have-i-been-pwned policy to the default-password-change flow on the default-password-change-prompt stage.
  4. This stage binding has the following settings:
  - _Evaluate on plan: True_
  - _Re-evaluate policies: False_
  - _Invalid respones action: RETRY returns the error message and a similar challenge to the executor._
  - _Policy engine mode: ALL, all policies must match to include this stage access._
  5. Go to the Flow Overview and Execute flow with current user, see that the have-i-been pwned policy works correctly.
  6. Use Authentik 2022.4.1
  7. Repeat steps 2 - 5 described above
  8. See that you will receive an error message 'Password not set in context'.
  
  **Expected behavior**
  The password should be checked, and the flow should not crash with the error 'Password not set in context'.
  
  **Version and Deployment (please complete the following information):**
   - authentik version: 2022.4.1
   - Deployment: tested both Docker & K8S
  
  **Additional context**
  I repeated these steps multiple times and I keep getting the same issue. Therefore I think it is safe to assume that this is a bug introduced in the update from version 2022.3.3 to version 2022.4.1
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `authentik/policies/hibp/models.py`
  Content:
  ```
  1 """authentik HIBP Models"""
  2 from hashlib import sha1
  3 
  4 from django.db import models
  5 from django.utils.translation import gettext as _
  6 from rest_framework.serializers import BaseSerializer
  7 from structlog.stdlib import get_logger
  8 
  9 from authentik.lib.utils.http import get_http_session
  10 from authentik.policies.models import Policy, PolicyResult
  11 from authentik.policies.types import PolicyRequest
  12 
  13 LOGGER = get_logger()
  14 
  15 
  16 class HaveIBeenPwendPolicy(Policy):
  17     """Check if password is on HaveIBeenPwned's list by uploading the first
  18     5 characters of the SHA1 Hash."""
  19 
  20     password_field = models.TextField(
  21         default="password",
  22         help_text=_("Field key to check, field keys defined in Prompt stages are available."),
  23     )
  24 
  25     allowed_count = models.IntegerField(default=0)
  26 
  27     @property
  28     def serializer(self) -> BaseSerializer:
  29         from authentik.policies.hibp.api import HaveIBeenPwendPolicySerializer
  30 
  31         return HaveIBeenPwendPolicySerializer
  32 
  33     @property
  34     def component(self) -> str:
  35         return "ak-policy-hibp-form"
  36 
  37     def passes(self, request: PolicyRequest) -> PolicyResult:
  38         """Check if password is in HIBP DB. Hashes given Password with SHA1, uses the first 5
  39         characters of Password in request and checks if full hash is in response. Returns 0
  40         if Password is not in result otherwise the count of how many times it was used."""
  41         if self.password_field not in request.context:
  42             LOGGER.warning(
  43                 "Password field not set in Policy Request",
  44                 field=self.password_field,
  45                 fields=request.context.keys(),
  46             )
  47             return PolicyResult(False, _("Password not set in context"))
  48         password = str(request.context[self.password_field])
  49 
  50         pw_hash = sha1(password.encode("utf-8")).hexdigest()  # nosec
  51         url = f"https://api.pwnedpasswords.com/range/{pw_hash[:5]}"
  52         result = get_http_session().get(url).text
  53         final_count = 0
  54         for line in result.split("\r\n"):
  55             full_hash, count = line.split(":")
  56             if pw_hash[5:] == full_hash.lower():
  57                 final_count = int(count)
  58         LOGGER.debug("got hibp result", count=final_count, hash=pw_hash[:5])
  59         if final_count > self.allowed_count:
  60             message = _("Password exists on %(count)d online lists." % {"count": final_count})
  61             return PolicyResult(False, message)
  62         return PolicyResult(True)
  63 
  64     class Meta:
  65 
  66         verbose_name = _("Have I Been Pwned Policy")
  67         verbose_name_plural = _("Have I Been Pwned Policies")
  68
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/authentik/policies/hibp/models.py b/authentik/policies/hibp/models.py
  --- a/authentik/policies/hibp/models.py
  +++ b/authentik/policies/hibp/models.py
  @@ -9,6 +9,7 @@
   from authentik.lib.utils.http import get_http_session
   from authentik.policies.models import Policy, PolicyResult
   from authentik.policies.types import PolicyRequest
  +from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
   
   LOGGER = get_logger()
   
  @@ -38,14 +39,17 @@
           """Check if password is in HIBP DB. Hashes given Password with SHA1, uses the first 5
           characters of Password in request and checks if full hash is in response. Returns 0
           if Password is not in result otherwise the count of how many times it was used."""
  -        if self.password_field not in request.context:
  +        password = request.context.get(PLAN_CONTEXT_PROMPT, {}).get(
  +            self.password_field, request.context.get(self.password_field)
  +        )
  +        if not password:
               LOGGER.warning(
                   "Password field not set in Policy Request",
                   field=self.password_field,
                   fields=request.context.keys(),
               )
               return PolicyResult(False, _("Password not set in context"))
  -        password = str(request.context[self.password_field])
  +        password = str(password)
   
           pw_hash = sha1(password.encode("utf-8")).hexdigest()  # nosec
           url = f"https://api.pwnedpasswords.com/range/{pw_hash[:5]}"
  
verification_info: {"golden_diff": "diff --git a/authentik/policies/hibp/models.py b/authentik/policies/hibp/models.py\n--- a/authentik/policies/hibp/models.py\n+++ b/authentik/policies/hibp/models.py\n@@ -9,6 +9,7 @@\n from authentik.lib.utils.http import get_http_session\n from authentik.policies.models import Policy, PolicyResult\n from authentik.policies.types import PolicyRequest\n+from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT\n \n LOGGER = get_logger()\n \n@@ -38,14 +39,17 @@\n         \"\"\"Check if password is in HIBP DB. Hashes given Password with SHA1, uses the first 5\n         characters of Password in request and checks if full hash is in response. Returns 0\n         if Password is not in result otherwise the count of how many times it was used.\"\"\"\n-        if self.password_field not in request.context:\n+        password = request.context.get(PLAN_CONTEXT_PROMPT, {}).get(\n+            self.password_field, request.context.get(self.password_field)\n+        )\n+        if not password:\n             LOGGER.warning(\n                 \"Password field not set in Policy Request\",\n                 field=self.password_field,\n                 fields=request.context.keys(),\n             )\n             return PolicyResult(False, _(\"Password not set in context\"))\n-        password = str(request.context[self.password_field])\n+        password = str(password)\n \n         pw_hash = sha1(password.encode(\"utf-8\")).hexdigest()  # nosec\n         url = f\"https://api.pwnedpasswords.com/range/{pw_hash[:5]}\"\n", "issue": "Using 'Have-I-been-pwned' policy breaks flows in Authentik 2022.4.1\n**Describe the bug**\r\nUsing a 'Have-I-been-pwned' policy on a password prompt within a flow breaks the flow.\r\n\r\n**To Reproduce**\r\nSteps to reproduce the behavior:\r\n1. Use Authentik 2022.3.3\r\n2. Use all the default settings/flows, so a clean install\r\n3. Add a have-i-been-pwned policy to the default-password-change flow on the default-password-change-prompt stage.\r\n4. This stage binding has the following settings:\r\n- _Evaluate on plan: True_\r\n- _Re-evaluate policies: False_\r\n- _Invalid respones action: RETRY returns the error message and a similar challenge to the executor._\r\n- _Policy engine mode: ALL, all policies must match to include this stage access._\r\n5. Go to the Flow Overview and Execute flow with current user, see that the have-i-been pwned policy works correctly.\r\n6. Use Authentik 2022.4.1\r\n7. Repeat steps 2 - 5 described above\r\n8. See that you will receive an error message 'Password not set in context'.\r\n\r\n**Expected behavior**\r\nThe password should be checked, and the flow should not crash with the error 'Password not set in context'.\r\n\r\n**Version and Deployment (please complete the following information):**\r\n - authentik version: 2022.4.1\r\n - Deployment: tested both Docker & K8S\r\n\r\n**Additional context**\r\nI repeated these steps multiple times and I keep getting the same issue. Therefore I think it is safe to assume that this is a bug introduced in the update from version 2022.3.3 to version 2022.4.1\r\n\n", "before_files": [{"content": "\"\"\"authentik HIBP Models\"\"\"\nfrom hashlib import sha1\n\nfrom django.db import models\nfrom django.utils.translation import gettext as _\nfrom rest_framework.serializers import BaseSerializer\nfrom structlog.stdlib import get_logger\n\nfrom authentik.lib.utils.http import get_http_session\nfrom authentik.policies.models import Policy, PolicyResult\nfrom authentik.policies.types import PolicyRequest\n\nLOGGER = get_logger()\n\n\nclass HaveIBeenPwendPolicy(Policy):\n    \"\"\"Check if password is on HaveIBeenPwned's list by uploading the first\n    5 characters of the SHA1 Hash.\"\"\"\n\n    password_field = models.TextField(\n        default=\"password\",\n        help_text=_(\"Field key to check, field keys defined in Prompt stages are available.\"),\n    )\n\n    allowed_count = models.IntegerField(default=0)\n\n    @property\n    def serializer(self) -> BaseSerializer:\n        from authentik.policies.hibp.api import HaveIBeenPwendPolicySerializer\n\n        return HaveIBeenPwendPolicySerializer\n\n    @property\n    def component(self) -> str:\n        return \"ak-policy-hibp-form\"\n\n    def passes(self, request: PolicyRequest) -> PolicyResult:\n        \"\"\"Check if password is in HIBP DB. Hashes given Password with SHA1, uses the first 5\n        characters of Password in request and checks if full hash is in response. Returns 0\n        if Password is not in result otherwise the count of how many times it was used.\"\"\"\n        if self.password_field not in request.context:\n            LOGGER.warning(\n                \"Password field not set in Policy Request\",\n                field=self.password_field,\n                fields=request.context.keys(),\n            )\n            return PolicyResult(False, _(\"Password not set in context\"))\n        password = str(request.context[self.password_field])\n\n        pw_hash = sha1(password.encode(\"utf-8\")).hexdigest()  # nosec\n        url = f\"https://api.pwnedpasswords.com/range/{pw_hash[:5]}\"\n        result = get_http_session().get(url).text\n        final_count = 0\n        for line in result.split(\"\\r\\n\"):\n            full_hash, count = line.split(\":\")\n            if pw_hash[5:] == full_hash.lower():\n                final_count = int(count)\n        LOGGER.debug(\"got hibp result\", count=final_count, hash=pw_hash[:5])\n        if final_count > self.allowed_count:\n            message = _(\"Password exists on %(count)d online lists.\" % {\"count\": final_count})\n            return PolicyResult(False, message)\n        return PolicyResult(True)\n\n    class Meta:\n\n        verbose_name = _(\"Have I Been Pwned Policy\")\n        verbose_name_plural = _(\"Have I Been Pwned Policies\")\n", "path": "authentik/policies/hibp/models.py"}], "after_files": [{"content": "\"\"\"authentik HIBP Models\"\"\"\nfrom hashlib import sha1\n\nfrom django.db import models\nfrom django.utils.translation import gettext as _\nfrom rest_framework.serializers import BaseSerializer\nfrom structlog.stdlib import get_logger\n\nfrom authentik.lib.utils.http import get_http_session\nfrom authentik.policies.models import Policy, PolicyResult\nfrom authentik.policies.types import PolicyRequest\nfrom authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT\n\nLOGGER = get_logger()\n\n\nclass HaveIBeenPwendPolicy(Policy):\n    \"\"\"Check if password is on HaveIBeenPwned's list by uploading the first\n    5 characters of the SHA1 Hash.\"\"\"\n\n    password_field = models.TextField(\n        default=\"password\",\n        help_text=_(\"Field key to check, field keys defined in Prompt stages are available.\"),\n    )\n\n    allowed_count = models.IntegerField(default=0)\n\n    @property\n    def serializer(self) -> BaseSerializer:\n        from authentik.policies.hibp.api import HaveIBeenPwendPolicySerializer\n\n        return HaveIBeenPwendPolicySerializer\n\n    @property\n    def component(self) -> str:\n        return \"ak-policy-hibp-form\"\n\n    def passes(self, request: PolicyRequest) -> PolicyResult:\n        \"\"\"Check if password is in HIBP DB. Hashes given Password with SHA1, uses the first 5\n        characters of Password in request and checks if full hash is in response. Returns 0\n        if Password is not in result otherwise the count of how many times it was used.\"\"\"\n        password = request.context.get(PLAN_CONTEXT_PROMPT, {}).get(\n            self.password_field, request.context.get(self.password_field)\n        )\n        if not password:\n            LOGGER.warning(\n                \"Password field not set in Policy Request\",\n                field=self.password_field,\n                fields=request.context.keys(),\n            )\n            return PolicyResult(False, _(\"Password not set in context\"))\n        password = str(password)\n\n        pw_hash = sha1(password.encode(\"utf-8\")).hexdigest()  # nosec\n        url = f\"https://api.pwnedpasswords.com/range/{pw_hash[:5]}\"\n        result = get_http_session().get(url).text\n        final_count = 0\n        for line in result.split(\"\\r\\n\"):\n            full_hash, count = line.split(\":\")\n            if pw_hash[5:] == full_hash.lower():\n                final_count = int(count)\n        LOGGER.debug(\"got hibp result\", count=final_count, hash=pw_hash[:5])\n        if final_count > self.allowed_count:\n            message = _(\"Password exists on %(count)d online lists.\" % {\"count\": final_count})\n            return PolicyResult(False, message)\n        return PolicyResult(True)\n\n    class Meta:\n\n        verbose_name = _(\"Have I Been Pwned Policy\")\n        verbose_name_plural = _(\"Have I Been Pwned Policies\")\n", "path": "authentik/policies/hibp/models.py"}]}