Skip to main content

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.TOKEN is correctly configured in main.tsx. If you manually clear localStorage without using the logout function, the isLoggedIn() check might get out of sync until the next page reload.
  • Redirect Loops: Ensure that the /login route itself is not protected by the _layout.tsx or any other beforeLoad hook that requires authentication.
  • Form Data Format: LoginService.loginAccessToken requires application/x-www-form-urlencoded data. The generated SDK handles this automatically as long as you pass the data into the formData property.