Normalize local usernames to lowercase

Normalize local account usernames by trimming and lowercasing across the stack. Added a Prisma migration to lowercase existing plex_username and rewrite local plex_id values for non-deleted accounts. Updated LocalAuthProvider, admin login route, and setup completion to use normalized usernames when looking up, creating, and storing users (including plexId `local-{username}`). Added/updated tests to assert case-insensitive lookups, storage of lowercased usernames/plexIds, and duplicate username rejection.
This commit is contained in:
kikootwo
2026-03-04 12:47:09 -05:00
parent d0ce485bdc
commit 441724c378
5 changed files with 87 additions and 8 deletions
+8 -5
View File
@@ -54,10 +54,12 @@ export class LocalAuthProvider implements IAuthProvider {
return { success: false, error: 'Username and password required' };
}
const normalizedUsername = username.trim().toLowerCase();
// Find user (exclude soft-deleted users)
const user = await prisma.user.findFirst({
where: {
plexUsername: username,
plexUsername: normalizedUsername,
authProvider: 'local',
deletedAt: null, // Exclude soft-deleted users
},
@@ -144,9 +146,10 @@ export class LocalAuthProvider implements IAuthProvider {
async register(params: RegisterParams): Promise<AuthResult> {
try {
const { username, password } = params;
const normalizedUsername = username?.trim().toLowerCase();
// Validate
if (!username || username.length < 3) {
if (!normalizedUsername || normalizedUsername.length < 3) {
return { success: false, error: 'Username must be at least 3 characters' };
}
@@ -167,7 +170,7 @@ export class LocalAuthProvider implements IAuthProvider {
// Check username uniqueness (only among non-deleted users)
const existing = await prisma.user.findFirst({
where: {
plexUsername: username,
plexUsername: normalizedUsername,
authProvider: 'local',
deletedAt: null, // Allow reuse of usernames from deleted accounts
},
@@ -194,8 +197,8 @@ export class LocalAuthProvider implements IAuthProvider {
// Create user
const user = await prisma.user.create({
data: {
plexId: `local-${username}`,
plexUsername: username,
plexId: `local-${normalizedUsername}`,
plexUsername: normalizedUsername,
authToken: encryptedHash,
authProvider: 'local',
role: isFirstUser ? 'admin' : 'user',