react-oidc-context
react-oidc-context copied to clipboard
How to authenticate react routes using react-oidc-centext ?
Make use of the useAuth
hook function, like described here https://github.com/authts/react-oidc-context#getting-started
This is what I've come up with.
export function RequireAuth({ children, roles }: { children: JSX.Element, roles?: string[] }) {
let auth: AuthContextProps = useAuth();
let navigate: NavigateFunction = useNavigate();
// Coped from https://github.com/authts/react-oidc-context
// "automatically sign -in"
useEffect(() => {
if (!hasAuthParams() &&
!auth.isAuthenticated && !auth.activeNavigator && !auth.isLoading) {
auth.signinRedirect();
}
}, [auth.isAuthenticated, auth.activeNavigator, auth.isLoading, auth.signinRedirect]);
useEffect(() => {
if (!auth) { return; /* Possible bollocks initial value. */ }
// I thought that maybe this might work, but it doesn't -- nothing is ever logged.
auth.events.addAccessTokenExpired(() => { console.warn('UserManager.events.addAccessTokenExpired'); });
auth.events.addAccessTokenExpiring(() => { console.warn('UserManager.events.addAccessTokenExpiring'); });
auth.events.addSilentRenewError(() => { console.warn('UserManager.events.addSilentRenewError'); });
auth.events.addUserLoaded(() => { console.warn('UserManager.events.addUserLoaded'); });
auth.events.addUserSessionChanged(() => { console.warn('UserManager.events.addUserSessionChanged'); });
auth.events.addUserSignedIn(() => { console.warn('UserManager.events.addUserSignedIn'); });
auth.events.addUserSignedOut(() => { console.warn('UserManager.events.addUserSignedOut'); });
auth.events.addUserUnloaded(() => { console.warn('UserManager.events.addUserUnloaded'); });
// This breaks everything becauase it is blocked by the browser; no pre-flight check is done.
//if (!auth.isAuthenticated) {
// console.warn(`-- RequireAuth signinRedirect user ${JSON.stringify(auth.user)}--`);
// auth.signinRedirect();
//}
if (roles && roles!.length > 0) {
const routeRoles = roles.map((role) => role.toLowerCase());
const userRoles: string[] = getUserRoles(auth.user?.id_token!).map((role) => role.toLowerCase());
const role_authorised: boolean = routeRoles.some(role => userRoles.includes(role));
if (!role_authorised) {
navigate('forbidden');
// Should we instead use the template to render ForbiddenComponent instead of {children}?
}
}
}, [auth]);
function getUserRoles(token: string): string[] {
const decodedToken: PrestwoodIdentityAccessToken = jwtDecode<PrestwoodIdentityAccessToken>(token);
let userRoles: string[] = decodedToken ? decodedToken.role : [];
// BOOBYTRAP: If only one role then it could look like string instead of string[].
return userRoles;
}
return (<Fragment>{auth.isAuthenticated ? children : (null)}</Fragment>);
}
Next is the problem of how to use roles.
It's easy in Angular but a nightmare in React -- switch to Angular if you can.
hi @rwb196884 what is hasAuthParams?
This is what I've come up with.
export function RequireAuth({ children, roles }: { children: JSX.Element, roles?: string[] }) { let auth: AuthContextProps = useAuth(); let navigate: NavigateFunction = useNavigate(); // Coped from https://github.com/authts/react-oidc-context // "automatically sign -in" useEffect(() => { if (!hasAuthParams() && !auth.isAuthenticated && !auth.activeNavigator && !auth.isLoading) { auth.signinRedirect(); } }, [auth.isAuthenticated, auth.activeNavigator, auth.isLoading, auth.signinRedirect]); useEffect(() => { if (!auth) { return; /* Possible bollocks initial value. */ } // I thought that maybe this might work, but it doesn't -- nothing is ever logged. auth.events.addAccessTokenExpired(() => { console.warn('UserManager.events.addAccessTokenExpired'); }); auth.events.addAccessTokenExpiring(() => { console.warn('UserManager.events.addAccessTokenExpiring'); }); auth.events.addSilentRenewError(() => { console.warn('UserManager.events.addSilentRenewError'); }); auth.events.addUserLoaded(() => { console.warn('UserManager.events.addUserLoaded'); }); auth.events.addUserSessionChanged(() => { console.warn('UserManager.events.addUserSessionChanged'); }); auth.events.addUserSignedIn(() => { console.warn('UserManager.events.addUserSignedIn'); }); auth.events.addUserSignedOut(() => { console.warn('UserManager.events.addUserSignedOut'); }); auth.events.addUserUnloaded(() => { console.warn('UserManager.events.addUserUnloaded'); }); // This breaks everything becauase it is blocked by the browser; no pre-flight check is done. //if (!auth.isAuthenticated) { // console.warn(`-- RequireAuth signinRedirect user ${JSON.stringify(auth.user)}--`); // auth.signinRedirect(); //} if (roles && roles!.length > 0) { const routeRoles = roles.map((role) => role.toLowerCase()); const userRoles: string[] = getUserRoles(auth.user?.id_token!).map((role) => role.toLowerCase()); const role_authorised: boolean = routeRoles.some(role => userRoles.includes(role)); if (!role_authorised) { navigate('forbidden'); // Should we instead use the template to render ForbiddenComponent instead of {children}? } } }, [auth]); function getUserRoles(token: string): string[] { const decodedToken: PrestwoodIdentityAccessToken = jwtDecode<PrestwoodIdentityAccessToken>(token); let userRoles: string[] = decodedToken ? decodedToken.role : []; // BOOBYTRAP: If only one role then it could look like string instead of string[]. return userRoles; } return (<Fragment>{auth.isAuthenticated ? children : (null)}</Fragment>); }
Next is the problem of how to use roles.
It's easy in Angular but a nightmare in React -- switch to Angular if you can.
what is AuthParams?
what is AuthParams?
See https://github.com/authts/react-oidc-context/blob/main/src/utils.ts