Files
HarborForge.Frontend/src/hooks/useAuth.ts
hzhang 8f8d6d5465 feat(auth): OIDC login UI + binding management + OIDC-only mode
- useAuthConfig fetches public /auth/config; LoginPage hides the
  password form when oidc_only and shows an SSO button when enabled.
- /oidc/callback route applies the returned JWT (sign-in) or shows the
  link result; oidc_error surfaced on LoginPage.
- UsersPage: hides password fields in OIDC-only mode; admin OIDC
  bind/unbind UI per user. Sidebar self-service "Link OIDC account"
  (non-OIDC_ONLY).
- Dockerfile ARG/ENV HARBORFORGE_OIDC_ONLY.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 20:22:14 +01:00

60 lines
1.6 KiB
TypeScript

import { useState, useEffect, useCallback } from 'react'
import api from '@/services/api'
import type { User } from '@/types'
interface AuthState {
user: User | null
token: string | null
loading: boolean
}
export function useAuth() {
const [state, setState] = useState<AuthState>({
user: null,
token: localStorage.getItem('token'),
loading: true,
})
const fetchUser = useCallback(async () => {
const token = localStorage.getItem('token')
if (!token) {
setState({ user: null, token: null, loading: false })
return
}
try {
const { data } = await api.get<User>('/auth/me')
setState({ user: data, token, loading: false })
} catch {
localStorage.removeItem('token')
setState({ user: null, token: null, loading: false })
}
}, [])
useEffect(() => { fetchUser() }, [fetchUser])
const login = async (username: string, password: string) => {
const form = new URLSearchParams()
form.append('username', username)
form.append('password', password)
const { data } = await api.post('/auth/token', form, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
})
localStorage.setItem('token', data.access_token)
setState((s) => ({ ...s, token: data.access_token }))
await fetchUser()
}
const loginWithToken = async (token: string) => {
localStorage.setItem('token', token)
setState((s) => ({ ...s, token }))
await fetchUser()
}
const logout = () => {
localStorage.removeItem('token')
setState({ user: null, token: null, loading: false })
}
return { ...state, login, loginWithToken, logout }
}