fix(auth): add password strength validation to admin.createUser
The admin.createUser endpoint wasn't checking password strength against configured rules, while admin.updateUser was doing this validation. This creates a security gap where users created via the admin API could have weak passwords that don't meet the configured requirements.
This commit adds the missing password strength validation to ensure consistent security across all user creation paths.
What kind of change does this PR introduce?
This is a security-related bug fix that closes a gap in password validation. It maintains backward compatibility for all valid use cases while enforcing proper security standards for passwords created through the admin API.
What is the current behavior?
Currently, there's an inconsistency in password validation within the Supabase Auth API:
- The admin.updateUser function correctly validates passwords against configured strength rules
- The admin.createUser function does not validate passwords against these same rules
This inconsistency creates a security vulnerability where:
- Users created through the admin API can have weak passwords that don't meet the configured security requirements
- These weak passwords bypass the password strength rules defined in the application configuration
- There's an inconsistent security posture between user creation and user updates
As noted in issue #1959, this means that even if an application has strict password rules configured, an administrator can inadvertently create users with weak passwords that would otherwise be rejected if created through normal registration or updated later.
What is the new behavior?
- The admin.createUser function now validates passwords against the configured strength rules
- Password validation is consistent across both user creation and user updates
- Weak passwords are rejected adequately with appropriate error messages (422 Unprocessable Entity with error_code="weak_password")
- The security gap is closed, ensuring all passwords in the system meet the configured security requirements
Checklist for Submitting Pull Requests
Is there a corresponding issue created for it? If so, please include it in the PR description so we can track/refer to it.: Fixes #1959
Does your PR follow the semantic-release commit guidelines?: Yes
Are the existing tests passing?:
The full test suite (make test) shows failures, but these failures existed before my changes and are unrelated to the password validation functionality I've fixed. I've verified that my specific changes work by running the relevant tests in isolation.
To verify my changes, I ran: go test ./internal/api -run TestAdmin -v which confirms the password validation is now working correctly.
Have you written some tests for your PR?: Yes
Got it! I'll make the required changes to bypass the password strength using an argument wherever necessary.
Got it! I'll make the required changes to bypass the password strength using an argument wherever necessary.
I think that we should add a parameter to include rather than exclude as this would be a BC break.
CC @kangmingtay @hf
Thanks for your comment, @cstockton. Are you suggesting that the default behavior should be to bypass password validation, not enforce it? In my current implementation:
- When bypass_password_check is not provided, it defaults to false and password validation happens
- When bypass_password_check is set to true, password validation is skipped.
Please confirm if this is not the expected behaviour, and I'll update the implementation to skip the password check by default and only do the check when some parameter (maybe like enforce_password_check) is set to true.
Thanks for your comment, @cstockton. Are you suggesting that the default behavior should be to bypass password validation, not enforce it? In my current implementation:
My main concern is changing any behavior that we have today. Some users may rely on the check being skipped, for better or worst. Having an additional flag that is false by default to enforce it is a backwards compatible change, having that setting true by default is not.
Thanks for your comment, @cstockton. Are you suggesting that the default behavior should be to bypass password validation, not enforce it? In my current implementation:
My main concern is changing any behavior that we have today. Some users may rely on the check being skipped, for better or worst. Having an additional flag that is false by default to enforce it is a backwards compatible change, having that setting true by default is not.
Thanks for the clarification. I will make the required changes.
Thank you for the change! Please update the openapi.yaml file.
Any update on this?
@bharabhi01 After looking at this in depth I see there is a subtle BC break here in that the adminUserUpdate now no longer applies password checks. I believe the original reason it wasn't applied on adminUserCreate but was left for adminUserUpdate was because of a pattern of setting short temporary password on new accounts, but requiring password updates which often may come from the user to be checked.
I believe a change that could be accepted would change the flag to be:
PasswordCheck *bool `json:"password_check,omitempty"`
Then add a simple func like:
func isPasswordCheckRequired(ifNotSet bool, option *bool) bool {
if option == nil {
return ifNotSet
}
return *option
}
Guard with default of true in adminUserUpdate:
if isPasswordCheckRequired(true, params.PasswordCheck) {
if err := a.checkPasswordStrength(ctx, password); err != nil {
return err
}
}
Guard with default of false in adminUserCreate:
if isPasswordCheckRequired(false, params.PasswordCheck) {
if err := a.checkPasswordStrength(ctx, password); err != nil {
return err
}
}
This way existing behavior is preserved, but it still grants final say to the callers of the API.
@hf Does this seem reasonable?