All files / client/src/services loginServices.ts

100% Statements 26/26
100% Branches 0/0
100% Functions 9/9
100% Lines 25/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                                    520x     30x   30x             27x     520x     417x           417x           520x 21x 336x               520x 21x 21x 21x           520x 6x     520x     18x           18x     520x     15x           15x     520x     27x           24x    
import { ApiEndpoint, AuthEndpoint } from "shared/constants/apiEndpoints";
import {
  PostKompassiLoginRedirectRequest,
  PostKompassiLoginRequest,
  PostKompassiLoginResponse,
  PostLoginRequest,
  PostLoginResponse,
  PostSessionRecoveryRequest,
  PostSessionRecoveryResponse,
  PostUpdateUserEmailAddressRequest,
  PostUpdateUserEmailAddressResponse,
  PostVerifyKompassiLoginRequest,
  PostVerifyKompassiLoginResponse,
} from "shared/types/api/login";
import { api } from "client/utils/api";
import { saveKompassiLoginState } from "client/utils/sessionStorage";
import { LoginFormFields } from "client/views/login/components/LocalLoginForm";
 
export const postLogin = async (
  loginFormFields: LoginFormFields,
): Promise<PostLoginResponse> => {
  const { username, password } = loginFormFields;
 
  const response = await api.post<PostLoginResponse, PostLoginRequest>(
    ApiEndpoint.LOGIN,
    {
      username,
      password,
    },
  );
  return response.data;
};
 
export const postSessionRecovery = async (
  jwt: string,
): Promise<PostSessionRecoveryResponse> => {
  const response = await api.post<
    PostSessionRecoveryResponse,
    PostSessionRecoveryRequest
  >(ApiEndpoint.SESSION_RESTORE, {
    jwt,
  });
  return response.data;
};
 
// getRandomValues rather than randomUUID: the latter is only available in
// secure contexts, and the app is served over plain http in local and
// containerized runs
const generateLoginState = (): string => {
  const bytes = crypto.getRandomValues(new Uint8Array(16));
  return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join(
    "",
  );
};
 
// The state is generated and kept here rather than server-side so that any
// server instance can serve the callback, and is checked against the value
// Kompassi echoes back before the code is exchanged
export const postKompassiLoginRedirect = async (): Promise<void> => {
  const state = generateLoginState();
  saveKompassiLoginState(state);
  await api.post<unknown, PostKompassiLoginRedirectRequest>(
    AuthEndpoint.KOMPASSI_LOGIN,
    { state },
  );
};
 
export const postKompassiLogoutRedirect = async (): Promise<void> => {
  await api.post(AuthEndpoint.KOMPASSI_LOGOUT);
};
 
export const postKompassiLoginCallback = async (
  code: string,
): Promise<PostKompassiLoginResponse> => {
  const response = await api.post<
    PostKompassiLoginResponse,
    PostKompassiLoginRequest
  >(AuthEndpoint.KOMPASSI_LOGIN_CALLBACK, {
    code,
  });
  return response.data;
};
 
export const postVerifyKompassiLogin = async (
  username: string,
): Promise<PostVerifyKompassiLoginResponse> => {
  const response = await api.post<
    PostVerifyKompassiLoginResponse,
    PostVerifyKompassiLoginRequest
  >(ApiEndpoint.VERIFY_KOMPASSI_LOGIN, {
    username,
  });
  return response.data;
};
 
export const postUpdateUserEmailAddress = async (
  email: string,
): Promise<PostUpdateUserEmailAddressResponse> => {
  const response = await api.post<
    PostUpdateUserEmailAddressResponse,
    PostUpdateUserEmailAddressRequest
  >(ApiEndpoint.UPDATE_USER_EMAIL_ADDRESS, {
    email,
  });
  return response.data;
};