Your cart is currently empty!
Debugging Attribute Persistence in Authentik Flows (Docker Edition)
Debugging Attribute Persistence in Authentik Flows (Docker Edition)
Posted: August 12, 2025 • Updated: August 13, 2025
If you’re working with goauthentik flows and prompts, you might run into a frustrating problem: you can set a value in your flow or policy (even print() it successfully), but when the flow finishes, that value is empty on the user’s attributes — and, by extension, in LDAP.
I hit this while trying to copy a user’s username into a custom attribute uid
so my NAS could pick it up from LDAP.
1) The Problem
- Created a hidden prompt named
attributes.uid
- Used logic to copy the username into this UID:
request.context["prompt_data"]["attributes"]["uid"] = request.context["prompt_data"]["username"]
print("UID set to:", request.context["prompt_data"]["attributes"]["uid"])
return True
The debug print worked — but the final user had uid: ""
.
2) Why This Happens
In Authentik flows:
request.context["prompt_data"]
is in-memory flow data- Just because you set it there doesn’t mean it’s written back to the user
- Prompt values are only persisted if both of the following are true:
- The prompt is mapped to a user field or attribute, and
- The stage is configured to persist values to the user
If those aren’t met, your change disappears when the flow ends.
3) Where print() Goes in Docker
If Authentik is running in Docker, any print()
in a policy or Python-like expression will go to the container’s stdout.
- Single container:
docker logs -f <container-name>
- Docker Compose (service name example):
docker compose logs -f server
Replace names with what your deployment uses (e.g., authentik
or authentik_server
). You won’t see these prints in the Authentik Events UI; for that you’d use Event.new(...)
.
4) Debugging Step-by-Step
Print both the prompt-side value and the saved user attribute:
print("Prompt UID:", request.context["prompt_data"]["attributes"].get("uid"))
print("User UID attr before save:", getattr(request.user, "attributes", {}).get("uid"))
When you run this in an expression/policy context:
- If Prompt UID has the value, but User UID is empty → you’re not persisting the change
- If both are empty → the value never got set
5) Two Ways to Fix It (Updated)
Option A – Prompt persistence (only in versions where it exists)
Some older Authentik versions exposed a Prompt option like “Persist as user attribute.” If your version still shows this:
- Edit your hidden prompt:
- Name:
attributes.uid
- Enable “Persist as user attribute” (or map the prompt to the user attribute explicitly)
- Name:
- Keep it in a stage that runs after you set the value
- Ensure the stage is a “Prompt” stage with write-back enabled
If you don’t see this option, use Option B.
Option B – Expression Policy on a stage after the user is resolved (recommended)
Recent Authentik versions may not expose a dedicated “Python stage,” and attaching policies to Prompt stages often runs before the user object is available. Instead:
- Create an Expression Policy with the following code:
uid_value = request.context["prompt_data"].get("username")
print("Copying username to UID:", uid_value)
user = request.user
user.attributes["uid"] = uid_value
user.save()
print("Saved UID to user:", user.attributes.get("uid"))
return True
- Attach this policy to a stage where
request.user
is available — for example, the built-inuser-login
stage — in your authentication flow. Do not attach it to a Prompt stage that runs before the user is resolved.
This approach bypasses prompt persistence entirely and writes directly to the user object so it survives into LDAP.
6) Minimal Working Example (Policy)
If the username is already known in your flow context, this minimal snippet reliably copies it to uid
:
uid_value = request.context["prompt_data"]["username"]
print("Copying username to UID:", uid_value)
user = request.user
user.attributes["uid"] = uid_value
user.save()
print("Saved UID to user:", user.attributes["uid"])
return True
Watch it live during testing:
docker logs -f <container-name>
# or
docker compose logs -f server
7) The Takeaway
- print() is your friend — watch it via
docker logs -f
- Prompt context is not the same as a saved user attribute
- Either ensure your prompt persists to the user (if supported), or write directly to the user via an Expression Policy on a stage after the user is resolved
Update (August 13, 2025)
Thanks to Axel Terizaki https://www.meido-rando.net/ for confirming this exact use case and the following notes:
- The “Persist as user attribute” option may not exist anymore in recent versions, so Option A might not be available.
- There isn’t a dedicated “Python stage.” Use an Expression Policy with the code above and attach it to the
user-login
stage (or any stage after the user is resolved). Attaching it to a Prompt stage can fail if the user is not yet available.
by
Tags:
Comments
One response to “Debugging Attribute Persistence in Authentik Flows (Docker Edition)”
-
Thank you so much ! This was my exact use case and I made it work thanks to you !
A few notes though :
– Option A was not possible for me, as the “persist as user attribute” doesn’t seem to exist anymore. It wasn’t on the prompt settings screen when editing it at least.
– Option B mentions a “python stage” but that doesn’t exist either. I created an expression policy with your code, and attached it to the “user-login” stage, and it worked. Attaching it to any prompt stage fails miserably.In any case, thank you again! 🙂
Leave a Reply