Username¶
Lets users claim a unique username and sign in with username + password instead of their email. The username maps to the same credential account created by email/password sign-up.
Install¶
Included in the core install. No extra is required.
The plugin reuses the existing password credential account, so the core email_and_password provider must be enabled for sign-in to work.
Setup¶
import deadbolt as db
from deadbolt.plugins.username import username
auth = db.Auth(
adapter=db.MemoryAdapter(),
secret=SECRET,
email_and_password=db.EmailPassword(enabled=True),
plugins=[username()],
)
Configuration¶
The username() factory takes no parameters.
| Parameter | Type | Default | Description |
|---|---|---|---|
| (none) | The plugin has no configuration options. |
API¶
POST /username/set¶
Sets or changes the current user's username. Normalises the value to lowercase for storage and matching, but keeps the original casing as the display username. Auth: session required.
Request (body JSON):
| Field | Type | Required | Description |
|---|---|---|---|
username |
string | Yes | Desired username. Must match ^[a-z0-9_]{3,32}$ after lowercasing (3–32 chars: a–z, 0–9, underscore). |
Response 200:
The returned username is the display value (original casing).
Errors:
| Status | Code | When |
|---|---|---|
| 401 | unauthorized |
No valid session on the request. |
| 400 | invalid_request |
username field missing or not a non-empty string. |
| 400 | invalid_username |
Normalised value fails the a-z0-9_, 3–32 char pattern. |
| 409 | username_taken |
The username already belongs to another user. |
Example:
curl -X POST https://api.example.com/username/set \
-H "Content-Type: application/json" \
-b "__Host-session=<session-cookie>" \
-d '{"username": "Alice_01"}'
GET /username/available¶
Checks whether a username is both valid and free. Public helper for signup forms. Auth: public.
Request (query):
| Field | Type | Required | Description |
|---|---|---|---|
username |
string | Yes | Candidate username to check. Compared after lowercasing. |
Response 200:
available is true only when the value matches the format pattern and is not already taken.
Errors:
| Status | Code | When |
|---|---|---|
| 400 | invalid_request |
username query parameter missing or empty. |
Example:
POST /sign-in/username¶
Signs in with a username and password, verifying against the user's existing password credential account. On success it creates a session and sets the session cookie. Auth: public.
Request (body JSON):
| Field | Type | Required | Description |
|---|---|---|---|
username |
string | Yes | Username to sign in as. Matched after lowercasing. |
password |
string | Yes | The account password. |
Response 200:
{
"user": {
"id": "usr_123",
"email": "a@b.com",
"email_verified": false,
"name": null,
"image": null,
"created_at": "2026-07-08T00:00:00Z",
"updated_at": "2026-07-08T00:00:00Z"
}
}
A session cookie is set in the response.
Errors:
| Status | Code | When |
|---|---|---|
| 400 | invalid_request |
username or password field missing or empty. |
| 401 | invalid_credentials |
Unknown username, no password credential on the account, or wrong password. |
Example:
curl -X POST https://api.example.com/sign-in/username \
-H "Content-Type: application/json" \
-d '{"username": "alice_01", "password": "hunter2pw"}'
Notes¶
- Case handling: usernames are stored normalised to lowercase (
username) alongside the original casing (display_username). Lookups and availability checks compare the lowercased value, soAlice_01andalice_01collide. - One username per user: setting a username again updates the existing record rather than creating a second one; the previous username stops working immediately.
- Ownership check on set: re-setting your own current username is allowed; the
username_takenconflict only triggers when the row belongs to a differentuser_id. - Timing-safe sign-in: when the username is unknown or the account has no password, the password is still verified against a fixed decoy hash before returning
invalid_credentials, keeping response timing uniform and avoiding username enumeration. - Password rehashing: if the stored hash needs upgrading (
needs_rehash), the password is re-hashed and persisted on successful sign-in. - Shared credential: sign-in verifies against the same password credential account as email/password auth, so passwords stay in sync across both methods.