You are currently viewing documentation for v1.8 (Beta)

Username

Username plugin

The username plugin is a lightweight plugin that adds username support to the email and password authenticator. This allows users to sign in with their username instead of their email.

Installation

Add Plugin to the server

auth.ts
import { betterAuth } from "better-auth"
import { username } from "better-auth/plugins"

export const auth = betterAuth({
    emailAndPassword: { 
        enabled: true, 
    }, 
    plugins: [ 
        username() 
    ] 
})

Migrate the database

Run the migration or generate the schema to add the necessary fields and tables to the database.

npx auth migrate

See the Schema section to add the fields manually.

Add the client plugin

auth-client.ts
import { createAuthClient } from "better-auth/client"
import { usernameClient } from "better-auth/client/plugins"

export const authClient = createAuthClient({
    plugins: [ 
        usernameClient() 
    ] 
})

Usage

Sign up

To sign up a user with username, you can use the existing signUp.email function provided by the client. The signUp function should take a new username property in the object.

POST/sign-up/email
const { data, error } = await authClient.signUp.email({    email: "email@domain.com", // required    name: "Test User", // required    password: "password1234", // required    username: "test",    displayUsername: "Test User123",});
Parameters
emailstringrequired

The email of the user.

namestringrequired

The name of the user.

passwordstringrequired

The password of the user.

usernamestring

The username of the user.

displayUsernamestring

An optional display username of the user.

If only username is provided, the displayUsername will be set to the pre normalized version of the username. You can see the Username Normalization and Display Username Normalization sections for more details.

Sign in

To sign in a user with username, you can use the signIn.username function provided by the client.

POST/sign-in/username
const { data, error } = await authClient.signIn.username({    username: "test", // required    password: "password1234", // required});
Parameters
usernamestringrequired

The username of the user.

passwordstringrequired

The password of the user.

Update username

To update the username of a user, you can use the updateUser function provided by the client.

If immutableUsername is enabled, users can set their username during sign-up or later if they do not already have a username, but they cannot change it after it has been set.

POST/update-user
const { data, error } = await authClient.updateUser({    username: "new-username",});
Parameters
usernamestring

The username to update.

Check if username is available

To check if a username is available, you can use the isUsernameAvailable function provided by the client.

POST/is-username-available
const { data: response, error } = await authClient.isUsernameAvailable({    username: "new-username", // required});if (response?.available) {    console.log("Username is available");} else {    console.log("Username is not available");}
Parameters
usernamestringrequired

The username to check.

Options

Min Username Length

The minimum length of the username. Default is 3.

auth.ts
import { betterAuth } from "better-auth"
import { username } from "better-auth/plugins"

const auth = betterAuth({
    emailAndPassword: {
        enabled: true,
    },
    plugins: [
        username({
            minUsernameLength: 5
        })
    ]
})

Max Username Length

The maximum length of the username. Default is 30.

auth.ts
import { betterAuth } from "better-auth"
import { username } from "better-auth/plugins"

const auth = betterAuth({
    emailAndPassword: {
        enabled: true,
    },
    plugins: [
        username({
            maxUsernameLength: 100
        })
    ]
})

Username Validator

A function that validates the username. The function should return false if the username is invalid. By default, the username should only contain alphanumeric characters, underscores, and dots.

auth.ts
import { betterAuth } from "better-auth"
import { username } from "better-auth/plugins"

const auth = betterAuth({
    emailAndPassword: {
        enabled: true,
    },
    plugins: [
        username({
            usernameValidator: (username) => {
                if (username === "admin") {
                    return false
                }
                return true
            }
        })
    ]
})

Display Username Validator

A function that validates the display username. The function should return false if the display username is invalid. By default, no validation is applied to display username.

auth.ts
import { betterAuth } from "better-auth"
import { username } from "better-auth/plugins"

const auth = betterAuth({
    emailAndPassword: {
        enabled: true,
    },
    plugins: [
        username({
            displayUsernameValidator: (displayUsername) => {
                // Allow only alphanumeric characters, underscores, and hyphens
                return /^[a-zA-Z0-9_-]+$/.test(displayUsername)
            }
        })
    ]
})

Username Normalization

A function that normalizes the username, or false if you want to disable normalization.

By default, usernames are normalized to lowercase, so "TestUser" and "testuser", for example, are considered the same username. The username field will contain the normalized (lower case) username, while displayUsername will contain the original username.

auth.ts
import { betterAuth } from "better-auth"
import { username } from "better-auth/plugins"

const auth = betterAuth({
    emailAndPassword: {
        enabled: true,
    },
    plugins: [
        username({
            usernameNormalization: (username) => {
                return username.toLowerCase()
                    .replaceAll("0", "o")
                    .replaceAll("3", "e")
                    .replaceAll("4", "a");
            }
        })
    ]
})

Display Username Normalization

A function that normalizes the display username, or false to disable normalization.

By default, display usernames are not normalized. When only username is provided during signup or update, the displayUsername will be set to match the original username value (before normalization). You can also explicitly set a displayUsername which will be preserved as-is. For custom normalization, provide a function that takes the display username as input and returns the normalized version.

auth.ts
import { betterAuth } from "better-auth"
import { username } from "better-auth/plugins"

const auth = betterAuth({
    emailAndPassword: {
        enabled: true,
    },
    plugins: [
        username({
            displayUsernameNormalization: (displayUsername) => displayUsername.toLowerCase(),
        })
    ]
})

Validation Order

By default, username and display username are validated before normalization. You can change this behavior by setting validationOrder to post-normalization.

auth.ts
import { betterAuth } from "better-auth"
import { username } from "better-auth/plugins"

const auth = betterAuth({
    emailAndPassword: {
        enabled: true,
    },
    plugins: [
        username({
            validationOrder: {
                username: "post-normalization",
                displayUsername: "post-normalization",
            }
        })
    ]
})

Disable Display Username

By default, the plugin adds a separate displayUsername field to the user table to store the non-normalized username. If your app doesn't need it, set displayUsername to false. When disabled:

  • the displayUsername field is not added to the user schema (no extra column or migration)
  • displayUsername is never written during sign-up or update
  • the inferred user/session types exclude displayUsername
  • username normalization continues to work as usual
auth.ts
import { betterAuth } from "better-auth"
import { username } from "better-auth/plugins"

const auth = betterAuth({
    emailAndPassword: {
        enabled: true,
    },
    plugins: [
        username({
            displayUsername: false
        })
    ]
})

Pass the same option to the client plugin so client-side inferred types match the server:

auth-client.ts
import { createAuthClient } from "better-auth/client"
import { usernameClient } from "better-auth/client/plugins"

const authClient = createAuthClient({
    plugins: [
        usernameClient({ displayUsername: false })
    ]
})

Immutable Username

By default, users can update their username. Set immutableUsername to true to prevent users from changing their username after it has been set.

Users can still set a username during sign-up, or on a later profile update if they haven't already picked a username. Updating other profile fields remains allowed.

auth.ts
import { betterAuth } from "better-auth"
import { username } from "better-auth/plugins"

const auth = betterAuth({
    emailAndPassword: {
        enabled: true,
    },
    plugins: [
        username({
            immutableUsername: true
        })
    ]
})

Disable Is Username Available

By default, the plugin exposes an endpoint /is-username-available to check if a username is available. You can disable this endpoint by providing disabledPaths option to the better-auth configuration. This is useful if you want to protect usernames from being enumerated.

auth.ts
import { betterAuth } from "better-auth"
import { username } from "better-auth/plugins"

const auth = betterAuth({
    emailAndPassword: {
        enabled: true,
    },
    disabledPaths: ["/is-username-available"],
    plugins: [
        username()
    ]
})

Schema

The plugin requires 2 fields to be added to the user table (the displayUsername field is omitted when displayUsername: false is set):

Table
Field
Type
Attributes
Description
username ?
string
UQ
The username of the user
displayUsername ?
string
-
Non normalized username of the user