Docs

User & Accounts

Beyond authenticating users, Better Auth also provides a set of methods to manage users. This includes, updating user information, changing passwords, and more.

User table

The user table stores the user data. The user table has the following fields:

  • id: The user id.
  • email: The email of the user.
  • name: The name of the user.
  • image: The image of the user.
  • createdAt: The creation date of the user.
  • updatedAt: The last update date of the user.

The user table can be extended by plugins to store additional data. When a plugin extends a user table it's inferred by the type system and can be used in the client.

Update User

Update User Information

To update user information, you can use the updateUser function provided by the client. The updateUser function takes an object with the following properties:

await authClient.updateUser({
    image: "https://example.com/image.jpg",
    name: "John Doe",
})

Change Email

To allow users to change their email, first enable the changeEmail feature, which is disabled by default. Set changeEmail.enabled to true:

export const auth = betterAuth({
    user: {
        changeEmail: {
            enabled: true,
        }
    }
})

For users with a verified email, provide the sendChangeEmailVerification function. This function triggers when a user changes their email, sending a verification email with a URL and token. If the current email isn't verified, the change happens immediately without verification.

export const auth = betterAuth({
    user: {
        changeEmail: {
            enabled: true,
            sendChangeEmailVerification: async ({ user, newEmail, url, token }, request) => {
                await sendEmail({
                    to: user.email, // verification email must be sent to the current user email to approve the change
                    subject: 'Approve email change',
                    text: `Click the link to approve the change: ${url}`
                })
            }
        }
    }
})

Once enabled, use the changeEmail function on the client to update a user’s email. The user must verify their current email before changing it.

await authClient.changeEmail({
    newEmail: "[email protected]",
    callbackURL: "/dashboard", //to redirect after verification
});

After verification, the new email is updated in the user table, and a confirmation is sent to the new address.

If the current email is unverified, the new email is updated without the verification step.

Change Password

Password of a user isn't stored in the user table. Instead, it's stored in the account table. To change the password of a user, you can use the changePassword function provided by the client. The changePassword function takes an object with the following properties:

await authClient.changePassword({
    newPassword: "newPassword123",
    currentPassword: "oldPassword123",
    revokeOtherSessions: true, // revoke all other sessions the user is signed into
});

Set Password

If a user was registered using OAuth or other providers, they won't have a password or a credential account. In this case, you can use the setPassword action to set a password for the user. For security reasons, this function can only be called from the server. We recommend having users go through a 'forgot password' flow to set a password for their account.

await auth.api.setPassword({
    newPassword: "password",
    headers: //
});

Delete User

Better Auth provides a utility to hard delete a user from your database. It's disabled by default, but you can enable it easily by passing enabled:true

export const auth = betterAuth({
    //...other config
    user: {
        deleteUser: {
            enabled: true // [!Code Highlight]
        }
    }
})

Once enabled, you can call authClient.deleteUser to permanently delete user data from your database.

Adding Verification Before Deletion

For added security, you’ll likely want to confirm the user’s intent before deleting their account. A common approach is to send a verification email. Better Auth provides a sendDeleteAccountVerification utility for this purpose.

Here’s how you can set it up:

export const auth = betterAuth({
    user: {
        deleteUser: {
            enabled: true,
            sendDeleteAccountVerification: async (
                {
                    user,   // The user object
                    url, // The auto-generated URL for deletion
                    token  // The verification token  (can be used to generate custom URL)
                },
                request  // The original request object (optional)
            ) => {
                // Your email sending logic here
                // Example: sendEmail(data.user.email, "Verify Deletion", data.url);
            },
        },
    },
});

How callback verification works:

  • Callback URL: The url provided in sendDeleteAccountVerification is a pre-generated link that deletes the user data when accessed.
delete-user.ts
await authClient.deleteUser({
    callbackURL: "/goodbye" // you can provide a callback URL to redirect after deletion
});
  • Authentication Check: The user must be signed in to the account they’re attempting to delete. If they aren’t signed in, the deletion process will fail.

If you have sent a custom URL, you can use the deleteUser method with the token to delete the user.

delete-user.ts
await authClient.deleteUser({
    token
});

Authentication Requirements

To delete a user, the user must meet one of the following requirements:

  1. A valid password

if the user has a password, they can delete their account by providing the password.

