Secure React Apps with Casdoor (Part 2)
Following part one’s introduction to Casdoor, this part delves into building a secure React application using Casdoor and TanStack Router. We’ll explore how to integrate Casdoor for user authentication and leverage TanStack Router for robust routing and access control.
Project Setup with Vite & TypeScript
Create a new project:
Terminal window pnpm create vite casdoor-demo-appcd casdoor-demo-apppnpm installDuring setup, select React and opt for TypeScript for type safety.
Install additional dependencies:
Terminal window pnpm install @tanstack/react-router @tanstack/react-query casdoor-js-sdkStart the development server:
Terminal window pnpm run dev
This launches your React development server, usually accessible at http://localhost:5173/.
TanStack Router Integration with Vite
In our secure React application, we’ll leverage TanStack Router for efficient navigation and route management. To integrate TanStack Router with Vite, we’ll utilize a dedicated plugin: @tanstack/router-vite-plugin.
1. Installation
pnpm install @tanstack/router-vite-plugin2. Vite Configuration
Within your vite.config.js file, update your Vite configuration to include the TanStack Router plugin:
export default defineConfig({ plugins: [react(), TanStackRouterVite()],});Casdoor SDK Setup
We’ll interact with Casdoor’s API using its JavaScript SDK. Here’s how we configure it in Settings.ts:
const sdkConfig = { serverUrl: import.meta.env.VITE_SERVER_URL, clientId: import.meta.env.VITE_CLIENT_ID, organizationName: import.meta.env.VITE_ORG_NAME, appName: import.meta.env.VITE_APP_NAME, redirectPath: import.meta.env.VITE_REDIRECT_PATH,};
export const CasdoorSDK = new Sdk(sdkConfig);Casdoor Login Integration
The login.tsx route allows users to initiate the Casdoor login flow:
const login = () => { CasdoorSDK.signin_redirect();};
// ... error handling code
return ( <div> <button onClick={login}>Casdoor Login</button> </div>);Authentication and User Data Fetching
Following a successful Casdoor login, an access token is typically provided. We’ll use this token to fetch user data from Casdoor’s API. Here’s how we leverage Tanstack Query for data management:
export const authQueryOptions = () => queryOptions({ queryKey: ['auth'], queryFn: () => CasdoorSDK.exchangeForAccessToken(), });
export const checkLogin = () => { const token = getToken(); return token !== undefined && token !== null && token.length > 0;};- checkLogin: This function checks if a valid access token exists in session storage by calling
getToken(). - getToken: This function retrieves the access token from session storage. (Using session storage is not secure and not recommended)
User Data Retrieval
Next, we’ll define functions to retrieve user data from Casdoor using the access token:
export interface User { name: string; picture?: string; preferred_username: string; roles: string[];}
export const userQueryOptions = (token: string) => queryOptions({ queryKey: ["user"], queryFn: async () => await getUserInfo(token), });
export async function getUserInfo(token: string): Promise<User | undefined> { return (await CasdoorSDK.getUserInfo(token)) as User;}- User Interface: This defines an interface named
Userthat specifies the expected structure of user data retrieved from Casdoor, including properties like name, roles, etc. - getUserInfo: This function fetches user data from Casdoor’s API using the SDK’s
getUserInfomethod.
Protected Routes with Role-Based Authorization
We can leverage user data (including roles) to define protected routes accessible only by authorized users. Here’s an example from _admin.tsx:
function AdminProtectedRoute() { const isLoggedIn = checkLogin(); const user = useLoaderData(); // Fetches user data from route loader
const isAdmin = user?.roles?.includes('admin');
if (!isLoggedIn || !isAdmin) { sessionStorage.setItem('redirect', location.pathname);
return <Navigate to="/login" search={{ redirect: location.href }} />; } return <Outlet />;}
export const Route = createFileRoute('/_admin')({ component: () => <AdminProtectedRoute />, loader: async ({ context: { queryClient } }) => { const token = getToken(); const user = await queryClient.ensureQueryData(userQueryOptions(token ?? ''));
return user; },});- AdminProtectedRoute: This component checks if the user is logged in (
isLoggedIn) and has the “admin” role using user data fetched via the route loader. If not authorized, it redirects to the login page. - Route Configuration: Here, we create a protected route for
/_adminusingcreateFileRoute. The component is set toAdminProtectedRoute, and the route loader utilizesuserQueryOptionsto fetch user data based on the retrieved token.
Conclusion
This blog post provides a foundational understanding of integrating Casdoor with a React application for user authentication and secure protected routes using TanStack Router. Remember to consult the official documentation of Casdoor, TanStack Router, and TanStack Query for more in-depth details and advanced functionalities. By leveraging these tools, you can build robust and secure React applications with Casdoor as your IAM solution.