[object Object]

← 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

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 →