-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CMS-586: Store Keycloak roles in a global context (#69)
- Loading branch information
1 parent
fb9fb2e
commit 69201b0
Showing
10 changed files
with
171 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// @TODO: map role names to specific permissions | ||
|
||
// Export constants for role names | ||
export const ROLES = { | ||
SUPER_ADMIN: "doot-super-admin", | ||
APPROVER: "doot-approver", | ||
RSO: "doot-rso", | ||
PO: "doot-po", | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { useContext } from "react"; | ||
import { AccessContext } from "@/router/AccessContext"; | ||
import { ROLES } from "@/config/permissions"; | ||
|
||
export function useAccess() { | ||
const { roles, checkAccess } = useContext(AccessContext); | ||
|
||
return { roles, checkAccess, ROLES }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import PropTypes from "prop-types"; | ||
import { createContext } from "react"; | ||
import getEnv from "@/config/getEnv"; | ||
import { ROLES } from "@/config/permissions"; | ||
|
||
export const AccessContext = createContext(); | ||
|
||
export function AccessProvider({ children, auth }) { | ||
// Decode the token to get the user's roles | ||
const roles = []; | ||
const accessToken = auth?.user?.access_token; | ||
const clientId = getEnv("VITE_OIDC_CLIENT_ID"); // "staff-portal" | ||
|
||
if (accessToken) { | ||
const payload = accessToken.split(".").at(1); | ||
const decodedPayload = atob(payload); | ||
const parsedPayload = JSON.parse(decodedPayload); | ||
const payloadRoles = parsedPayload?.resource_access?.[clientId].roles ?? []; | ||
|
||
roles.push(...payloadRoles); | ||
} | ||
|
||
// @TODO: implement fine-grained permission checks here | ||
function checkAccess(requiredRole) { | ||
// Super admin can access everything | ||
if (roles.includes(ROLES.SUPER_ADMIN)) return true; | ||
|
||
return roles.includes(requiredRole); | ||
} | ||
|
||
// Provide the context value to child components | ||
return ( | ||
<AccessContext.Provider value={{ roles, checkAccess }}> | ||
{children} | ||
</AccessContext.Provider> | ||
); | ||
} | ||
|
||
// prop validation | ||
AccessProvider.propTypes = { | ||
children: PropTypes.node, | ||
auth: PropTypes.object, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// src/App.jsx | ||
import { useAuth } from "react-oidc-context"; | ||
// import getEnv from "@/config/getEnv"; | ||
|
||
function App() { | ||
const auth = useAuth(); | ||
|
||
function logOut() { | ||
auth.stopSilentRenew(); | ||
auth.signoutRedirect(); | ||
} | ||
|
||
// function logOutManual() { | ||
// // Manually clear cookies | ||
// const cookiesToClear = [ | ||
// "AUTH_SESSION_ID", | ||
// "AUTH_SESSION_ID_LEGACY", | ||
// "FAILREASON", | ||
// "KEYCLOAK_IDENTITY", | ||
// "KEYCLOAK_IDENTITY_LEGACY", | ||
// "KEYCLOAK_SESSION", | ||
// "KEYCLOAK_SESSION_LEGACY", | ||
// "SMSESSION", | ||
// ]; | ||
|
||
// cookiesToClear.forEach((cookieName) => { | ||
// document.cookie = `${cookieName}=; path=/; domain=${window.location.hostname}; expires=Thu, 01 Jan 1970 00:00:00 GMT`; | ||
// }); | ||
|
||
// // Manually clear session storage | ||
// sessionStorage.clear(); | ||
|
||
// // Manually go to the logout page | ||
// window.location.href = `${getEnv("VITE_OIDC_AUTHORITY")}/protocol/openid-connect/logout?redirect_uri=${window.location.origin}`; | ||
// } | ||
|
||
if (auth.isLoading) { | ||
return <div>Loading...</div>; | ||
} | ||
|
||
if (auth.error) { | ||
return <div>Auth error: {auth.error.message}</div>; | ||
} | ||
|
||
return ( | ||
<div> | ||
<button type="button" onClick={logOut}> | ||
Log out {auth.user?.profile.sub} | ||
</button> | ||
</div> | ||
); | ||
} | ||
|
||
export default App; |