All files / src/services auth.ts

98.75% Statements 79/80
94.11% Branches 16/17
85.71% Functions 6/7
98.75% Lines 79/80

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 1111x     1x 1x     1x     1x     1x   1x 1x 1x 1x 1x 1x 1x             1x 2x 2x 2x 2x 2x 2x 2x   2x   2x 2x 2x 2x 2x 2x 2x 2x   1x 2x 2x 2x 2x 2x 2x           1x 4x 4x 4x 4x   4x   4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x   3x   4x 2x 2x 2x 1x 1x 1x 2x 2x   1x 4x 4x 1x 1x 1x 1x 1x 1x   1x 1x 4x 4x 4x 4x      
import { Effect, pipe, Schema } from 'effect';
 
// Constants
import { END_POINTS, ERROR_MESSAGES, ENV } from '@/constants';
import { STATUS_CODE, STATUS_MESSAGES } from '@shared/constants';
 
// Utils
import { handleActionError } from '@/utils/errorHandlers';
 
// Services
import { httpClient } from '@/services/httpClient';
 
// Types
import { HttpMethod, HttpClientConfig, ApiError } from '@shared/types';
 
const GetTokenSchema = Schema.Struct({
  token: Schema.String.pipe(
    Schema.minLength(1, {
      message: () => STATUS_MESSAGES[STATUS_CODE.UNAUTHORIZED]!,
    }),
  ),
});
 
interface GetCustomTokenOptions {
  token: string;
  requestConfig?: HttpClientConfig;
}
 
const getCustomToken = async ({
  token,
  requestConfig,
}: GetCustomTokenOptions) =>
  Effect.runPromise(
    pipe(
      Effect.tryPromise({
        try: async () => {
          // Validate payload
          await Schema.decodeUnknown(GetTokenSchema)({ token });
 
          const response = await httpClient<{ token: string }>(
            END_POINTS.GET_CUSTOM_TOKEN,
            {
              method: HttpMethod.POST,
              token,
              ...requestConfig,
            },
          );
 
          return response;
        },
        catch: (error) => handleActionError<{ token: string }>(error),
      }),
      Effect.catchAll((error) => Effect.succeed(error)),
    ),
  );
 
interface RefreshTokenParams {
  token: string;
}
 
const getRefreshToken = async ({ token }: RefreshTokenParams) =>
  Effect.runPromise(
    pipe(
      Effect.tryPromise({
        try: async () => {
          // Validate payload
          await Schema.decodeUnknown(GetTokenSchema)({ token });
 
          const response = await fetch(
            `https://securetoken.googleapis.com/v1/token?key=${ENV.FIREBASE_API_KEY}`,
            {
              method: HttpMethod.POST,
              headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
              body: new URLSearchParams({
                grant_type: 'refresh_token',
                refresh_token: token,
              }),
            },
          );
 
          const json = await response.json().catch(() => null);
 
          if (!json || json?.error) {
            return {
              data: null,
              error: json?.error || {
                status: response.status,
                message: ERROR_MESSAGES.FETCH_FAILED(response.status),
              },
            };
          }
 
          return { data: json };
        },
        catch: (error) => ({
          data: null,
          error: {
            status: STATUS_CODE.INTERNAL_SERVER_ERROR,
            message:
              error instanceof Error
                ? error.message
                : STATUS_MESSAGES[STATUS_CODE.INTERNAL_SERVER_ERROR],
          },
        }),
      }),
      Effect.catchAll((error) => Effect.succeed(error)),
    ),
  );
 
export { getCustomToken, getRefreshToken };