Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | /* Copyright (C) Click & Push Accessibility, Inc - All Rights Reserved * Unauthorized copying of this file, via any medium is strictly prohibited * Proprietary and confidential * Written and maintained by the Click & Push Development team * <dev@clicknpush.ca>, January 2022 */ import { RacingSansOne_400Regular } from '@expo-google-fonts/racing-sans-one'; import { NavigationContainer } from '@react-navigation/native'; import { MenuProvider } from 'react-native-popup-menu' import axios, { AxiosRequestConfig } from 'axios'; import { useFonts } from 'expo-font'; import { getItemAsync } from 'expo-secure-store'; import { observer } from 'mobx-react'; import React, { useEffect, useRef, useState } from 'react'; import { AppState, SafeAreaView, StatusBar, Platform, Alert } from 'react-native'; import { QueryClient, QueryClientProvider } from 'react-query'; import { Splash } from '../components/Splash'; import { colors, SECURESTORE_ACCESSTOKEN, SECURESTORE_REFRESHTOKEN } from '../utils/GlobalUtils'; import { useAuth } from '../hooks/useAuth'; import AuthorizedNavigator, { navigationRef } from '../navigation/AuthorizedNavigator'; import UnauthorizedNavigator from '../navigation/UnauthorizedNavigator'; import { authStore } from '../libs/auth/AuthStore'; import { API_URL } from '../utils/RequestUtils'; import {reportAxiosError} from '../libs/auth/core' export enum TokenState { CheckingToken, ValidToken, InvalidToken } const queryClient = new QueryClient(); /** * Sub-root component of the app. Contains all global providers (NavigationContainer and SafeAreaProvider for React Navigation, QueryClientProvider for react-query) and is responsible for restricting unauthenticated users to the Intro screen by listening to {@link AuthStore}'s accessToken value. * @component */ const Atlas : React.FC = () => { /** * Flag that is switched on when the app is checking for tokens in the keystore and in memory. When true, "Logging you in.." and a spinner will be displayed to the user. */ const [checkingToken, setCheckingToken] = useState<boolean>(true); const { refreshAccessToken } = useAuth(); const [fontsLoaded, error] = useFonts({ RacingSansOne_400Regular }); /** * Checks if there is an access token available in {@link AuthStore}, then checks if that access token is valid by calling the API. * If the response is valid, the access token will be stored in memory, otherwise the user will be directed to intro screen. */ const checkToken = async () => { // check both the mobx store and secure storage for the token console.log('[Authentication]: Checking for access token in memory...') let currentAccessToken = authStore.accessToken; Iif (!currentAccessToken) { console.log('[Authentication]: No access token in memory, checking in secure store...') currentAccessToken = await getItemAsync(SECURESTORE_ACCESSTOKEN); } Iif (!currentAccessToken) { console.log('[Authentication]: No access token in secure store, attempting to use a refresh token...') let refreshToken = authStore.refreshToken; Iif (!refreshToken) { refreshToken = await getItemAsync(SECURESTORE_REFRESHTOKEN) } Iif (refreshToken) { await refreshAccessToken() currentAccessToken = authStore.accessToken } } if (currentAccessToken) { console.log('[Authentication]: Found access token, testing its validity...') // check to see if the token is valid by making test call const requestConfig: AxiosRequestConfig = { method: 'GET', url: API_URL + "/api/me/", headers: { "Authorization": "Bearer " + currentAccessToken } }; try { const response = await axios(requestConfig); Iif (response.status == 200) { await authStore.setAccessTokenAsync(currentAccessToken); await authStore.setRefreshTokenAsync(await getItemAsync(SECURESTORE_REFRESHTOKEN)) await authStore.setIdAsync(response.data.id) console.log('[Authentication]: Access token valid.') } } catch (error) { // check if access token can be refreshed console.log(error) Iif (error.response.status == 401) { try { await refreshAccessToken(); // update authorization header w/ new token await axios({...requestConfig, headers: { "Authorization": "Bearer " + authStore.accessToken }}); } catch (error) { } } // something went wrong with the api call, log error and clear auth state reportAxiosError('[Authentication]: Something went wrong when retrieving an access token', error) await authStore.setAccessTokenAsync(null); await authStore.setRefreshTokenAsync(null); await authStore.setNotificationTokenAsync(null); await authStore.setIdAsync(null); } } else { // no access token was found, user will be taken to login console.log('[Authentication]: No access token was found, prompting user to login.') await authStore.setAccessTokenAsync(null); await authStore.setRefreshTokenAsync(null); await authStore.setNotificationTokenAsync(null); await authStore.setIdAsync(null); } setCheckingToken(false); } useEffect(() => { /** * useEffect hook that is responsible for registering an appState "change" handler that will call {@linkcode checkToken} each time the app is opened or closed on the device. * @memberOf Atlas */ function registerAppStateChangeHandler() { AppState.addEventListener("change", (appState: string) => { Iif (appState == 'active') { console.log('[Authentication]: App opened, checking auth tokens...') checkToken(); } }); return () => { AppState.removeEventListener("change", (appState: string) => { Iif (appState == 'active') { checkToken(); } }); }; } registerAppStateChangeHandler(); }, [AppState.currentState]); useEffect(() => { /** * Calls {@linkcode checkToken} when a change to the access token stored in {@link AuthStore} is detected. * @memberOf Atlas */ const checkTokenOnAccessTokenChange = async () => { console.log('[Authentication]: Change to accessToken detected, checking token state...') await checkToken() } checkTokenOnAccessTokenChange() }, [authStore.accessToken]); return ( <MenuProvider> <SafeAreaView accessible={true} accessibilityLabel="rootView" style={{height: '100%', backgroundColor: colors.red}}> <StatusBar barStyle='light-content' backgroundColor={colors.red}/> <NavigationContainer ref={navigationRef}> {checkingToken ? <Splash/> : <QueryClientProvider client={queryClient}> {authStore.accessToken ? <AuthorizedNavigator /> : <UnauthorizedNavigator /> } </QueryClientProvider> } </NavigationContainer> </SafeAreaView> </MenuProvider> ); } export default observer(Atlas); |