# All files / atlas-mobile-ts/src/libs/auth AuthStore.ts

0% Statements 0/25
0% Branches 0/8
0% Functions 0/5
0% Lines 0/25

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();