@@ -1,12 +1,15 @@
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import auth from "../../services/auth";
|
||||
import getTimerWorker from "../../services/timer";
|
||||
import { parsePayload } from "../../utils/jwt";
|
||||
import { getGroups } 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 [error, setError] = useState(null);
|
||||
let timer;
|
||||
|
||||
@@ -15,13 +18,7 @@ export const UserProvider = ({ children }) => {
|
||||
if (!localStorage) return;
|
||||
const t = JSON.parse(localStorage.getItem("token"));
|
||||
if (!t) return;
|
||||
if (
|
||||
!t.idToken ||
|
||||
!t.idToken.jwtToken ||
|
||||
!t.idToken.payload ||
|
||||
!t.idToken.payload.exp
|
||||
)
|
||||
throw new Error("Invalid token");
|
||||
if (!t.idToken || !t.idToken.jwtToken) throw new Error("Invalid token");
|
||||
setToken(t);
|
||||
} catch (e) {
|
||||
document.location = signOut();
|
||||
@@ -45,7 +42,12 @@ export const UserProvider = ({ children }) => {
|
||||
};
|
||||
|
||||
const startSessionTimer = () => {
|
||||
const duration = 1000 * token.idToken.payload.exp - new Date().getTime();
|
||||
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) => {
|
||||
@@ -71,6 +73,7 @@ export const UserProvider = ({ children }) => {
|
||||
if (!t || t.error) throw new Error("Unable to refresh token");
|
||||
}
|
||||
|
||||
setGroups(getGroups(idToken));
|
||||
startSessionTimer();
|
||||
} catch (e) {
|
||||
setError(`Verify error. ${e.message}`);
|
||||
@@ -103,6 +106,7 @@ export const UserProvider = ({ children }) => {
|
||||
};
|
||||
|
||||
const signOut = () => {
|
||||
setGroups(null);
|
||||
setToken(null);
|
||||
if (localStorage) {
|
||||
localStorage.removeItem("token");
|
||||
@@ -149,6 +153,7 @@ export const UserProvider = ({ children }) => {
|
||||
value={{
|
||||
fetching,
|
||||
token,
|
||||
groups,
|
||||
error,
|
||||
setError,
|
||||
signIn,
|
||||
|
||||
@@ -11,15 +11,7 @@ import {
|
||||
import { UserProvider, useUserContext } from "../Contexts/UserContext";
|
||||
import auth from "../../services/auth";
|
||||
import getTimerWorker from "../../services/timer";
|
||||
|
||||
const TEST_TOKEN = {
|
||||
idToken: {
|
||||
jwtToken: "TEST",
|
||||
payload: {
|
||||
exp: new Date().getTime() / 1000,
|
||||
},
|
||||
},
|
||||
};
|
||||
import { TEST_AUTH_OBJECT, TEST_EXPECTED_GROUPS } from "../../utils/testing";
|
||||
|
||||
const INVALID_TOKEN_RESPONSE = {
|
||||
error: "Bad Request Error",
|
||||
@@ -36,10 +28,11 @@ const setupSignInEnv = (refreshResponse, valid) => {
|
||||
auth.setVerifyResponse({ valid });
|
||||
};
|
||||
|
||||
const checkBaseResults = (error, fetching, token) => {
|
||||
const checkBaseResults = (error, fetching, token, groups) => {
|
||||
expect(screen.getByTestId("error").innerHTML).toEqual(error);
|
||||
expect(screen.getByTestId("fetching").innerHTML).toEqual(fetching);
|
||||
expect(screen.getByTestId("token").innerHTML).toEqual(token);
|
||||
expect(screen.getByTestId("groups").innerHTML).toEqual(groups);
|
||||
};
|
||||
|
||||
const checkTokenResults = (timer, token) => {
|
||||
@@ -57,13 +50,14 @@ describe("UseContext", () => {
|
||||
describe("Signin", () => {
|
||||
beforeEach(() => {
|
||||
const TestComp = () => {
|
||||
const { signIn, error, token, fetching } = useUserContext();
|
||||
const { signIn, error, token, groups, fetching } = useUserContext();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div data-testid="error">{error}</div>
|
||||
<div data-testid="fetching">{fetching.toString()}</div>
|
||||
<div data-testid="token">{JSON.stringify(token)}</div>
|
||||
<div data-testid="groups">{groups}</div>
|
||||
<button data-testid="signInNoCode" onClick={() => signIn("")} />
|
||||
<button
|
||||
data-testid="signInInvalidCode"
|
||||
@@ -85,13 +79,13 @@ describe("UseContext", () => {
|
||||
});
|
||||
|
||||
it("Initial state", () => {
|
||||
checkBaseResults("", "false", "null");
|
||||
checkBaseResults("", "false", "null", "");
|
||||
});
|
||||
|
||||
it("No auth code", () => {
|
||||
fireEvent.click(screen.getByTestId("signInNoCode"));
|
||||
|
||||
checkBaseResults("", "false", "null");
|
||||
checkBaseResults("", "false", "null", "");
|
||||
});
|
||||
|
||||
it("Invalid auth code", async () => {
|
||||
@@ -103,14 +97,19 @@ describe("UseContext", () => {
|
||||
expect(screen.getByTestId("fetching").innerHTML).toEqual("true")
|
||||
);
|
||||
|
||||
checkBaseResults("Sign in error. Bad Request Message", "false", "null");
|
||||
checkBaseResults(
|
||||
"Sign in error. Bad Request Message",
|
||||
"false",
|
||||
"null",
|
||||
""
|
||||
);
|
||||
});
|
||||
|
||||
it("Sign in form", async () => {
|
||||
const TOKEN_STRING = JSON.stringify(TEST_TOKEN);
|
||||
const TOKEN_STRING = JSON.stringify(TEST_AUTH_OBJECT);
|
||||
const timer = getTimerWorker();
|
||||
|
||||
setupSignInEnv(TEST_TOKEN, true);
|
||||
setupSignInEnv(TEST_AUTH_OBJECT, true);
|
||||
|
||||
fireEvent.click(screen.getByTestId("signIn"));
|
||||
|
||||
@@ -118,7 +117,7 @@ describe("UseContext", () => {
|
||||
expect(screen.getByTestId("fetching").innerHTML).toEqual("true")
|
||||
);
|
||||
|
||||
checkBaseResults("", "false", TOKEN_STRING);
|
||||
checkBaseResults("", "false", TOKEN_STRING, TEST_EXPECTED_GROUPS);
|
||||
checkTokenResults(timer, TOKEN_STRING);
|
||||
});
|
||||
});
|
||||
@@ -126,12 +125,20 @@ describe("UseContext", () => {
|
||||
describe("Signout", () => {
|
||||
beforeEach(async () => {
|
||||
const TestComp = () => {
|
||||
const { signIn, signOut, error, token, fetching } = useUserContext();
|
||||
const {
|
||||
signIn,
|
||||
signOut,
|
||||
error,
|
||||
token,
|
||||
groups,
|
||||
fetching,
|
||||
} = useUserContext();
|
||||
return (
|
||||
<>
|
||||
<div data-testid="error">{error}</div>
|
||||
<div data-testid="fetching">{fetching.toString()}</div>
|
||||
<div data-testid="token">{JSON.stringify(token)}</div>
|
||||
<div data-testid="groups">{groups}</div>
|
||||
<button data-testid="signIn" onClick={() => signIn("TEST_CODE")} />
|
||||
<button data-testid="signOut" onClick={() => signOut()} />
|
||||
</>
|
||||
@@ -142,7 +149,7 @@ describe("UseContext", () => {
|
||||
<TestComp />
|
||||
</UserProvider>
|
||||
);
|
||||
auth.setSignInResponse(TEST_TOKEN);
|
||||
auth.setSignInResponse(TEST_AUTH_OBJECT);
|
||||
fireEvent.click(screen.getByTestId("signIn"));
|
||||
await waitFor(() =>
|
||||
expect(screen.getByTestId("fetching").innerHTML).toEqual("true")
|
||||
@@ -154,10 +161,9 @@ describe("UseContext", () => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it("Token cleared", () => {
|
||||
it("Token cleared", async () => {
|
||||
fireEvent.click(screen.getByTestId("signOut"));
|
||||
|
||||
checkBaseResults("", "false", "null");
|
||||
checkBaseResults("", "false", "null", "");
|
||||
if (!localStorage) return;
|
||||
expect(localStorage.getItem("token")).toBeNull();
|
||||
});
|
||||
@@ -166,13 +172,14 @@ describe("UseContext", () => {
|
||||
describe("Refresh", () => {
|
||||
beforeEach(() => {
|
||||
const TestComp = () => {
|
||||
const { refresh, error, token, fetching } = useUserContext();
|
||||
const { refresh, error, token, groups, fetching } = useUserContext();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div data-testid="error">{error}</div>
|
||||
<div data-testid="fetching">{fetching.toString()}</div>
|
||||
<div data-testid="token">{JSON.stringify(token)}</div>
|
||||
<div data-testid="groups">{groups}</div>
|
||||
<button data-testid="refreshNoToken" onClick={() => refresh("")} />
|
||||
<button
|
||||
data-testid="refreshInvalidToken"
|
||||
@@ -197,12 +204,12 @@ describe("UseContext", () => {
|
||||
});
|
||||
|
||||
it("Initial state", () => {
|
||||
checkBaseResults("", "false", "null");
|
||||
checkBaseResults("", "false", "null", "");
|
||||
});
|
||||
|
||||
it("No refresh token", () => {
|
||||
fireEvent.click(screen.getByTestId("refreshNoToken"));
|
||||
checkBaseResults("Refresh error. Token required", "false", "null");
|
||||
checkBaseResults("Refresh error. Token required", "false", "null", "");
|
||||
});
|
||||
|
||||
it("Invalid refresh token", async () => {
|
||||
@@ -213,21 +220,26 @@ describe("UseContext", () => {
|
||||
expect(screen.getByTestId("fetching").innerHTML).toEqual("true")
|
||||
);
|
||||
|
||||
checkBaseResults("Refresh error. Bad Request Message", "false", "null");
|
||||
checkBaseResults(
|
||||
"Refresh error. Bad Request Message",
|
||||
"false",
|
||||
"null",
|
||||
""
|
||||
);
|
||||
});
|
||||
|
||||
it("Valid refresh token", async () => {
|
||||
const TOKEN_STRING = JSON.stringify(TEST_TOKEN);
|
||||
const TOKEN_STRING = JSON.stringify(TEST_AUTH_OBJECT);
|
||||
const timer = getTimerWorker();
|
||||
|
||||
setupRefreshEnv(TEST_TOKEN, true);
|
||||
setupRefreshEnv(TEST_AUTH_OBJECT, true);
|
||||
|
||||
fireEvent.click(screen.getByTestId("refreshValidToken"));
|
||||
await waitFor(() =>
|
||||
expect(screen.getByTestId("fetching").innerHTML).toEqual("true")
|
||||
);
|
||||
|
||||
checkBaseResults("", "false", TOKEN_STRING);
|
||||
checkBaseResults("", "false", TOKEN_STRING, TEST_EXPECTED_GROUPS);
|
||||
checkTokenResults(timer, TOKEN_STRING);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import React from "react";
|
||||
|
||||
import { getGroups } from "../../../utils/roles";
|
||||
|
||||
let token = null;
|
||||
let groups = null;
|
||||
let fetching = false;
|
||||
let error = null;
|
||||
let signInResp = {};
|
||||
let authorizeURL = "https://cognito.com/authorize?redirect=https://example.com/callback";
|
||||
let logoutURL = "https://cognito.com/logout?redirect=https://example.com/callback";
|
||||
let authorizeURL =
|
||||
"https://cognito.com/authorize?redirect=https://example.com/callback";
|
||||
let logoutURL =
|
||||
"https://cognito.com/logout?redirect=https://example.com/callback";
|
||||
|
||||
export const UserProvider = ({ children }) => {
|
||||
return <div data-testid="mocked-userprovider">{children}</div>;
|
||||
@@ -15,6 +20,7 @@ export const useUserContext = () => ({
|
||||
token,
|
||||
fetching,
|
||||
error,
|
||||
groups,
|
||||
signIn: jest.fn(() => signInResp),
|
||||
signOut: jest.fn(),
|
||||
getAuthorizeURL: jest.fn(() => authorizeURL),
|
||||
@@ -26,6 +32,11 @@ export const useUserContext = () => ({
|
||||
|
||||
export const setToken = (val) => {
|
||||
token = val;
|
||||
if (!val || !val.idToken || !val.idToken.jwtToken) {
|
||||
groups = null;
|
||||
} else {
|
||||
groups = getGroups(val.idToken.jwtToken);
|
||||
}
|
||||
};
|
||||
|
||||
export const setFetching = (val) => {
|
||||
|
||||
Reference in New Issue
Block a user