Identity & Access Management
This guide explains how to manage user authentication, secure routes, and handle password recovery using the generated LoginService and the useAuth hook.
Authenticating a User
To authenticate a user, use the LoginService.loginAccessToken method within a TanStack Query mutation. This method handles the OAuth2-compatible form data login.
// From frontend/src/hooks/useAuth.ts
import { LoginService, type Body_login_login_access_token as AccessToken } from "@/client"
const login = async (data: AccessToken) => {
const response = await LoginService.loginAccessToken({
formData: data,
})
// Store the token for future requests
localStorage.setItem("access_token", response.access_token)
}
// Usage in a component via useMutation
const loginMutation = useMutation({
mutationFn: login,
onSuccess: () => {
navigate({ to: "/" })
},
onError: handleError.bind(showErrorToast),
})
The loginAccessToken method expects formData containing the username and password, as defined by the LoginLoginAccessTokenData interface in the generated SDK.
Configuring Global Token Injection
The project uses a global OpenAPI configuration to automatically attach the bearer token to every outgoing request. This is configured once in the application entry point.
// From frontend/src/main.tsx
import { OpenAPI } from "./client"
OpenAPI.BASE = import.meta.env.VITE_API_URL
OpenAPI.TOKEN = async () => {
return localStorage.getItem("access_token") || ""
}
By setting OpenAPI.TOKEN to a function that retrieves the token from localStorage, the generated SDK methods (like UsersService.readUserMe) will automatically include the Authorization: Bearer <token> header.
Protecting Private Routes
Routes are protected using the beforeLoad hook in TanStack Router. You can enforce authentication globally for a group of routes by wrapping them in a layout route.
// From frontend/src/routes/_layout.tsx
import { createFileRoute, redirect } from "@tanstack/react-router"
import { isLoggedIn } from "@/hooks/useAuth"
export const Route = createFileRoute("/_layout")({
component: Layout,
beforeLoad: async () => {
if (!isLoggedIn()) {
throw redirect({
to: "/login",
})
}
},
})
The isLoggedIn helper simply checks for the existence of the token:
// From frontend/src/hooks/useAuth.ts
export const isLoggedIn = () => {
return localStorage.getItem("access_token") !== null
}
Implementing Password Recovery
Password recovery is handled by LoginService.recoverPassword. This workflow typically involves sending a recovery email to the user.
// From frontend/src/routes/recover-password.tsx
import { LoginService } from "@/client"
const recoverPassword = async (data: { email: string }) => {
await LoginService.recoverPassword({
email: data.email,
})
}
const mutation = useMutation({
mutationFn: recoverPassword,
onSuccess: () => {
showSuccessToast("Password recovery email sent successfully")
},
onError: handleError.bind(showErrorToast),
})
For the actual password reset (after the user clicks the link in their email), use LoginService.resetPassword, which accepts a requestBody containing the new password and the token received from the email.
Troubleshooting: Session Expiration
If a request fails with a 401 Unauthorized or 403 Forbidden error (e.g., the token expired), the application is configured to automatically clear the local session and redirect the user to the login page.
This is handled globally via the TanStack Query QueryCache and MutationCache in main.tsx:
// From frontend/src/main.tsx
import { ApiError } from "./client"
const handleApiError = (error: Error) => {
if (error instanceof ApiError && [401, 403].includes(error.status)) {
localStorage.removeItem("access_token")
window.location.href = "/login"
}
}
const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: handleApiError,
}),
mutationCache: new MutationCache({
onError: handleApiError,
}),
})
Common Issues
- Token Not Sent: Ensure
OpenAPI.TOKENis correctly configured inmain.tsx. If you manually clearlocalStoragewithout using thelogoutfunction, theisLoggedIn()check might get out of sync until the next page reload. - Redirect Loops: Ensure that the
/loginroute itself is not protected by the_layout.tsxor any otherbeforeLoadhook that requires authentication. - Form Data Format:
LoginService.loginAccessTokenrequiresapplication/x-www-form-urlencodeddata. The generated SDK handles this automatically as long as you pass the data into theformDataproperty.