https://www.linkedin.com/in/harshathirimanna/
ReactATX Meetup Sample Applications
Asgardeo Auth React SDK https://www.npmjs.com/package/@asgardeo/auth-react
Open the project folder using your familar editor and open a terminal from either inside the editor or external terminal
reactatx-samples/react-sample
npm install
npm run dev
https://asgardeo.io/signup?utm_source=console
https://wso2.com/asgardeo/docs/get-started/try-samples/qsg-spa-react/ Use this redirect URL when create the application
Redirect URL = http://localhost:5173/
https://wso2.com/asgardeo/docs/guides/users/manage-users/
[email protected]
ReactATX@2024
https://www.npmjs.com/package/@asgardeo/auth-react
Go inside this sample prject (main branch) in below folder reactatx-samples/react-sample/src/pages/main.jsx and modify accordingly
npm install @asgardeo/auth-react --save
To update the existing code and wrap the root App component with the AuthProvider component from the React SDK, you can follow these steps. This ensures that all child components within the App component have access to the authContext provided by AuthProvider.
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.jsx";
import "./index.css";
import { AuthProvider } from "@asgardeo/auth-react";
const config = {
signInRedirectURL: "http://localhost:5173/",
signOutRedirectURL: "http://localhost:5173/",
clientID: "63jnmnPytGXmHLOqxJ6oNCMNmbka_",
baseUrl: "https://api.asgardeo.io/t/reactatx",
scope: ["openid", "profile"],
};
createRoot(document.getElementById("root")).render(
<StrictMode>
<AuthProvider config={config}>
<App />
</AuthProvider>
</StrictMode>
);
To secure the /dashboard route using a SecureApp component, you can wrap that specific route within your routing configuration as in the below. When a user directly accesses /dashboard, they will be automatically redirected to the Identity Provider's login screen, which is configured with the AuthProvider at the root level.
import "./App.css";
import Home from "./pages/Home";
import Dashboard from "./pages/Dashboard";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import Error from "./pages/Error";
import { SecureApp } from "@asgardeo/auth-react";
const router = createBrowserRouter([
{
path: "/",
element: <Home />,
},
{
path: "/home",
element: <Home />,
},
{
path: "/dashboard",
element: (
<SecureApp>
<Dashboard />
</SecureApp>
),
},
{
path: "/error",
element: <Error />,
},
]);
function App() {
return <RouterProvider router={router} />;
}
export default App;
Add a "SignIn" button to the homepage that enables users to initiate the login process by clicking it. This button will redirect users to the Identity Provider for authentication. After authentication, users will be redirected back to the original context, with "http://localhost:5173/" set as the redirect URL in the Identity Provider.
You can then register a callback function to the useAuthContext hook using the 'on' function, as shown in the code below. This setup will automatically invoke the navigate function, redirecting the user to the dashboard.
import reactLogo from "../assets/react.svg";
import viteLogo from "/vite.svg";
import capitalFac from "../assets/cap.png";
import asgardeo from "../assets/asgardeo.png";
import "../App.css";
import { useNavigate } from "react-router-dom";
import { useAuthContext, Hooks } from "@asgardeo/auth-react";
import { useEffect } from "react";
function Home() {
const navigate = useNavigate();
const { on, state, signIn } = useAuthContext();
useEffect(() => {
on(Hooks.SignIn, () => {
navigate("/dashboard");
});
}, [on]);
if (state.isLoading) {
return <div className="home">Loading</div>;
}
return (
<div className="home">
<div>
<a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
<a href="https://www.capitalfactory.com/" target="_blank">
<img
src={capitalFac}
className="logo react"
alt="Capital Factory logo"
/>
</a>
<a href="https://wso2.com/asgardeo/" target="_blank">
<img src={asgardeo} className="logo react" alt="WSO2 logo" />
</a>
</div>
<h1>Vite + React</h1>
<h1>Home Page</h1>
<h2>Authenticated : {String(state?.isAuthenticated)}</h2>
<div className="card">
<p>
Edit <code>src/App.jsx</code> and save to test HMR
</p>
<input
type="button"
value="SingIn >>"
className="button"
onClick={() => {
signIn();
}}
/>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</div>
);
}
export default Home;
Add a logout button to the Dashboard component that, when clicked, triggers the signOut function from the authContext, which is accessible via the useAuthContext hook. This setup ensures users can easily log out from the dashboard and redirect back to the Home page.
import reactLogo from "../assets/react.svg";
import viteLogo from "/vite.svg";
import capitalFac from "../assets/cap.png";
import asgardeo from "../assets/asgardeo.png";
import "../App.css";
import { useAuthContext } from "@asgardeo/auth-react";
function Dashboard() {
const { signOut } = useAuthContext();
return (
<div className="dashboard">
<div>
<a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
<a href="https://www.capitalfactory.com/" target="_blank">
<img
src={capitalFac}
className="logo react"
alt="Capital Factory logo"
/>
</a>
<a href="https://wso2.com/asgardeo/" target="_blank">
<img src={asgardeo} className="logo react" alt="WSO2 logo" />
</a>
</div>
<h1>Vite + React</h1>
<h1>User Dashboard</h1>
<div className="card">
<p>
Edit <code>src/App.jsx</code> and save to test HMR
</p>
<input
type="button"
value="Logout >>"
className="button"
onClick={() => {
signOut();
}}
/>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</div>
);
}
export default Dashboard;
To read user attributes such as username and roles in your React application, first ensure that roles are established on the Identity Provider side.
Using Asgardeo, you would start by creating a group and a role, then assign the role to the group and the group to the user.
https://wso2.com/asgardeo/docs/guides/users/manage-roles/
As the 'roles' attribute needs to be included in the openid or profile scopes, make sure to add it to one of these scopes to retrieve it with the ID Token response.
https://wso2.com/asgardeo/docs/guides/users/attributes/manage-scopes/
In your React application, you can then access this ID Token by calling the getDecodedIDToken function from authContext.
Note that the following code should not be replaced your existing Dashboard.jsx directly; you will need to integrate it appropriately by examining your current code structure.
import { useEffect } from "react";
import { useState } from "react";
----
const [name, setName] = useState("Unknown");
const [roles, setRoles] = useState("Unknown");
const { state, signOut, getDecodedIDToken } = useAuthContext();
-----
useEffect(() => {
getDecodedIDToken()
.then((token) => {
setName(token?.username);
setRoles(token?.roles);
})
.catch((error) => {
console.error("Error while getting the decoded ID token", error);
});
}, [getDecodedIDToken, state]);
-----
<h2>Username : {state?.username}</h2>
<h2>Authenticated : {String(state?.isAuthenticated)}</h2>
<h2>Roles : {Array(roles).join()}</h2>
To call RestAPI from this component, we can use httpRequest that was wrapped by the SDK. When we config the httpRequest, we can enable either the access token should be add to the header or not. To access exernal resources, we need to add this URL to the resourceServerURLs in AuthProvider config as in the below main.jsx
const { state, signOut, getDecodedIDToken, httpRequest } = useAuthContext();
const [resource, setResource] = useState("");
--------------
const dataRetrieve = () => {
const reqConfig = {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
method: "GET",
url: "https://jsonplaceholder.typicode.com/todos/1",
attachToken: false,
};
httpRequest(reqConfig)
.then((response) => {
console.log("Data retrieved successfully", response);
setResource(JSON.stringify(response.data));
})
.catch((error) => {
console.error("Error while getting the data", error);
});
};
--------------
<div className="card">
<input
type="button"
value="Access Resource"
className="button"
onClick={dataRetrieve}
/>
</div>
<div>{resource}</div>
Add resoruce URLs to the list.
We can configure which storage that we can use with the ReactSDK.
sessionStorage localStorage webWorker
const config = {
signInRedirectURL: "http://localhost:5173/",
signOutRedirectURL: "http://localhost:5173/",
clientID: "63jnmnPytGXmHLOqxJ6oNCMNmbka",
baseUrl: "https://api.asgardeo.io/t/reactatx",
scope: ["openid", "profile"],
resourceServerURLs: ["https://jsonplaceholder.typicode.com"],
storage: "webWorker",
};