Configure Authentication
Username Authentication
Sign in with a username and password instead of an email address.
Username authentication lets your users sign up and sign in with a username and password instead of an email address. This is ideal for apps where a handle or gamertag is the primary identity -- gaming platforms, social networks, internal tools, and developer communities.
You can use username auth on its own or combine it with email/password so users have the flexibility to sign in either way.
When to Use Username Auth
| Scenario | Recommended? | Why |
|---|---|---|
| Gaming platforms | Yes | Players identify by gamertag or handle |
| Social apps | Yes | Usernames are the public identity |
| Internal tools | Yes | Employees use a short handle instead of a full email |
| Developer platforms | Yes | Developers expect username-based accounts (like GitHub) |
| E-commerce | No | Email is needed for order confirmations and receipts |
| Enterprise SaaS | No | Email is the standard identity in business contexts |
Enabling Username Auth
From the Dashboard
- Open your project in the Banata Auth dashboard.
- Go to Authentication > Methods.
- Toggle on Username & Password.
- Click Save.
That's it -- your project now accepts username-based sign-ups and sign-ins.
From the SDK
You can also enable username auth programmatically:
await banata.configuration.saveDashboardConfig({
authMethods: {
username: true,
},
});No email provider is required for username-only auth, since there are no verification emails or password reset links to send.
Username Validation Rules
Banata Auth enforces the following rules on all usernames:
| Rule | Value |
|---|---|
| Minimum length | 3 characters |
| Maximum length | 32 characters |
| Must start with | A letter (a-z) |
| Allowed characters | Lowercase letters (a-z), numbers (0-9), underscores (_), hyphens (-) |
| Case sensitivity | Case-insensitive -- JaneDoe and janedoe are treated as the same username |
| Uniqueness | Globally unique -- no two users in your project can share a username |
Usernames are normalized to lowercase before storage. If a user signs up as JaneDoe, it is stored and compared as janedoe.
Client API
The auth client exposes three methods for username auth:
authClient.signUp.username()-- Create a new account with a username and password.authClient.signIn.username()-- Sign in to an existing account.authClient.username.checkAvailability()-- Check whether a username is already taken.
Sign Up
import { authClient } from "@/lib/auth-client";
const { data, error } = await authClient.signUp.username({
username: "janedoe",
password: "securePassword123",
name: "Jane Doe", // optional display name
email: "jane@example.com", // optional -- only relevant if email auth is also enabled
});
if (error) {
console.error(error.message);
} else {
console.log(data.user); // { id, username, name, ... }
console.log(data.session); // { id, token, expiresAt, ... }
}Sign In
const { data, error } = await authClient.signIn.username({
username: "janedoe",
password: "securePassword123",
});
if (error) {
console.error(error.message);
} else {
window.location.href = "/dashboard";
}Check Availability
Before the user submits the sign-up form, you can check if their desired username is available:
const { data } = await authClient.username.checkAvailability({
username: "janedoe",
});
console.log(data.available); // true or falseThis is useful for showing real-time feedback as the user types.
Complete Sign-Up Form with Availability Checking
Here is a full React example that debounces the availability check and disables the submit button when the username is taken:
"use client";
import { authClient } from "@/lib/auth-client";
import { useState, useEffect } from "react";
export default function UsernameSignUpPage() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [name, setName] = useState("");
const [error, setError] = useState("");
const [usernameStatus, setUsernameStatus] = useState<
"idle" | "checking" | "available" | "taken"
>("idle");
// Debounced availability check
useEffect(() => {
if (username.length < 3) {
setUsernameStatus("idle");
return;
}
setUsernameStatus("checking");
const timer = setTimeout(async () => {
const { data } = await authClient.username.checkAvailability({
username,
});
setUsernameStatus(data?.available ? "available" : "taken");
}, 500);
return () => clearTimeout(timer);
}, [username]);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setError("");
const { data, error } = await authClient.signUp.username({
username,
password,
name: name || undefined,
});
if (error) {
setError(error.message ?? "Sign-up failed");
} else {
window.location.href = "/dashboard";
}
}
return (
<form onSubmit={handleSubmit}>
<h1>Create Account</h1>
<div>
<label htmlFor="username">Username</label>
<input
id="username"
type="text"
placeholder="janedoe"
value={username}
onChange={(e) => setUsername(e.target.value.toLowerCase())}
minLength={3}
maxLength={32}
pattern="[a-z][a-z0-9_-]*"
required
/>
{usernameStatus === "checking" && <span>Checking...</span>}
{usernameStatus === "available" && (
<span style={{ color: "green" }}>Available</span>
)}
{usernameStatus === "taken" && (
<span style={{ color: "red" }}>Already taken</span>
)}
</div>
<div>
<label htmlFor="password">Password</label>
<input
id="password"
type="password"
placeholder="Minimum 8 characters"
value={password}
onChange={(e) => setPassword(e.target.value)}
minLength={8}
required
/>
</div>
<div>
<label htmlFor="name">Display Name (optional)</label>
<input
id="name"
type="text"
placeholder="Jane Doe"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
{error && <p style={{ color: "red" }}>{error}</p>}
<button
type="submit"
disabled={usernameStatus === "taken" || usernameStatus === "checking"}
>
Create Account
</button>
</form>
);
}Sign-In Form Example
A minimal sign-in form for username auth:
"use client";
import { authClient } from "@/lib/auth-client";
import { useState } from "react";
export default function UsernameSignInPage() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setError("");
const { data, error } = await authClient.signIn.username({
username,
password,
});
if (error) {
setError(error.message ?? "Sign-in failed");
} else {
window.location.href = "/dashboard";
}
}
return (
<form onSubmit={handleSubmit}>
<h1>Sign In</h1>
<input
type="text"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
required
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
{error && <p style={{ color: "red" }}>{error}</p>}
<button type="submit">Sign In</button>
</form>
);
}Combining Username with Email/Password
You can enable both authentication methods so users can sign in with either a username or an email address:
- In the dashboard, toggle on both Username & Password and Email & Password under Authentication > Methods.
- Or via the SDK:
await banata.configuration.saveDashboardConfig({
authMethods: {
username: true,
emailPassword: true,
},
});When both are enabled, users can optionally provide an email during sign-up. If they do, they can sign in with either credential:
// Sign in with username
await authClient.signIn.username({
username: "janedoe",
password: "securePassword123",
});
// Sign in with email (if the user provided one at sign-up)
await authClient.signIn.email({
email: "jane@example.com",
password: "securePassword123",
});Both methods authenticate the same user and create the same session.
Password Reset Without Email
If a user signed up with only a username and no email, the standard "forgot password" email flow is not available. You have a few options:
- Require an email at sign-up. Even in a username-first flow, you can make email mandatory so password reset is always available.
- Admin reset. Use the server-side SDK to reset a user's password directly:
await banata.users.updateUser({
userId: "usr_01HXYZ...",
password: "newTemporaryPassword",
});- Prompt the user to add an email. If the user's account has no email, prompt them to add one before they can use self-service password reset.
Tip: If your app targets an audience that may forget passwords often (like younger users in gaming), requiring an email at sign-up is strongly recommended even when username is the primary identifier.
Rate Limits
Username auth endpoints are rate-limited to prevent brute-force attacks:
| Endpoint | Limit |
|---|---|
Sign In (signIn.username) | 30 requests per minute |
Sign Up (signUp.username) | 10 requests per minute |
Check Availability (username.checkAvailability) | 60 requests per minute |
When a limit is exceeded, the server returns a 429 Too Many Requests response. Your client should display a message like "Too many attempts -- please try again in a moment."
Error Reference
| Error | HTTP Status | When It Occurs |
|---|---|---|
AuthenticationError | 401 | Invalid username or password on sign-in |
ForbiddenError | 403 | The account has been banned |
ConflictError | 409 | The username is already taken on sign-up |
ValidationError | 422 | Invalid username format, username too short/long, or password too short |
RateLimitError | 429 | Too many requests to the endpoint |
You can handle these by checking the error.status code:
const { error } = await authClient.signUp.username({
username: "janedoe",
password: "securePassword123",
});
if (error?.status === 409) {
// Username is taken -- suggest the user pick a different one
}
if (error?.status === 422) {
// Validation failed -- show the error message to the user
}Troubleshooting
"Invalid username format"
The username does not meet the validation rules. Make sure it:
- Is between 3 and 32 characters long
- Starts with a letter (
a-z) - Contains only lowercase letters, numbers, underscores, and hyphens
"Username already taken"
Another user has already claimed that username. Remember that usernames are case-insensitive, so JaneDoe and janedoe count as the same name. Prompt the user to choose a different one, or use checkAvailability to give feedback before they submit.
"Cannot reset password"
The user signed up with a username only and has no email on their account. Use the admin SDK to reset their password, or ask them to add an email address first.
Sign-in returns 401 but the username exists
The most likely cause is an incorrect password. The error message is intentionally vague ("Invalid username or password") to prevent username enumeration. Double-check that the password is correct and that Caps Lock is not enabled.
Next Steps
- Email & Password -- Add traditional email-based authentication alongside username auth
- Social OAuth -- Let users link a Google, GitHub, or other social account
- Anonymous Auth -- Allow guest access with optional upgrade to a full account
- Roles & Permissions -- Control what authenticated users can access