Repository navigation
fix(auth): skip the too long password check on plugin logins - #7828
Merged
Chocobozzz merged 4 commits intoNov 5, 2026
Merged
Chocobozzz merged 4 commits into
Chocobozzz merged 4 commits into
Conversation
dlarochette
force-pushed
the
fix/password-grant-plugin-bypass-too-long-check
branch
from
October 15, 2026 14:54
4a1ebfc to
98a6040
Compare
handlePasswordGrant rejected any password over the bcrypt limit before knowing how the user would be authenticated. On the id-and-pass plugin path the password is validated by the plugin itself and never reaches bcrypt, so the check rejected valid credentials without protecting anything on that path. Move the check into getUserOrThrow, immediately before user.isPasswordMatch(), which is the only place a password is compared with bcrypt on the login path. Test: the id-and-pass-auth plugin test now logs in with a password over the bcrypt limit, and confirms a local account still gets a clean too_long_password error instead of silently falling through.
usersCheckCurrentPasswordFactory and the /users/me password update validator both call user.isPasswordMatch(currentPassword) directly, without going through getUserOrThrow. A currentPassword over 72 bytes reaches comparePassword, which throws a plain Error instead of returning a clean too_long_password response. Add the same isUserPasswordTooLong check at both call sites, gated on the account being local (it is for update-me; explicit for the factory, where a plugin account takes a different branch already). Tests: - unit: isUserPasswordTooLong/isUserPasswordValid boundary cases (char count vs byte count), and a pinning test for comparePassword's own 72-byte guard (it throws rather than letting bcrypt silently truncate and compare a prefix) - integration: too long currentPassword on PUT /users/me and on the two-factor request endpoint now both return a clean 400 too_long_password instead of an unhandled error
dlarochette
force-pushed
the
fix/password-grant-plugin-bypass-too-long-check
branch
from
October 15, 2026 15:23
98a6040 to
e4f1e9f
Compare
Owner
|
Thank you |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #7829
Problem
handlePasswordGrantrejects any password over the bcrypt limit (50characters / 72 bytes) before knowing how the user will be
authenticated. On the id-and-pass plugin path (LDAP, SAML, etc.) the
password is validated by the plugin itself and never reaches bcrypt,
so the check rejects valid credentials without protecting anything on
that path.
A production log shows the plugin succeeding and the request failing
4ms later:
The fix
Move the check into
getUserOrThrow, immediately beforeuser.isPasswordMatch()-- the only place a password is actuallycompared with bcrypt on the login path. This naturally excludes the
plugin path (which returns before reaching that line) without needing
to special-case it.
While auditing the other two places that compare a password with
bcrypt (
usersCheckCurrentPasswordFactoryand the/users/mepassword-update validator), I found both call
user.isPasswordMatch(currentPassword)directly, bypassing lengthvalidation entirely. A
currentPasswordover 72 bytes there reachescomparePassword, which throws a plainErrorinstead of returning aclean
too_long_passwordresponse. Fixed the same way at both sites.This is not a security fix:
comparePasswordhas its own guard thatthrows above 72 bytes rather than letting bcrypt silently truncate and
compare a prefix, so there is no bypass either before or after this
change. It's a correctness fix (reject a long password with a clean,
typed error instead of either skipping validation entirely on the
plugin path, or crashing with an untyped error on the two
currentPasswordpaths).Unrelated to #7344 (raising the 50-character limit) -- that's a
deliberate product constraint tighter than the 72-byte bcrypt limit,
and this PR doesn't touch it.
Tests
server-helpers/password.ts):isUserPasswordTooLongcharacter-count vs byte-count boundaries, and a pinning test that
comparePassworditself refuses (throws on) anything over 72 bytesrather than truncating
plugins/id-and-pass-auth.ts): a plugin login with a167-byte password now succeeds; a local login with a 305-byte
password still returns a clean
too_long_passwordcheck-params/user-me.ts,check-params/two-factor.ts): an over-longcurrentPasswordonPUT /users/meand on the two-factor request endpoint both nowreturn a clean 400
too_long_passwordinstead of an unhandled errorAll 11 CI jobs pass on this branch.