Phase 3.4: control_center self-managed auth cutover
Replaces Firebase Auth with the new JWT + httpOnly-cookie refresh flow. Smoke-tested end-to-end via curl (login → /me → refresh rotation → logout). - Remove firebase dep + firebase.js - New token-bridge decouples api-client from AuthContext and de-dupes concurrent 401 refreshes - AuthContext: in-memory access token (useRef), bootstrap via /internal/auth/refresh, login/logout/refresh methods - api-client: withCredentials, Bearer attach, auto-retry once on 401 - LoginPage: handle INVALID_CREDENTIALS / ACCOUNT_LOCKED / VALIDATION_ERROR - Layout: self-service "Ganti password" form - UsersPage: initial password field on create + per-row admin-forced reset - .env / .env.example: drop VITE_FIREBASE_* vars - backend/CLAUDE.md + control_center/CLAUDE.md: describe new auth (were stale on Firebase) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,21 @@ import { useState, useEffect } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useAuth } from '../../core/auth/AuthContext'
|
||||
|
||||
const messageForError = (err) => {
|
||||
const code = err?.response?.data?.error?.code
|
||||
const msg = err?.response?.data?.error?.message
|
||||
switch (code) {
|
||||
case 'ACCOUNT_LOCKED':
|
||||
return msg || 'Akun terkunci sementara. Coba lagi nanti.'
|
||||
case 'INVALID_CREDENTIALS':
|
||||
return 'Email atau password salah.'
|
||||
case 'VALIDATION_ERROR':
|
||||
return 'Email dan password wajib diisi.'
|
||||
default:
|
||||
return 'Gagal masuk. Coba lagi.'
|
||||
}
|
||||
}
|
||||
|
||||
export default function LoginPage() {
|
||||
const { user, loading: authLoading, login } = useAuth()
|
||||
const navigate = useNavigate()
|
||||
@@ -20,12 +35,14 @@ export default function LoginPage() {
|
||||
setLoading(true)
|
||||
try {
|
||||
await login(email, password)
|
||||
} catch {
|
||||
setError('Email atau password salah.')
|
||||
} catch (err) {
|
||||
setError(messageForError(err))
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (authLoading) return <div style={{ padding: 24 }}>Loading...</div>
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: 360, margin: '100px auto', padding: 24 }}>
|
||||
<h1>Halo Bestie</h1>
|
||||
|
||||
Reference in New Issue
Block a user