Skip to content
Snippets Groups Projects
authorizationHook.tsx 4.82 KiB
import { useAppDispatch, useAuthorizationCache } from '../store';
import { authorized, unauthorized, updateAccessToken } from '../store/authSlice';

/**
 * getNewAccessToken gets a new access token using the refresh-token cookie
 * @returns an authResponse containing details
 */
async function getNewAccessToken(
  domain: string,
  accessToken: string
): Promise<authResponse> {
  // If we have an access token already, append it to the url as a query param to keep sessionID the same
  let url = `https://${domain}/auth/refresh`;
  if (accessToken != '') {
    url += '?access_token=' + accessToken;
  }

  try {
    const response = await fetch(url, {
      method: 'GET',
      credentials: 'include',
    });

    if (!response.ok) {
      // User is not authorized
      const text = await response.text();
      console.error('User UNAUTHORIZED', text);

      return { success: false };
      // throw Error(response.statusText);
    }

    const json = await response.json();
    return {
      success: true,
      accessToken: json.accessToken,
      userID: json.userID,
      sessionID: json.sessionID,
    };
  } catch (error) {
    console.error('Error authorizing user', error);
    return { success: false };
  }
}

type authResponse = {
  success: boolean;
  accessToken?: string;
  userID?: string;
  sessionID?: string;
};

export type useIsAuthorizedState = {
  userID?: string;
  sessionID?: string;
  accessToken: string;
  authorized?: boolean;
};

export function useAuthorization(domain: string) {
  const dispatch = useAppDispatch();
  const auth = useAuthorizationCache();

  /**
   * refreshTokens refreshes tokens
   */
  async function refreshTokens() {
    console.log('refreshing tokens');
    // Get a new access + refresh token pair
    const authResponse = await getNewAccessToken(domain, auth.accessToken);

    if (authResponse.success) {
      // Set the new access token
      dispatch(updateAccessToken(authResponse.accessToken ?? ''));

      if (auth.accessToken !== '') {
        // Initialize the new refresh token
        initializeRefreshToken();
      }
    }
  }

  // /**
  //  * initializeRefreshToken attempts to initialize a refresh token
  //  */
  // async function refreshRefreshToken() {
  //   fetch(
  //     'https://${domain}/auth/refresh?access_token=' +
  //       state.accessToken,
  //     {
  //       method: 'GET',
  //       credentials: 'include',
  //     }
  //   )
  //     .then((response) => {
  //       if (!response.ok) {
  //         throw Error(response.statusText);
  //       }
  //     })
  //     .catch((error) => {
  //       console.error(error);
  //     });
  // }

  /**
   * initializeRefreshToken attempts to initialize a refresh token
   */
  async function initializeRefreshToken() {
    fetch(`https://${domain}/auth/refresh`, {
      method: 'POST',
      credentials: 'include',
    })
      .then((response) => {
        if (!response.ok) {
          console.error(response.statusText);
          // throw Error(response.statusText);
        }
      })
      .catch((error) => {
        console.error(error);
      });
  }

  // MARK: Setters
  /**
   * SetAccessToken sets the current access token (should only be called by the sign-in component)
   * @param accessToken
   */
  async function SetAccessToken(accessToken: string) {
    dispatch(updateAccessToken(accessToken));
    console.log('set access token', auth.accessToken);

    const result = await AuthorizeFromCache();
    if (result) {
      // Activate the refresh token TODO
      initializeRefreshToken();

      // Start the automatic refreshing every 10 minutes
      setInterval(() => {
        refreshTokens();
      }, 10 * 60 * 1000);
    } else {
      console.error(
        'Failed Logging in due to refresh token initialization',
        result
      );
      // throw Error('Failed Logging in');
    }
  }

  /**
   * Authorize attempts to authorize using a refresh-token set as a cookie. If the user has been inactive for more than 7 days this cookie will be gone.
   * @returns true is authorization was successful, else returns false
   */
  async function AuthorizeFromCache(): Promise<boolean> {
    


    // Attempt to log in with a refresh-token
    const authResponse = await getNewAccessToken(domain, auth.accessToken);

    // If the request was a success, we have an accessToken, userID and sessionID
    if (authResponse.success) {
      console.log('SUCCESS authorize');

      console.log(auth);
      // Store them
      await dispatch(
        authorized({
          userID: authResponse.userID ?? '',
          sessionID: authResponse.sessionID ?? '',
          accessToken: authResponse.accessToken ?? '',
          authorized: true,
        })
      );
    } else {
      await dispatch(
        unauthorized()
      );
    }

    return authResponse.success;
  }

  return {
    AuthorizeFromCache,
    SetAccessToken,
    auth,
  };
}