feat(auth): remove center api key from frontend login flow

This commit is contained in:
nav
2026-05-14 14:17:12 +00:00
parent cfaa1cb657
commit 4f28f102e0
5 changed files with 20 additions and 25 deletions

View File

@@ -13,15 +13,15 @@ export function AuthProvider({ children }: PropsWithChildren) {
() => ({ () => ({
session, session,
isAuthed: !!session, isAuthed: !!session,
login: async (centerApiBase: string, centerApiKey: string, email: string, password: string) => { login: async (centerApiBase: string, email: string, password: string) => {
const next = await loginCenter(centerApiBase, centerApiKey, { email, password }) const next = await loginCenter(centerApiBase, { email, password })
setAuthSession(next) setAuthSession(next)
setSession(next) setSession(next)
}, },
logout: async () => { logout: async () => {
if (session?.refreshToken) { if (session?.refreshToken) {
try { try {
await logoutCenter(session.centerApiBase, session.centerApiKey, session.refreshToken) await logoutCenter(session.centerApiBase, session.refreshToken)
} catch { } catch {
// noop // noop
} }
@@ -33,12 +33,13 @@ export function AuthProvider({ children }: PropsWithChildren) {
if (!session) return null if (!session) return null
if (!isAccessTokenStale(session.accessToken)) return session.accessToken if (!isAccessTokenStale(session.accessToken)) return session.accessToken
const refreshed = await refreshCenter(session.centerApiBase, session.centerApiKey, session.refreshToken) const refreshed = await refreshCenter(session.centerApiBase, session.refreshToken)
const next: AuthSession = { const next: AuthSession = {
...session, ...session,
accessToken: refreshed.accessToken, accessToken: refreshed.accessToken,
refreshToken: refreshed.refreshToken, refreshToken: refreshed.refreshToken,
tokenType: refreshed.tokenType, tokenType: refreshed.tokenType,
expiresIn: refreshed.expiresIn,
} }
setAuthSession(next) setAuthSession(next)
setSession(next) setSession(next)

View File

@@ -4,7 +4,7 @@ import type { AuthSession } from '../lib/auth-storage'
export type AuthContextValue = { export type AuthContextValue = {
session: AuthSession | null session: AuthSession | null
isAuthed: boolean isAuthed: boolean
login: (centerApiBase: string, centerApiKey: string, email: string, password: string) => Promise<void> login: (centerApiBase: string, email: string, password: string) => Promise<void>
logout: () => Promise<void> logout: () => Promise<void>
ensureFreshToken: () => Promise<string | null> ensureFreshToken: () => Promise<string | null>
} }

View File

@@ -1,9 +1,9 @@
export type AuthSession = { export type AuthSession = {
centerApiBase: string centerApiBase: string
centerApiKey: string
accessToken: string accessToken: string
refreshToken: string refreshToken: string
tokenType: string tokenType: string
expiresIn?: number
user: { user: {
id: string id: string
email: string email: string
@@ -18,6 +18,7 @@ export type AuthSession = {
guildNodeId: string guildNodeId: string
token: string token: string
tokenType: string tokenType: string
expiresIn?: number
}> }>
} }

View File

@@ -4,22 +4,23 @@ import type { AuthSession } from './auth-storage'
export type LoginPayload = { email: string; password: string } export type LoginPayload = { email: string; password: string }
type LoginResponse = { type LoginResponse = {
centerApiBase: string
accessToken: string accessToken: string
refreshToken: string refreshToken: string
tokenType: string tokenType: string
expiresIn?: number
user: { id: string; email: string } user: { id: string; email: string }
guilds: Array<{ nodeId: string; name: string; endpoint: string; status: 'active' | 'offline' | 'revoked' }> guilds: Array<{ nodeId: string; name: string; endpoint: string; status: 'active' | 'offline' | 'revoked' }>
guildAccessTokens: Array<{ guildNodeId: string; token: string; tokenType: string }> guildAccessTokens: Array<{ guildNodeId: string; token: string; tokenType: string; expiresIn?: number }>
} }
type RefreshResponse = { type RefreshResponse = {
accessToken: string accessToken: string
refreshToken: string refreshToken: string
tokenType: string tokenType: string
expiresIn?: number
} }
function centerClient(centerApiBase: string, centerApiKey: string) { function centerClient(centerApiBase: string) {
const client = axios.create({ const client = axios.create({
baseURL: centerApiBase, baseURL: centerApiBase,
timeout: 10000, timeout: 10000,
@@ -27,7 +28,6 @@ function centerClient(centerApiBase: string, centerApiKey: string) {
client.interceptors.request.use((request) => { client.interceptors.request.use((request) => {
const requestId = crypto.randomUUID() const requestId = crypto.randomUUID()
request.headers['x-api-key'] = centerApiKey
request.headers['x-request-id'] = requestId request.headers['x-request-id'] = requestId
request.headers['x-client-name'] = 'fabric-frontend' request.headers['x-client-name'] = 'fabric-frontend'
return request return request
@@ -36,16 +36,16 @@ function centerClient(centerApiBase: string, centerApiKey: string) {
return client return client
} }
export async function loginCenter(centerApiBase: string, centerApiKey: string, payload: LoginPayload): Promise<AuthSession> { export async function loginCenter(centerApiBase: string, payload: LoginPayload): Promise<AuthSession> {
const res = await centerClient(centerApiBase, centerApiKey).post<LoginResponse>('/auth/login', payload) const res = await centerClient(centerApiBase).post<LoginResponse>('/auth/login', payload)
return { ...res.data, centerApiBase, centerApiKey } return { ...res.data, centerApiBase }
} }
export async function refreshCenter(centerApiBase: string, centerApiKey: string, refreshToken: string): Promise<RefreshResponse> { export async function refreshCenter(centerApiBase: string, refreshToken: string): Promise<RefreshResponse> {
const res = await centerClient(centerApiBase, centerApiKey).post<RefreshResponse>('/auth/refresh', { refreshToken }) const res = await centerClient(centerApiBase).post<RefreshResponse>('/auth/refresh', { refreshToken })
return res.data return res.data
} }
export async function logoutCenter(centerApiBase: string, centerApiKey: string, refreshToken: string): Promise<void> { export async function logoutCenter(centerApiBase: string, refreshToken: string): Promise<void> {
await centerClient(centerApiBase, centerApiKey).post('/auth/logout', { refreshToken }) await centerClient(centerApiBase).post('/auth/logout', { refreshToken })
} }

View File

@@ -7,7 +7,6 @@ export default function LoginPage() {
const navigate = useNavigate() const navigate = useNavigate()
const { login, isAuthed, session } = useAuth() const { login, isAuthed, session } = useAuth()
const [centerApiBase, setCenterApiBase] = useState('http://localhost:7001/api') const [centerApiBase, setCenterApiBase] = useState('http://localhost:7001/api')
const [centerApiKey, setCenterApiKey] = useState('')
const [email, setEmail] = useState('') const [email, setEmail] = useState('')
const [password, setPassword] = useState('') const [password, setPassword] = useState('')
const [error, setError] = useState('') const [error, setError] = useState('')
@@ -16,7 +15,7 @@ export default function LoginPage() {
e.preventDefault() e.preventDefault()
setError('') setError('')
try { try {
await login(centerApiBase.trim(), centerApiKey.trim(), email, password) await login(centerApiBase.trim(), email, password)
navigate('/workspace') navigate('/workspace')
} catch { } catch {
setError('Login failed. Please check your email and password.') setError('Login failed. Please check your email and password.')
@@ -34,12 +33,6 @@ export default function LoginPage() {
onChange={(e) => setCenterApiBase(e.target.value)} onChange={(e) => setCenterApiBase(e.target.value)}
placeholder="Center API Base (e.g. http://localhost:7001/api)" placeholder="Center API Base (e.g. http://localhost:7001/api)"
/> />
<input
className="input"
value={centerApiKey}
onChange={(e) => setCenterApiKey(e.target.value)}
placeholder="Center API Key"
/>
<input className="input" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" type="email" /> <input className="input" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" type="email" />
<input <input
className="input" className="input"