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:
33
control_center/src/core/auth/token-bridge.js
Normal file
33
control_center/src/core/auth/token-bridge.js
Normal file
@@ -0,0 +1,33 @@
|
||||
// Bridge between AuthContext (owner of the access token) and api-client
|
||||
// (needs it on every request). AuthContext registers getters/setters here
|
||||
// on mount; api-client reads them. Avoids circular imports + lets us
|
||||
// de-duplicate concurrent 401 refreshes via a shared in-flight promise.
|
||||
|
||||
let getAccessToken = () => null
|
||||
let runRefresh = async () => null
|
||||
let onUnauthenticated = () => {}
|
||||
|
||||
let refreshInFlight = null
|
||||
|
||||
export const registerAuthBridge = ({ getAccessToken: g, runRefresh: r, onUnauthenticated: u }) => {
|
||||
getAccessToken = g
|
||||
runRefresh = r
|
||||
onUnauthenticated = u
|
||||
}
|
||||
|
||||
export const readAccessToken = () => getAccessToken()
|
||||
|
||||
export const refreshAccessToken = async () => {
|
||||
if (!refreshInFlight) {
|
||||
refreshInFlight = (async () => {
|
||||
try {
|
||||
return await runRefresh()
|
||||
} finally {
|
||||
refreshInFlight = null
|
||||
}
|
||||
})()
|
||||
}
|
||||
return refreshInFlight
|
||||
}
|
||||
|
||||
export const notifyUnauthenticated = () => onUnauthenticated()
|
||||
Reference in New Issue
Block a user