blob: 440c4dc401cedbab677ab5d62888044b6b6b9e9e [file] [log] [blame]
simon416d0792022-11-03 02:46:18 -04001/*
2 * Copyright (C) 2022 Savoir-faire Linux Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as
6 * published by the Free Software Foundation; either version 3 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public
15 * License along with this program. If not, see
16 * <https://www.gnu.org/licenses/>.
17 */
simon94fe53e2022-11-10 12:51:58 -050018import axios, { AxiosInstance } from 'axios';
simon416d0792022-11-03 02:46:18 -040019import { Account } from 'jami-web-common/dist/Account';
20import { HttpStatusCode } from 'jami-web-common/dist/enums/http-status-code';
simon94fe53e2022-11-10 12:51:58 -050021import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
Issam E. Maghni09a3a1f2022-11-02 04:56:21 +000022import { useNavigate } from 'react-router-dom';
simon416d0792022-11-03 02:46:18 -040023
24import ProcessingRequest from '../components/ProcessingRequest';
25import { apiUrl } from '../utils/constants';
Issam E. Maghni09a3a1f2022-11-02 04:56:21 +000026import { WithChildren } from '../utils/utils';
simon416d0792022-11-03 02:46:18 -040027
28interface IAuthContext {
29 token: string;
30 account: Account;
31 logout: () => void;
simon94fe53e2022-11-10 12:51:58 -050032 axiosInstance: AxiosInstance;
simon416d0792022-11-03 02:46:18 -040033}
34
35const AuthContext = createContext<IAuthContext | undefined>(undefined);
36
Issam E. Maghni09a3a1f2022-11-02 04:56:21 +000037export default ({ children }: WithChildren) => {
simon416d0792022-11-03 02:46:18 -040038 const [token, setToken] = useState<string | undefined>();
39 const [account, setAccount] = useState<Account | undefined>();
40 const navigate = useNavigate();
41
42 const logout = useCallback(() => {
43 localStorage.removeItem('accessToken');
simon3f5f3e72022-11-08 21:01:57 -050044 navigate('/login');
simon416d0792022-11-03 02:46:18 -040045 }, [navigate]);
46
simon94fe53e2022-11-10 12:51:58 -050047 const axiosInstance = useMemo(() => {
48 if (!token) {
49 return;
50 }
51
52 const instance = axios.create({
53 baseURL: apiUrl,
54 headers: {
55 Authorization: `Bearer ${token}`,
56 },
57 });
58
59 instance.interceptors.response.use(
60 (res) => res,
61 (e) => {
62 switch (e.response?.status) {
63 case HttpStatusCode.Unauthorized:
64 logout();
65 break;
66 }
67 throw e;
68 }
69 );
70
71 return instance;
72 }, [token, logout]);
73
simon416d0792022-11-03 02:46:18 -040074 useEffect(() => {
75 const accessToken = localStorage.getItem('accessToken');
76
77 if (!accessToken) {
78 console.warn('Missing authentication JWT. Redirecting to login page...');
79 logout();
80 } else {
81 setToken(accessToken);
82 }
83 }, [logout]);
84
85 useEffect(() => {
simon94fe53e2022-11-10 12:51:58 -050086 if (!axiosInstance) {
87 return;
simon416d0792022-11-03 02:46:18 -040088 }
simon416d0792022-11-03 02:46:18 -040089
simon94fe53e2022-11-10 12:51:58 -050090 axiosInstance.get('/account').then(({ data }) => setAccount(Account.from(data)));
91 }, [axiosInstance, logout]);
92
93 if (!token || !account || !axiosInstance) {
simon416d0792022-11-03 02:46:18 -040094 return <ProcessingRequest open />;
95 }
96
97 return (
98 <AuthContext.Provider
99 value={{
100 token,
101 logout,
102 account,
simon94fe53e2022-11-10 12:51:58 -0500103 axiosInstance,
simon416d0792022-11-03 02:46:18 -0400104 }}
105 >
Issam E. Maghni09a3a1f2022-11-02 04:56:21 +0000106 {children}
simon416d0792022-11-03 02:46:18 -0400107 </AuthContext.Provider>
108 );
109};
110
111export function useAuthContext(dontThrowIfUndefined: true): IAuthContext | undefined;
112export function useAuthContext(): IAuthContext;
113export function useAuthContext(dontThrowIfUndefined?: true) {
114 const authContext = useContext(AuthContext);
115 if (!authContext && !dontThrowIfUndefined) {
116 throw new Error('AuthContext is not provided');
117 }
118 return authContext;
119}