import React, { useContext, useEffect, useState } from "react"; import auth from "../../services/auth"; import getTimerWorker from "../../services/getTimerWorker"; import { parsePayload } from "../../utils/jwt"; import {getGroups, getProviders} from "../../utils/roles"; const UserContext = React.createContext(); export const UserProvider = ({ children }) => { const [fetching, setFetching] = useState(false); const [token, setToken] = useState(null); const [groups, setGroups] = useState(null); const [providers, setProviders] = useState(null); const [error, setError] = useState(null); let timer; useEffect(() => { try { if (!localStorage) return; const t = JSON.parse(localStorage.getItem("token")); if (!t) return; if (!t.idToken || !t.idToken.jwtToken) throw new Error("Invalid token"); setToken(t); } catch (e) { document.location = signOut(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { if (!token) return; verifyToken(); return () => { if (timer) timer.terminate(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [token]); const refreshTokens = async () => { if (!token || !token.refreshToken || !token.refreshToken.token) return null; return refresh(token.refreshToken.token); }; const startSessionTimer = () => { if (!token || !token.idToken || !token.idToken.jwtToken) { throw new Error("No id token"); } const payload = parsePayload(token.idToken.jwtToken); if (!payload || !payload.exp) throw new Error("Bad id token payload"); const duration = 1000 * payload.exp - new Date().getTime(); if (!timer) { timer = getTimerWorker(); timer.onMessage(async (e) => { if (e.data === "timeout") { const t = await refreshTokens(); if (t && !t.error) return; document.location = signOut(); } }); } timer.start(duration); }; const verifyToken = async () => { try { const { idToken: { jwtToken: idToken }, } = token; const result = await auth.verify(idToken); if (!result || !result.valid) { const t = await refreshTokens(); if (!t || t.error) throw new Error("Unable to refresh token"); } setGroups(getGroups(idToken)); setProviders(getProviders(idToken)); startSessionTimer(); } catch (e) { setError(`Verify error. ${e.message}`); document.location = signOut(); } }; const signIn = async (code) => { let result = null; try { if (!code) return; setFetching(true); setError(null); result = await auth.signIn(code); if (result.message) { throw new Error(result.message); } signedIn(result); } catch (err) { setError(`Sign in error. ${err.message}`); } finally { setFetching(false); } return result; }; const signOut = () => { setGroups(null); setProviders(null); setToken(null); if (localStorage) { localStorage.removeItem("token"); } return getLogoutURL(); }; const signedIn = (value) => { setToken(value); if (!localStorage || !value || !value.idToken) return; localStorage.setItem("token", JSON.stringify(value)); }; const refresh = async (value) => { let result = null; try { if (!value) { throw new Error("Token required"); } setFetching(true); setError(null); result = await auth.refresh(value); if (result.message) { throw new Error(result.message); } signedIn(result); } catch (err) { setError(`Refresh error. ${err.message}`); } finally { setFetching(false); } return result; }; const getAuthorizeURL = () => auth.ssoAuthorize(); const getLogoutURL = () => auth.ssoLogout(); return ( {children} ); }; export const useUserContext = () => useContext(UserContext);