This start app integrates the following features:
- Enforces RBAC at the component or route level using the fantastically useful Microsoft Authentication Library for React
- Uses the beautiful Microsoft Graph Toolkit for React log-in widgets
- Optionally, initializes user login on page load for a smoother protected-app experience
- Demonstrates protected routes using popular React Router library
- Use code splitting with Loadable Components for ultra-fast page loads
- Standard navbar with vanilla CSS
- Uses TypeScript React (
.tsx
)
- The awesome
@azure/msal-react
and@microsoft/mgt-react
libraries don't well play together without some trickery - Private and protected routes and components should just work (without lots of boiler plate code!)
- Using user access tokens should be easy (without lots of boiler plate code!)
-
Create an Azure AD app registration, and collect the Client ID, Tenant ID, and Scop in a
.env
file at the root of his repo, like so:REACT_APP_CLIENT_ID={{Azure AD app client ID}} REACT_APP_AUTHORITY=https://login.microsoftonline.com/{{Azure AD tenant ID}} REACT_APP_SCOPE={{ Scope URI }}
-
Install the dependencies with
yarn
and start the app withyarn start
, and view it at localhost:3000, as usual. -
View the examples:
- Forced user login
src/App.tsx
- Private and protected routing in
src/main.tsx
- Private and protected components in
src/components/page-1/index.tsx
- Using user access tokens in
src/components/page-1/index.tsx
User Access Tokens can be obtained in the app using the
fetchUserAccessToken
function , typically for fetching protected content
from a protected API
const [protectedAPIContent, setProtectedAPIContent] = useState();
useEffect(async () => {
const userAccessToken = await fetchUserAccessToken();
const response = await fetch("/some/protected/route.json", {
headers: {
Authorization: `Bearer ${userAccessToken}`,
},
});
// do things with the protected contents
setProtectedAPIContent(await response.json());
});
To conditionally render components manually, the useUserAccount()
hook provides access to the user account and the the users assigned roles.
import { useUserAccount } from "src/auth/UserAccount";
const MyAwesomeComponent = () => {
const AccountInfo = useUserAccount();
return (
<span>
You are{" "}
{"admin" in (AccountInfo.roles || []) ? "Authorized" : "Unauthorized"}
</span>
);
};
However, the following components will typically offer a more thorough Auth/Auth controlled rendering.
The PrivateComponent
requires that a user is signed in in order to view the
private contents.
<PrivateComponent
unauthenticated={() => <p>Loading...</p>}
loading={() => <p>Loading...</p>}
content={() => <div>You must be logged in to view this component</div>}
/>
The RBACProtectedComponent
requires that a user is signed in and is
assigned a role, as follows
-
When the
requiredRole
attribute is a string, the user is required to be assigned a role in order to view the protected content:<RBACProtectedComponent requiredRole="RequiredRole" // string loading={() => <p>Loading...</p>} unauthorized={() => <p>UNAUTHORIZED</p>} unauthenticated={() => <p>LOGGED OUT</p>} content={() => ( <p>You must be assigned the "RequiredRole" to view this component</p> )} />
-
When the
requiredRole
attribute is an array of strings, the user is required to be assigned have all of provided roles in order to view the protected content:<RBACProtectedComponent requiredRoles={["RoleOne", "RoleTwo"]} // string[] loading={() => <p>Loading...</p>} unauthorized={() => <p>UNAUTHORIZED</p>} unauthenticated={() => <p>LOGGED OUT</p>} content={() => ( <p> You must be assigned <em>both</em> "RoleTwo" and "RoleTwo" to view this component </p> )} />
-
When the
allowedRoles
is provided, the user is required to have at least one of the provided roles in order to view the protected content:<RBACProtectedComponent allowedRoles={["RoleOne", "RoleTwo"]} // string[] loading={() => <p>Loading...</p>} unauthorized={() => <p>UNAUTHORIZED</p>} unauthenticated={() => <p>LOGGED OUT</p>} content={() => ( <p> You must be assigned <em>either</em> "RoleTwo" and "RoleTwo" to view this component </p> )} />
Note that a simple way to enforce protected routing is to use () => <Navigate to="/unauthorized" replace />
in either the unauthorized
or
unauthenticated
properties of an RBACProtectedComponent