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 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | /* 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 * as SecureStore from 'expo-secure-store'; import { getItemAsync } from 'expo-secure-store'; import { action, makeObservable, observable } from "mobx"; import { SECURESTORE_ACCESSTOKEN, SECURESTORE_IDTOKEN, SECURESTORE_NOTIFTOKEN, SECURESTORE_REFRESHTOKEN } from '../../utils/GlobalUtils'; /** * Represents OIDC ID token. */ export interface IdToken { sub: string } interface CheckAuthStateResult { tokenValue: string tokenType: "access" | "refresh" } /** * A mobx store responsible for holding the current access token, refresh token and userId in memory and in Expo's [SecureStore]{@link https://docs.expo.dev/versions/latest/sdk/securestore/}. * Used by {@link Atlas} * @category Stores */ class AuthStore { /** * A string representing the OAuth2 access token issued to the user when they authenticate. It is sent in the Authorization header of all subsequent XHR requests in order to use authorized API endpoints. * For more information on OAuth2 and OpenIdConnect, visit {@link https://openid.net/connect/} and {@link https://oauth.net/2/}. * @memberOf AuthStore */ accessToken?: string | null = null; /** * A string representing the OAuth2 refresh token issued to the user when they authenticate. It is sent to the identity provider to retrieve a new access token when the previous access token expires. * @memberOf AuthStore */ refreshToken?: string | null = null; /** * A string representing the OAuth2 refresh token issued to the user when they authenticate. It is used to retrieve the users information from the API and to link objects created by the user to the user. */ userId?: string = undefined; /** * A string representing the expo notification token. */ notificationToken?: string = undefined; /** * Constructor. Uses mobx's [makeObservable]{@linkcode https://mobx.js.org/observable-state.html} to register actions and observable elements. */ constructor() { makeObservable(this, { accessToken: observable, refreshToken: observable, userId: observable, notificationToken: observable, setAccessTokenAsync: action, setRefreshTokenAsync: action, setIdAsync: action, setNotificationTokenAsync: action }); } /** * Uses the given access token value to set {@linkcode accessToken} and the 'access' element in the secure store. */ async setAccessTokenAsync(tokenValue: string | null) { this.accessToken = tokenValue; if (this.accessToken) { await SecureStore.setItemAsync(SECURESTORE_ACCESSTOKEN, this.accessToken); } else { await SecureStore.deleteItemAsync(SECURESTORE_ACCESSTOKEN); } } /** * Uses the given refresh token value to set {@linkcode refreshToken} and the 'refresh' element in the secure store. */ async setRefreshTokenAsync(tokenValue?: string) { this.refreshToken = tokenValue; if (this.refreshToken) { await SecureStore.setItemAsync(SECURESTORE_REFRESHTOKEN, this.accessToken); } else { await SecureStore.deleteItemAsync(SECURESTORE_REFRESHTOKEN); } } /** * Uses the given user id value to set {@linkcode userId} and the 'id' element in the secure store. */ async setIdAsync(id?: string) { this.userId = id; if (this.userId) { await SecureStore.setItemAsync(SECURESTORE_IDTOKEN, this.userId); } else { await SecureStore.deleteItemAsync(SECURESTORE_IDTOKEN); } } /** * Uses the given notification token value to set {@linkcode notificationToken} and the 'notif-token' element in the secure store. */ async setNotificationTokenAsync(token?: string) { this.notificationToken = token; if (this.notificationToken) { await SecureStore.setItemAsync(SECURESTORE_NOTIFTOKEN, this.notificationToken); } else { await SecureStore.deleteItemAsync(SECURESTORE_NOTIFTOKEN); } } /** * 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. */ // checkAuthState = async (): CheckAuthStateResult => { // // check both the mobx store and secure storage for the token // console.log('[Authentication]: Checking for access token in memory...') // let currentAccessToken = authStore.accessToken; // if (!currentAccessToken) { // console.log('[Authentication]: No access token in memory, checking in secure store...') // currentAccessToken = await getItemAsync(SECURESTORE_ACCESSTOKEN); // } // if (!currentAccessToken) { // console.log('[Authentication]: No access token in secure store, attempting to use a refresh token...') // let refreshToken = authStore.refreshToken; // if (!refreshToken) { // refreshToken = await getItemAsync(SECURESTORE_REFRESHTOKEN) // } // if (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); // if (response.status == 200) { // await authStore.setAccessTokenAsync(currentAccessToken); // console.log('[Authentication]: Access token valid.') // } // } catch (error) { // // check if access token can be refreshed // console.log(error) // if (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); // } } export const authStore = new AuthStore(); |