BTST
PluginsQuickstartDocs
Live Blog

From research to product evaluation

Evaluating a publishing workflow for an app you already own?

See what the BTST Blog plugin adds to an existing React or Next.js app
BTST

Open-source TypeScript features for the React application, data, and deployment you already own.

Released plugins

  • Blog
  • AI Chat
  • CMS
  • Form Builder
  • UI Builder
  • Kanban
  • Comments
  • Media
  • Route Docs
  • OpenAPI
  • Better Auth UI

Resources

  • Quickstart
  • Documentation
  • All plugins
  • Live Blog
  • GitHub (opens in a new tab)
© 2026 BTST. Open source under the MIT License.
AI Chat
September 21, 2026ReactBetter Auth UI

Change Passwords with BTST Better Auth UI

Verify current passwords, rotate sessions, and handle social-only accounts and older valid credentials in account settings.

Change Passwords with BTST Better Auth UI

Changing a password from account settings requires the current password and a decision about other signed-in devices. BTST Better Auth UI supplies that form, while Better Auth validates the credentials and updates the stored password.

This guide follows @btst/better-auth-ui@2.0.1, @btst/stack@3.1.2, and better-auth@1.6.16. It covers an existing signed-in account. For a user who cannot sign in, start with the password-reset guide.

Keep server and form validation aligned#

Enable email/password authentication in the existing Better Auth server. Merge these options into its existing configuration, preserving email delivery and any other auth policies:

TS
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
import type { BetterAuthOptions } from "better-auth";

export const passwordPolicy = {
  emailAndPassword: {
    enabled: true,
    minPasswordLength: 8,
    maxPasswordLength: 128,
  },
} satisfies BetterAuthOptions;

These lengths match the pinned release's defaults; they are not a recommendation to lower an existing policy. Keep stronger requirements already in force. Server validation remains necessary because requests can bypass the form.

The companion's matching configuration belongs in overrides.auth on the Stack provider:

TS
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
import type { AuthPluginOverrides } from "@btst/better-auth-ui/client";

export const passwordUI = {
  credentials: {
    confirmPassword: true,
    passwordValidation: { minLength: 8, maxLength: 128 },
  },
} satisfies Partial<AuthPluginOverrides>;

The confirmation field checks the new password for a matching entry before submission. The auth and account plugins must already be mounted; the ordinary security route is /p/account/security. See BTST auth setup for the shared integration.

Know what the supplied form sends#

The released change-password card calls authClient.changePassword with the current password, new password, and revokeOtherSessions: true. That revocation choice is built into this component; the validation override above does not change it.

Better Auth verifies the current password on the server before saving a new hash. A successful request with revocation enabled deletes the user's existing sessions and creates a replacement for the caller, setting its cookie in the response. Custom server integrations must preserve that cookie response. Merely calling the API and discarding the updated cookie can leave the browser holding an invalidated session.

If your product needs a different session choice or a different form, implement an override while retaining current-password verification and server validation. The upstream password API documents the operation; the pinned server route establishes this release's exact behavior.

Handle accounts that have no password yet#

The card checks whether the account list contains providerId: "credential". Without that linked account, it displays a Set Password action instead of asking for a current password that does not exist.

In this release, Set Password starts requestPasswordReset using the current user's email and the reset-password redirect, then navigates to sign-in. It does not create a credential locally or silently assign a password. Configure the server's reset email delivery and allowed redirect before exposing that path. The reset flow's revokeSessionsOnPasswordReset policy is separate from the change-password card's hard-coded revocation choice.

Test a social-only account explicitly. Receiving an “email sent” message is not evidence that delivery succeeded or that the account now has a password. Complete the link, then verify email/password sign-in and inspect the account's actual credential state.

Avoid locking out older passwords in the UI#

The card applies its passwordValidation rules to both the current-password and new-password fields. If you raise the UI minimum from eight to twelve characters, a user with a valid ten-character password can be blocked before the server sees the change request. Adding a new regular expression can cause the same problem.

The server's current-password check verifies the existing hash; its new-password length policy applies to the replacement. When tightening policy, test older valid credentials and use an appropriate form override if different rules are needed for the two fields. Do not weaken the new-password server policy to work around a form limitation. Keep a tested recovery path available.

Verify password replacement and session revocation#

Create two sessions for a disposable credential account, then change the password from the first. Confirm all of the following:

  1. A wrong current password fails without changing the stored credential or revoking sessions.
  2. A new password outside the server's length policy is rejected even through a direct request.
  3. After success, the old password no longer signs in and the new one does.
  4. The changing browser receives a replacement session and remains signed in.
  5. The second session is rejected by an authoritative server lookup.

If cookie caching is enabled, test that last point both with ordinary reads and with the cache bypass described in the session-management guide. Previously cached session data can remain visible until its cache lifetime ends. A stale header or client-side session object is not proof that revocation failed, and immediate disappearance from a UI is not proof that a protected operation rejects the old session.

The fragments were type-checked, and in-memory handler tests cover wrong credentials, length rejection, password replacement, current-session rotation, and rejection of the other session. They do not test production mail delivery, your database migration, a complete social-provider flow, or the full account-page UI. Run the same lifecycle against your configured application before relying on it.

In This Post

Keep server and form validation alignedKnow what the supplied form sendsHandle accounts that have no password yetAvoid locking out older passwords in the UIVerify password replacement and session revocation