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

0% Statements 0/65
0% Branches 0/5
0% Functions 0/4
0% Lines 0/62

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

    /* 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 axios, { AxiosError, AxiosRequestConfig } from "axios";
    import { authStore, IdToken } from "./AuthStore";
    import { API_URL } from "../../utils/RequestUtils";
    import { AuthRequestConfig, DiscoveryDocument, IssuerOrDiscovery, loadAsync, ResponseType } from "expo-auth-session";
    import jwt_decode from 'jwt-decode';

    export const SECURESTORE_ACCESSTOKEN = "access"
    export const SECURESTORE_REFRESHTOKEN = "refresh"
    export const SECURESTORE_NOTIFTOKEN = 'notif'
    export const SECURESTORE_IDTOKEN = 'id'

    interface AuthenticationResult {
        success: boolean
        errorMessage?: string
    }

    /**
         * Function that initiates the login flow. It opens up with the in app browser and sends an authorization request to the API, which redirects the user to the backend login page.
         * If the credentials entered are valid, the OAuth Authorization Code flow will occur, which results in the user recieving an access token and refresh token which are then stored in {@link AuthStore}.
         * */
    export const authenticate = async (requestConfig: AuthRequestConfig, discovery: DiscoveryDocument): Promise<AuthenticationResult> => {
        console.log('[Authentication]: User is attempting to login, opening web portal login...')

        // initiate authentication request to the server
        const request = await loadAsync(requestConfig, discovery)

        // handle authentication response from the server
        const response = await request.promptAsync(discovery);

        // if succesful, prepare a request for an access/id token
        Iif (response.type == "success" && request.codeVerifier) {
            console.log('[Authentication]: User authentication was successful.')
            const tokenData = new URLSearchParams();
            tokenData.append('grant_type', 'authorization_code');
            tokenData.append('client_id', 'atlas.mobile');
            tokenData.append('code', response.params.code);
            tokenData.append('redirect_uri', request.redirectUri);
            tokenData.append('code_verifier', request.codeVerifier);
            console.log('[Authentication]: Attempting to retrieve access token...')

            // send the token request
            try {
                const response = await axios.post(API_URL + `/o/token/`, tokenData, {
                    headers: {
                        'Content-Type': "application/x-www-form-urlencoded"
                    },
                });

                const tokenResponse = response.data;

                // if its a successful response, decode the jwt id token, and store the tokens in the corresponding stores
                const idToken = jwt_decode(tokenResponse.id_token) as IdToken;

                await authStore.setAccessTokenAsync(tokenResponse.access_token);
                await authStore.setRefreshTokenAsync(tokenResponse.refresh_token);
                await authStore.setIdAsync(idToken.sub)

                console.log('[Authentication]: Tokens successfully retrieved.')

                return {success: true}
            } catch (error) {
                reportAxiosError("Something went wrong when retrieving access token", error);
                return {success: false, errorMessage: "Something went wrong while logging in. Please try again."}
            }
        }
    }

    /**
     * Logs the user out
     */
    export const logout = async () => {
        try {
            const tokenParams = new URLSearchParams();
            tokenParams.append('client_id', 'atlas.mobile');
            tokenParams.append('token', authStore.accessToken as string);

            const response = await axios.post(API_URL + `/o/revoke-token/`, tokenParams, {
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
            });

            await authStore.setAccessTokenAsync(null);
            await authStore.setRefreshTokenAsync(null);
            await authStore.setNotificationTokenAsync(null);
            await authStore.setIdAsync(null);
        } catch (error) {
            reportAxiosError("Something went wrong when retrieving access token", error);
        }
    }

        /**
         * If there is a refresh token available, attempts to use it to obtain a new, valid access token.
         * Used in {@link Atlas}
         * @memberOf useAuth
         */
        const refreshAccessToken = async () => {
            Iif (authStore.refreshToken) {
                try {
                    const tokenData = new URLSearchParams();
                    tokenData.append('grant_type', 'refresh_token');
                    tokenData.append('refresh_token', authStore.refreshToken);
                    tokenData.append('client_id', 'atlas.mobile');
                    console.log('[Authentication]: Attempting to refresh token...')
                    const { data: refreshResponseData } = await axios.post(API_URL + "/o/token/", tokenData, {
                        headers: { 'Content-Type': "application/x-www-form-urlencoded" }
                    });

                    await authStore.setRefreshTokenAsync(refreshResponseData.refresh_token);
                    await authStore.setAccessTokenAsync(refreshResponseData.access_token);

                    console.info('Successfully refreshed access token, re-attempting initial request...')
                }
                catch (error) {
                    reportAxiosError("[Authentication]: Error when trying to refresh access token", error);
                }
            }
        }

        export const reportAxiosError = (desc: string,  error: AxiosError, printResponse?: boolean) => {
            let errorString = `XHR error: ${desc}\nError code: ${error.response?.status}\nError message: ${error.message}`;

            Iif (printResponse) {
                errorString + "\nError response: " + error.response;
            }

            console.error(errorString);
        }