Skip to content

events: add reason to the MODEL_UPDATED event for password hash updates - #18068

Open
danidee10 wants to merge 13 commits into
goauthentik:mainfrom
danidee10:feat/add-reason-password-hash-update
Open

events: add reason to the MODEL_UPDATED event for password hash updates#18068
danidee10 wants to merge 13 commits into
goauthentik:mainfrom
danidee10:feat/add-reason-password-hash-update

Conversation

@danidee10

@danidee10 danidee10 commented Nov 11, 2025

Copy link
Copy Markdown

Checklist

  • Local tests pass (ak test authentik/)
  • The code has been formatted (make lint-fix)

Initially, I started with a more complex Implementation which detected the password hash change in all cases but it felt a bit hacky as I had to use Queryset.update to avoid emitting a signal.

I switched to a simpler approach that uses model_utils.FieldTracker. It only detects changes when the audit middleware runs and register the signal handlers i.e It will not detect changes that happen outside the request/response cycle.

The rationale behind this is that anyone who changes the iterations/rounds of the hashers is probably an advanced user that knows that the hashes will be updated the next time django runs check_password and they shouldn't need an explicit event log

closes #17840

@netlify

netlify Bot commented Nov 11, 2025

Copy link
Copy Markdown

Deploy Preview for authentik-storybook ready!

Name Link
🔨 Latest commit 36c9a2b
🔍 Latest deploy log https://app.netlify.com/projects/authentik-storybook/deploys/69874fe4b4f5c800080c52dc
😎 Deploy Preview https://deploy-preview-18068--authentik-storybook.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Nov 11, 2025

Copy link
Copy Markdown

Deploy Preview for authentik-docs ready!

Name Link
🔨 Latest commit 391a3d3
🔍 Latest deploy log https://app.netlify.com/projects/authentik-docs/deploys/691f244f066fa80008383ca5
😎 Deploy Preview https://deploy-preview-18068--authentik-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Nov 11, 2025

Copy link
Copy Markdown

Deploy Preview for authentik-integrations ready!

Name Link
🔨 Latest commit 36c9a2b
🔍 Latest deploy log https://app.netlify.com/projects/authentik-integrations/deploys/69874fe4bb6f860008ecc5e6
😎 Deploy Preview https://deploy-preview-18068--authentik-integrations.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov

codecov Bot commented Nov 11, 2025

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.18%. Comparing base (ab16661) to head (36c9a2b).
⚠️ Report is 706 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #18068      +/-   ##
==========================================
- Coverage   93.23%   93.18%   -0.06%     
==========================================
  Files         968      969       +1     
  Lines       53589    53648      +59     
==========================================
+ Hits        49965    49993      +28     
- Misses       3624     3655      +31     
Flag Coverage Δ
conformance 37.92% <9.83%> (-0.04%) ⬇️
e2e 43.89% <9.83%> (-0.05%) ⬇️
integration 22.64% <9.83%> (-0.07%) ⬇️
unit 91.41% <100.00%> (+0.03%) ⬆️
unit-migrate 91.42% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Comment thread scripts/test_docker.sh
export COMPOSE_PROJECT_NAME="authentik-test-${AUTHENTIK_TAG}"

if [[ -v BUILD ]]; then
if [ -n "${BUILD:-}" ]; then

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When trying to test with docker. I discovered that this syntax wasn't POSIX compliant and didn't work on macos (with bash 3.2)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can confirm the same with zsh 5.9 (arm64-apple-darwin25.0)

@rissson rissson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@danidee10 fancy seeing you here! Thank you for the contribution!

Comment thread authentik/events/middleware.py Outdated
Comment on lines +207 to +208
if isinstance(instance, User) and instance.tracker.has_changed("password"):
context["reason"] = PASSWORD_HASH_UPGRADE_REASON

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we know this is not just the user changing their password?

@danidee10 danidee10 Nov 12, 2025

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't know as there's no way to distinguish why the hash changed here
But I thought it might be acceptable since there's already an existing PASSWORD_SET event that tracks password changes. Thus, anyone looking at the event log can figure out why the hash changed.

I had a different approach here which shouldn't log the event (when the user changes their password) as it doesn't depend on the User model's post_save signal.

I guess it depends on how you answer these questions:
Should we track all password hash changes regardless of how they happened ? or
Should we only track password hash changes because Django changed the iterations of the hasher ?

@rissson rissson added this to the Release 2025.10.2 milestone Nov 12, 2025
@rissson
rissson requested a review from BeryJu November 12, 2025 13:47
@BeryJu

BeryJu commented Nov 18, 2025

Copy link
Copy Markdown
Member

ideas from chatting with @rissson

  • check if we can override django to know when the password is upgraded
  • context manager for authenticate() call to add this to event since that's the only time this should be triggered

@danidee10

Copy link
Copy Markdown
Author

check if we can override django to know when the password is upgraded

That was my Initial approach by overriding User.check_password but I didn't like the fact that I had to disable the post_save signal. I've been thinking about it again and I think there's a better way to Implement it without disabling the signal entirely.

context manager for authenticate() call to add this to event since that's the only time this should be triggered
I'm less inclined towards this approach

It has more moving parts (We have to apply the context manager to all existing auth backends and future auth backends)
I also have some open questions about the Implementation:

  1. is the plan to get the context manager to attach some extra context so we can reuse the existing code in the audit middleware ? If yes then it's even more moving parts because the middleware runs before authenticate is called.
  2. Are we sending a new Event from the context manager ? If yes then we still have to find a way to disable the signal that's triggered in check_password.

I will go with the check if we can override django to know when the password is upgraded approach

@danidee10

danidee10 commented Nov 20, 2025

Copy link
Copy Markdown
Author

@BeryJu I went with Option 1 as stated earlier. I replaced model_utils.FieldTracker with a class attribute to track the password hash change. I also added a new test.

@BeryJu
BeryJu requested review from melizeche and rissson November 20, 2025 14:46
@BeryJu
BeryJu removed the request for review from authentik-bot November 20, 2025 14:46
@BeryJu BeryJu changed the title feat: add reason to the MODEL_UPDATED event for password hash updates events: add reason to the MODEL_UPDATED event for password hash updates Dec 5, 2025
@BeryJu

BeryJu commented Feb 5, 2026

Copy link
Copy Markdown
Member

We wanted to look into overwriting the check_password function from django to detect when it's doing a hash upgrade and there using audit_ignore() to prevent events from being created, but we haven't gotten around to that yet.

@danidee10

Copy link
Copy Markdown
Author

We wanted to look into overwriting the check_password function from django to detect when it's doing a hash upgrade and there using audit_ignore() to prevent events from being created, but we haven't gotten around to that yet.

I wasn't aware of the audit_ignore context manager. Now, I've reverted the code to an earlier version (that worked like you described above) and integrated it.

@github-project-automation github-project-automation Bot moved this from Todo to In Progress in authentik Core Feb 9, 2026
@rissson

rissson commented Feb 9, 2026

Copy link
Copy Markdown
Member

Thanks for being so responsive, even if we have been anything but. The changes look alright to me, but I'll let the team take another pass to determine if any change is still needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

Add reason for model_updated of a user after authentik upgrade

5 participants