A
Etienne Deneuve
Mar 31, 2024 · 4 min read

Secure React Apps with Casdoor (Part 2)

Side view worker wearing gloves

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

  1. Create a new project:

    Terminal window
    pnpm create vite casdoor-demo-app
    cd casdoor-demo-app
    pnpm install

    During setup, select React and opt for TypeScript for type safety.

  2. Install additional dependencies:

    Terminal window
    pnpm install @tanstack/react-router @tanstack/react-query casdoor-js-sdk
  3. Start 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

Terminal window
pnpm install @tanstack/router-vite-plugin

2. 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 User that 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 getUserInfo method.

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 /_admin using createFileRoute. The component is set to AdminProtectedRoute, and the route loader utilizes userQueryOptions to 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.

Casdoor Authentication Authorization

Related articles