All files / server/src/utils jwt.ts

93.75% Statements 30/32
83.33% Branches 10/12
100% Functions 7/7
93.75% Lines 30/32

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  39x             39x 1068x         1068x       1068x     39x 2260x 2260x 2204x   2204x                     740x               740x                 39x 317x 317x 317x 1x   316x     39x 3328x   1077x     1840x     411x             39x       1213x 376x 1107x     376x 398x         1188x    
import jsonwebtoken from "jsonwebtoken";
const { TokenExpiredError } = jsonwebtoken;
type SignOptions = jsonwebtoken.SignOptions;
import { config } from "shared/config";
import { JWTBody, JWTBodySchema, JWTResponse } from "server/types/jwtTypes";
import { UserGroup } from "shared/types/models/user";
import { exhaustiveSwitchGuard } from "shared/utils/exhaustiveSwitchGuard";
 
export const getJWT = (userGroup: UserGroup, username: string): string => {
  const payload = {
    username,
    userGroup,
  };
 
  const options: SignOptions = {
    expiresIn: "14 days",
  };
 
  return jsonwebtoken.sign(payload, getSecret(userGroup), options);
};
 
export const verifyJWT = (jwt: string, userGroup: UserGroup): JWTResponse => {
  try {
    const jwtBody = jsonwebtoken.verify(jwt, getSecret(userGroup));
    const result = JWTBodySchema.parse(jwtBody);
 
    return {
      body: {
        username: result.username,
        userGroup: result.userGroup,
        iat: result.iat,
        exp: result.exp,
      },
      status: "success",
      message: "success",
    };
  } catch (error) {
    Iif (error instanceof TokenExpiredError) {
      return {
        status: "error",
        message: "expired jwt",
        body: { username: "", userGroup: UserGroup.USER, iat: 0, exp: 0 },
      };
    }
 
    return {
      status: "error",
      message: "unknown jwt error",
      body: { username: "", userGroup: UserGroup.USER, iat: 0, exp: 0 },
    };
  }
};
 
// Be careful: this does not verify jwt signature
export const decodeJWT = (jwt: string): JWTBody | null => {
  const decodedJwt = jsonwebtoken.decode(jwt);
  const result = JWTBodySchema.safeParse(decodedJwt);
  if (!result.success) {
    return null;
  }
  return result.data;
};
 
const getSecret = (userGroup: UserGroup): string => {
  switch (userGroup) {
    case UserGroup.ADMIN: {
      return config.server().jwtSecretKeyAdmin;
    }
    case UserGroup.USER: {
      return config.server().jwtSecretKey;
    }
    case UserGroup.HELPER: {
      return config.server().jwtSecretKeyHelp;
    }
    default:
      return exhaustiveSwitchGuard(userGroup);
  }
};
 
export const getJwtResponse = (
  jwt: string,
  requiredUserGroup: UserGroup | UserGroup[],
): JWTResponse => {
  if (Array.isArray(requiredUserGroup)) {
    const responses = requiredUserGroup.map((userGroup) => {
      return verifyJWT(jwt, userGroup);
    });
 
    return (
      responses.find((response) => response.status === "success") ??
      responses[0]
    );
  }
 
  return verifyJWT(jwt, requiredUserGroup);
};