delete-user.ts
await authClient.deleteUser({
    password: "password"
});
  1. Fresh session

The user must have a fresh session token, meaning the user must have signed in recently. This is checked if the password is not provided.

By default session.freshAge is set to 60 * 60 * 24 (1 day). You can change this value by passing the session object to the auth configuration. If it is set to 0, the freshness check is disabled.

delete-user.ts
await authClient.deleteUser();
  1. The user must provide a token generated by the sendDeleteAccountVerification callback.
delete-user.ts
await authClient.deleteUser({
    token
});

Callbacks

beforeDelete: This callback is called before the user is deleted. You can use this callback to perform any cleanup or additional checks before deleting the user.

auth.ts
export const auth = betterAuth({
    user: {
        deleteUser: {
            enabled: true,
            beforeDelete: async (user) => {
                // Perform any cleanup or additional checks here
            },
        },
    },
});

you can also throw APIError to interrupt the deletion process.

auth.ts
import { betterAuth } from "better-auth";
import { APIError } from "better-auth/api";
 
export const auth = betterAuth({
    user: {
        deleteUser: {
            enabled: true,
            beforeDelete: async (user, request) => {
                if (user.email.includes("admin")) {
                    throw new APIError("BAD_REQUEST", {
                        message: "Admin accounts can't be deleted",
                    });
                }
            },
        },
    },
});

afterDelete: This callback is called after the user is deleted. You can use this callback to perform any cleanup or additional actions after the user is deleted.

auth.ts
export const auth = betterAuth({
    user: {
        deleteUser: {
            enabled: true,
            afterDelete: async (user, request) => {
                // Perform any cleanup or additional actions here
            },
        },
    },
});

Accounts

Better Auth supports multiple authentication methods. Each authentication method is called a provider. For example, email and password authentication is a provider, Google authentication is a provider, etc.

When a user signs in using a provider, an account is created for the user. The account stores the authentication data returned by the provider. This data includes the access token, refresh token, and other information returned by the provider.

Account table

The account table stores the authentication data of the user. The account table has the following fields:

  • id: The unique identifier of the account.
  • userId: The id of the user.
  • accountId: The id of the account provided by the OAuth provider. (optional)
  • providerId: The id of the provider. (optional)
  • accessToken: The access token of the account. Returned by the provider. (optional)
  • refreshToken: The refresh token of the account. Returned by the provider. (optional)
  • expiresAt: The time when the access token expires. (optional)
  • password: The password of the account. Mainly used for email and password authentication. (optional)

List User Accounts

To list user accounts you can use client.user.listAccounts method. Which will return all accounts associated with a user.

const accounts = await authClient.listAccounts();

Account Linking

Account linking enables users to associate multiple authentication methods with a single account. With Better Auth, users can connect additional social sign-ons or OAuth providers to their existing accounts if the provider confirms the user's email as verified.

If account linking is disabled, no accounts can be linked, regardless of the provider or email verification status.

auth.ts
const auth = new BetterAuth({
    account: {
        accountLinking: {
            enabled: true, 
        }
    },
});

Forced Linking

You can specify a list of "trusted providers." When a user logs in using a trusted provider, their account will be automatically linked even if the provider doesn’t confirm the email verification status. Use this with caution as it may increase the risk of account takeover.

auth.ts
const auth = new BetterAuth({
    account: {
        accountLinking: {
            enabled: true,
            trustedProviders: ["google", "github"]
        }
    },
});

Manually Linking Accounts

Users already signed in can manually link their account to additional social providers or credential-based accounts.

  • Linking Social Accounts: Use the user.linkSocial method on the client to link a social provider to the user's account.

    await authClient.linkSocial({
        provider: "google", // Provider to link
        callbackURL: "/callback" // Callback URL after linking completes
    });
  • Linking Credential-Based Accounts: To link a credential-based account (e.g., email and password), users can initiate a "forgot password" flow, or you can call the setPassword method on the server.

    await auth.api.setPassword({
        headers: /* headers containing the user's session token */,
        password: /* new password */
    });

setPassword can't be called from the client for security reasons.

Account Unlinking

You can unlink a user account by providing a providerId.

await authClient.unlinkAccount({
    providerId: "google"
});

If the providerId doesn't exist, it will throw an error. Additionally, if the user only has one account, the unlinking process will fail to prevent account lockout.

On this page