← back to Norma Platform
login: standard username/password only (remove Google/Apple OAuth); harden /api/auth/login to 400 on non-JSON body
df259fc2cf1b9e2f8e88ab7090a39509ff4b4f4d · 2026-08-05 12:38:34 -0700 · Steve
Files touched
M app/api/auth/login/route.tsM app/login/page.tsx
Diff
commit df259fc2cf1b9e2f8e88ab7090a39509ff4b4f4d
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Aug 5 12:38:34 2026 -0700
login: standard username/password only (remove Google/Apple OAuth); harden /api/auth/login to 400 on non-JSON body
---
app/api/auth/login/route.ts | 12 ++++-
app/login/page.tsx | 122 ++------------------------------------------
2 files changed, 13 insertions(+), 121 deletions(-)
diff --git a/app/api/auth/login/route.ts b/app/api/auth/login/route.ts
index 88ce4cb..190a164 100644
--- a/app/api/auth/login/route.ts
+++ b/app/api/auth/login/route.ts
@@ -37,8 +37,16 @@ export async function POST(request: NextRequest) {
}
try {
- const body = await request.json();
- const { username, password, clientType } = body as {
+ let body: unknown;
+ try {
+ body = await request.json();
+ } catch {
+ return NextResponse.json(
+ { error: 'Invalid request body — expected JSON' },
+ { status: 400 },
+ );
+ }
+ const { username, password, clientType } = (body ?? {}) as {
username?: string;
password?: string;
clientType?: 'retail' | 'trade';
diff --git a/app/login/page.tsx b/app/login/page.tsx
index 53fa475..f1552d8 100644
--- a/app/login/page.tsx
+++ b/app/login/page.tsx
@@ -2,28 +2,7 @@
import { useState, FormEvent, Suspense } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
-import { Loader2, Mail } from 'lucide-react';
-
-/* ─── Google Icon (inline SVG) ──────────────────────────────────────────── */
-function GoogleIcon() {
- return (
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none">
- <path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z" fill="#4285F4" />
- <path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853" />
- <path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18A11.96 11.96 0 0 0 0 12c0 1.94.46 3.77 1.28 5.4l3.56-2.77.01-.54z" fill="#FBBC05" />
- <path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335" />
- </svg>
- );
-}
-
-/* ─── Apple Icon (inline SVG) ───────────────────────────────────────────── */
-function AppleIcon() {
- return (
- <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
- <path d="M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z" />
- </svg>
- );
-}
+import { Loader2 } from 'lucide-react';
/* ─── Login Flow ────────────────────────────────────────────────────────── */
@@ -32,7 +11,6 @@ function LoginFlow() {
const searchParams = useSearchParams();
const returnTo = searchParams.get('returnTo');
- const [showEmailForm, setShowEmailForm] = useState(false);
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
@@ -69,31 +47,6 @@ function LoginFlow() {
}
}
- function handleGoogleLogin() {
- // TODO: wire to Google OAuth
- window.location.href = '/api/auth/google';
- }
-
- function handleAppleLogin() {
- // TODO: wire to Apple OAuth
- window.location.href = '/api/auth/apple';
- }
-
- const btnBase: React.CSSProperties = {
- width: '100%',
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- gap: 10,
- padding: '12px 16px',
- fontSize: 15,
- fontWeight: 600,
- borderRadius: 'var(--radius-md)',
- cursor: 'pointer',
- transition: 'all 0.15s',
- border: '1px solid var(--color-border)',
- };
-
return (
<div className="w-full" style={{ maxWidth: 380 }}>
{/* Logo */}
@@ -125,77 +78,8 @@ function LoginFlow() {
border: '1px solid var(--color-border)',
}}
>
- {/* Social buttons */}
- <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
- {/* Google */}
- <button
- type="button"
- onClick={handleGoogleLogin}
- style={{
- ...btnBase,
- backgroundColor: '#fff',
- color: '#1f1f1f',
- border: '1px solid #dadce0',
- }}
- onMouseEnter={(e) => { e.currentTarget.style.backgroundColor = '#f7f8f8'; }}
- onMouseLeave={(e) => { e.currentTarget.style.backgroundColor = '#fff'; }}
- >
- <GoogleIcon />
- Continue with Google
- </button>
-
- {/* Apple — hidden until /api/auth/apple is wired. The handler still
- exists for when Apple OAuth ships. */}
- {process.env.NEXT_PUBLIC_APPLE_OAUTH_ENABLED === 'true' && (
- <button
- type="button"
- onClick={handleAppleLogin}
- style={{
- ...btnBase,
- backgroundColor: '#000',
- color: '#fff',
- border: '1px solid #000',
- }}
- onMouseEnter={(e) => { e.currentTarget.style.backgroundColor = '#1a1a1a'; }}
- onMouseLeave={(e) => { e.currentTarget.style.backgroundColor = '#000'; }}
- >
- <AppleIcon />
- Continue with Apple
- </button>
- )}
- </div>
-
- {/* Divider */}
- <div
- style={{
- display: 'flex',
- alignItems: 'center',
- gap: 12,
- margin: '20px 0',
- }}
- >
- <div style={{ flex: 1, height: 1, backgroundColor: 'var(--color-border)' }} />
- <span style={{ fontSize: 12, color: 'var(--color-text-muted)', fontWeight: 500 }}>or</span>
- <div style={{ flex: 1, height: 1, backgroundColor: 'var(--color-border)' }} />
- </div>
-
- {/* Email sign in */}
- {!showEmailForm ? (
- <button
- type="button"
- onClick={() => setShowEmailForm(true)}
- style={{
- ...btnBase,
- backgroundColor: 'transparent',
- color: 'var(--color-text)',
- }}
- onMouseEnter={(e) => { e.currentTarget.style.backgroundColor = 'var(--color-surface-el)'; }}
- onMouseLeave={(e) => { e.currentTarget.style.backgroundColor = 'transparent'; }}
- >
- <Mail size={18} />
- Continue with Email
- </button>
- ) : (
+ {/* Standard username / password login only — OAuth (Google/Apple) removed per request */}
+ {(
<form onSubmit={handleEmailLogin} noValidate style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
{error && (
<div
← 19d20a6 SECURITY incident 2026-07-29: agent-base fail-closed on empt
·
back to Norma Platform
·
auto-save: 2026-08-05T13:12:15 (2 files) — scripts/test-inst d9cb4a2 →