← back to Norma
Fix tier_credentials schema drift in auth + impersonate routes
86dc7d4cbd182cbb338101e4983ff9d0f677a150 · 2026-07-11 08:55:30 -0700 · Steve Abrams
login: remove dead is_active block + swap row.full_name -> display_name.
session: drop full_name/email from SELECT (email inferred from username).
impersonate: drop is_active from SELECT + remove disabled-account guard.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
M app/api/admin/impersonate/route.tsM app/api/auth/login/route.tsM app/api/auth/session/route.ts
Diff
commit 86dc7d4cbd182cbb338101e4983ff9d0f677a150
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sat Jul 11 08:55:30 2026 -0700
Fix tier_credentials schema drift in auth + impersonate routes
login: remove dead is_active block + swap row.full_name -> display_name.
session: drop full_name/email from SELECT (email inferred from username).
impersonate: drop is_active from SELECT + remove disabled-account guard.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
app/api/admin/impersonate/route.ts | 7 ++-----
app/api/auth/login/route.ts | 15 ++-------------
app/api/auth/session/route.ts | 13 +++++--------
3 files changed, 9 insertions(+), 26 deletions(-)
diff --git a/app/api/admin/impersonate/route.ts b/app/api/admin/impersonate/route.ts
index 2362dab..58bbe6e 100644
--- a/app/api/admin/impersonate/route.ts
+++ b/app/api/admin/impersonate/route.ts
@@ -74,17 +74,14 @@ export async function POST(request: NextRequest) {
return NextResponse.json({ error: 'Already that user' }, { status: 400 });
}
- const result = await query<{ username: string; role: string; org_id: string | null; is_active: boolean }>(
- 'SELECT username, role, org_id, is_active FROM tier_credentials WHERE username = $1',
+ const result = await query<{ username: string; role: string; org_id: string | null }>(
+ 'SELECT username, role, org_id FROM tier_credentials WHERE username = $1',
[targetUsername],
);
if (result.rows.length === 0) {
return NextResponse.json({ error: 'User not found' }, { status: 404 });
}
const target = result.rows[0];
- if (target.is_active === false) {
- return NextResponse.json({ error: 'Cannot impersonate a disabled account' }, { status: 403 });
- }
// Preserve admin's existing session in the imp-by cookie
const cookies = parseCookies(request);
diff --git a/app/api/auth/login/route.ts b/app/api/auth/login/route.ts
index b461ec6..88ce4cb 100644
--- a/app/api/auth/login/route.ts
+++ b/app/api/auth/login/route.ts
@@ -73,17 +73,6 @@ export async function POST(request: NextRequest) {
return invalidCredentials(ip);
}
- // Block deactivated accounts AFTER password check so we don't leak whether
- // a given username exists + is disabled vs simply invalid.
- if (row.is_active === false) {
- return NextResponse.json(
- { error: 'Account disabled. Contact an admin.' },
- { status: 403 },
- );
- }
-
- // (last_login_at bump removed — column not present in this schema)
-
// Auto-upgrade SHA-256 hashes to bcrypt on successful login
if (needsRehash) {
const bcryptHash = await hashPassword(password);
@@ -111,8 +100,8 @@ export async function POST(request: NextRequest) {
success: true,
role: row.role,
orgId: row.org_id,
- displayName: row.full_name || row.display_name,
- fullName: row.full_name,
+ displayName: row.display_name,
+ fullName: row.display_name,
clientType: clientType || row.client_type || null,
isInteriorDesigner: clientType === 'trade',
});
diff --git a/app/api/auth/session/route.ts b/app/api/auth/session/route.ts
index d302985..c0c6fb2 100644
--- a/app/api/auth/session/route.ts
+++ b/app/api/auth/session/route.ts
@@ -10,26 +10,23 @@ export async function GET(request: NextRequest) {
// who's logged in (header chrome, Gmail CRM, audit log, etc.)
let displayName: string | null = null;
let clientType: string | null = null;
- let fullName: string | null = null;
let email: string | null = null;
try {
const res = await query(
- 'SELECT display_name, client_type, full_name, email FROM tier_credentials WHERE username = $1',
+ 'SELECT display_name, client_type FROM tier_credentials WHERE username = $1',
[session.username],
);
if (res.rows.length > 0) {
displayName = res.rows[0].display_name;
clientType = res.rows[0].client_type;
- fullName = res.rows[0].full_name;
- email = res.rows[0].email;
}
} catch {
// Non-fatal — proceed without display name
}
- // Fallback: if `email` column wasn't populated, infer from the username
- // when it's already an email-shaped string (e.g. 'info@studentdebtcrisis.org').
- if (!email && session.username.includes('@')) {
+ // Infer email from the username when it's already an email-shaped string
+ // (e.g. 'info@studentdebtcrisis.org') — tier_credentials has no email column.
+ if (session.username.includes('@')) {
email = session.username;
}
@@ -39,7 +36,7 @@ export async function GET(request: NextRequest) {
role: session.role,
orgId: session.orgId,
displayName,
- fullName,
+ fullName: displayName,
email,
clientType,
isInteriorDesigner: clientType === 'trade',
← 9ba27ad Fix tier_credentials schema drift in settings CRUD routes
·
back to Norma
·
Fix tier_credentials schema drift in users + gmail routes b184c10 →