diff --git a/example.env b/example.env index b98824643..3be32d933 100644 --- a/example.env +++ b/example.env @@ -244,6 +244,7 @@ GOTRUE_LOG_LEVEL="debug" GOTRUE_SECURITY_REFRESH_TOKEN_ROTATION_ENABLED="false" GOTRUE_SECURITY_REFRESH_TOKEN_REUSE_INTERVAL="0" GOTRUE_SECURITY_UPDATE_PASSWORD_REQUIRE_REAUTHENTICATION="false" +GOTRUE_SECURITY_UPDATE_PASSWORD_REQUIRE_CURRENT_PASSWORD="false" GOTRUE_OPERATOR_TOKEN="unused-operator-token" GOTRUE_RATE_LIMIT_HEADER="X-Forwarded-For" GOTRUE_RATE_LIMIT_EMAIL_SENT="100" diff --git a/internal/api/user.go b/internal/api/user.go index 723bce144..2458e8837 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -18,6 +18,7 @@ import ( type UserUpdateParams struct { Email string `json:"email"` Password *string `json:"password"` + CurrentPassword *string `json:"current_password"` Nonce string `json:"nonce"` Data map[string]interface{} `json:"data"` AppData map[string]interface{} `json:"app_metadata,omitempty"` @@ -147,6 +148,22 @@ func (a *API) UserUpdate(w http.ResponseWriter, r *http.Request) error { } if params.Password != nil { + if config.Security.UpdatePasswordRequireCurrentPassword { + if params.CurrentPassword == nil || *params.CurrentPassword == "" { + return apierrors.NewBadRequestError(apierrors.ErrorCodeValidationFailed, "Current password is required to update password") + } + + if user.HasPassword() { + authenticated, _, err := user.Authenticate(ctx, db, *params.CurrentPassword, config.Security.DBEncryption.DecryptionKeys, false, "") + if err != nil { + return apierrors.NewInternalServerError("Error verifying current password").WithInternalError(err) + } + if !authenticated { + return apierrors.NewBadRequestError(apierrors.ErrorCodeInvalidCredentials, InvalidLoginMessage) + } + } + } + if config.Security.UpdatePasswordRequireReauthentication { now := time.Now() // we require reauthentication if the user hasn't signed in recently in the current session diff --git a/internal/conf/configuration.go b/internal/conf/configuration.go index 3f4bbf833..4f80d1fde 100644 --- a/internal/conf/configuration.go +++ b/internal/conf/configuration.go @@ -728,6 +728,7 @@ type SecurityConfiguration struct { RefreshTokenReuseInterval int `json:"refresh_token_reuse_interval" split_words:"true"` RefreshTokenAllowReuse bool `json:"refresh_token_allow_reuse" split_words:"true"` UpdatePasswordRequireReauthentication bool `json:"update_password_require_reauthentication" split_words:"true"` + UpdatePasswordRequireCurrentPassword bool `json:"update_password_require_current_password" split_words:"true" default:"false"` ManualLinkingEnabled bool `json:"manual_linking_enabled" split_words:"true" default:"false"` DBEncryption DatabaseEncryptionConfiguration `json:"database_encryption" split_words:"true"`