Add AuthProvider to provide account and token
When logged in, the token and account info can be retrieved using
`useAuthContext` hook.
Fix jamid.node path in jamid.ts
Fix some eslint warnings.
Change-Id: I1ea4d537693df807b4ea67a277addfecfc749e4a
diff --git a/client/src/contexts/AuthProvider.tsx b/client/src/contexts/AuthProvider.tsx
new file mode 100644
index 0000000..50b1723
--- /dev/null
+++ b/client/src/contexts/AuthProvider.tsx
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+import { Account } from 'jami-web-common/dist/Account';
+import { HttpStatusCode } from 'jami-web-common/dist/enums/http-status-code';
+import { createContext, useCallback, useContext, useEffect, useState } from 'react';
+import { Outlet, useNavigate } from 'react-router-dom';
+
+import ProcessingRequest from '../components/ProcessingRequest';
+import { apiUrl } from '../utils/constants';
+
+interface IAuthContext {
+ token: string;
+ account: Account;
+ logout: () => void;
+}
+
+const AuthContext = createContext<IAuthContext | undefined>(undefined);
+
+export default () => {
+ const [token, setToken] = useState<string | undefined>();
+ const [account, setAccount] = useState<Account | undefined>();
+ const navigate = useNavigate();
+
+ const logout = useCallback(() => {
+ localStorage.removeItem('accessToken');
+ navigate('/');
+ }, [navigate]);
+
+ useEffect(() => {
+ const accessToken = localStorage.getItem('accessToken');
+
+ if (!accessToken) {
+ console.warn('Missing authentication JWT. Redirecting to login page...');
+ logout();
+ } else {
+ setToken(accessToken);
+ }
+ }, [logout]);
+
+ useEffect(() => {
+ if (token) {
+ const getAccount = async () => {
+ const url = new URL('/account', apiUrl);
+ const response = await fetch(url, {
+ method: 'GET',
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ });
+
+ if (response.status === HttpStatusCode.Ok) {
+ const serializedAccount = await response.json();
+ const account = Account.from(serializedAccount);
+ setAccount(account);
+ } else {
+ throw new Error(response.statusText);
+ }
+ };
+
+ getAccount().catch((e) => {
+ console.error('Error while retrieving account: ', e);
+ logout();
+ });
+ }
+ }, [token, logout]);
+
+ if (!token || !account) {
+ return <ProcessingRequest open />;
+ }
+
+ return (
+ <AuthContext.Provider
+ value={{
+ token,
+ logout,
+ account,
+ }}
+ >
+ <Outlet />
+ </AuthContext.Provider>
+ );
+};
+
+export function useAuthContext(dontThrowIfUndefined: true): IAuthContext | undefined;
+export function useAuthContext(): IAuthContext;
+export function useAuthContext(dontThrowIfUndefined?: true) {
+ const authContext = useContext(AuthContext);
+ if (!authContext && !dontThrowIfUndefined) {
+ throw new Error('AuthContext is not provided');
+ }
+ return authContext;
+}