Advertisement
aldikhan13

Typescript Secure AccessToken And Refresh Token

Nov 13th, 2021
945
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // for more example code you can check my repository here:
  2. // https://github.com/restuwahyu13/express-book-store/blob/main/src/libs/lib.jwt.ts
  3.  
  4. import { Request } from 'express'
  5. import { StatusCodes as Status } from 'http-status-codes'
  6. import { decrypt, encrypt } from 'jwt-transform'
  7. import { NodeDiskStorage } from 'node-disk-storage'
  8. import jwt from 'jsonwebtoken'
  9. import { BookStoreError } from '@helpers/helper.error'
  10. import { convertTime } from '@helpers/helper.convertTime'
  11.  
  12. const nds: InstanceType<typeof NodeDiskStorage> = new NodeDiskStorage({ compress: true })
  13. const secretKey: string = process.env.JWT_SECRET_KEY || ''
  14. const typeTime: Record<string, any> = {
  15.   days: 'd',
  16.   minute: 'm',
  17.   second: 's'
  18. }
  19.  
  20. interface IToken {
  21.   accessToken: string
  22.   refreshToken: string
  23.   accessTokenExpired: string
  24.   refreshTokenExpired: string
  25. }
  26.  
  27. interface ITokenMixed {
  28.   accessToken: string
  29.   refreshToken: string
  30.   accessTokenExpired: string
  31.   refreshTokenExpired: string
  32.   status: string
  33. }
  34.  
  35. interface Ioptions {
  36.   expiredAt: number
  37.   type: string
  38. }
  39.  
  40. const healthToken = async (accessToken: string): Promise<boolean> => {
  41.   const getAccessToken: any = (await nds.get('accessToken')) || accessToken
  42.  
  43.   if (!getAccessToken) {
  44.     throw { code: Status.BAD_REQUEST, message: 'Get accessTokenfrom disk failed' }
  45.   }
  46.  
  47.   const decryptAccessToken: string = await decrypt(getAccessToken, 20)
  48.   const decodedAccesToken: string | jwt.JwtPayload = jwt.verify(decryptAccessToken, secretKey, { audience: 'book-store-api' })
  49.  
  50.   if (!decodedAccesToken || decodedAccesToken instanceof jwt.JsonWebTokenError) {
  51.     return false
  52.   }
  53.  
  54.   return true
  55. }
  56.  
  57. export const signToken = async (data: Record<string, any>, options: Ioptions): Promise<Record<string, any> | string> => {
  58.   try {
  59.     const accessToken: string = jwt.sign({ ...data }, secretKey, {
  60.       expiresIn: `${options.expiredAt}${typeTime[options.type]}`,
  61.       audience: 'book-store-api'
  62.     })
  63.  
  64.     const refreshToken: string = jwt.sign({ ...data }, secretKey, { expiresIn: '30d', audience: 'book-store-api' })
  65.  
  66.      const encryptedAccessToken: string = await encrypt(accessToken, 20)
  67.      const encryptedRefreshToken: string = await encrypt(refreshToken, 20)
  68.  
  69.     const setAccessToken: boolean | undefined = nds.set('accessToken', encryptedAccessToken)
  70.     const setRefreshToken: boolean | undefined = nds.set('refreshToken', encryptedRefreshToken)
  71.  
  72.     if (!setAccessToken || !setRefreshToken) {
  73.       throw { code: Status.BAD_REQUEST, message: 'Store accessToken and refreshToken into disk failed' }
  74.     }
  75.  
  76.     const token: IToken = {
  77.       accessToken: await encrypt(accessToken, 20),
  78.       refreshToken: await encrypt(refreshToken, 20),
  79.       accessTokenExpired: `${convertTime(options.expiredAt as number, 'days')} Days`,
  80.       refreshTokenExpired: `${convertTime(30, 'days')} Days`
  81.     }
  82.  
  83.     return token
  84.   } catch (e: any) {
  85.     return Promise.reject(new BookStoreError('Generate accessToken and refreshToken failed' || e.message))
  86.   }
  87. }
  88.  
  89. export const verifyToken = async (accessToken: string): Promise<Record<string, any> | string> => {
  90.   try {
  91.     const getAccessToken: string | undefined = nds.get('accessToken') || accessToken
  92.  
  93.     if (!getAccessToken) {
  94.       throw { code: Status.BAD_REQUEST, message: 'Get accessToken from disk failed' }
  95.     }
  96.  
  97.     const decryptAccessToken: string = await decrypt(getAccessToken, 20)
  98.     const decodedToken: string | jwt.JwtPayload = jwt.verify(decryptAccessToken, secretKey, { audience: 'book-store-api' })
  99.  
  100.     return decodedToken
  101.   } catch (e: any) {
  102.     return Promise.reject(new BookStoreError('Verified accessToken expired or invalid'))
  103.   }
  104. }
  105.  
  106. export const refreshToken = async (refreshToken: string, options: Ioptions): Promise<Record<string, any> | string> => {
  107.   try {
  108.     let token: ITokenMixed
  109.     const getAccessToken: any = await nds.get('accessToken')
  110.     const getRefreshToken: any = (await nds.get('refreshToken')) || refreshToken
  111.  
  112.     const decryptAccessToken: string = await decrypt(getRefreshToken, 20)
  113.     const decryptRefreshToken: string = await decrypt(getAccessToken, 20)
  114.  
  115.     if (!healthToken(getAccessToken)) {
  116.       if (!getAccessToken || !getRefreshToken) {
  117.         throw { code: Status.BAD_REQUEST, message: 'Get accessToken and refreshToken from disk failed' }
  118.       }
  119.  
  120.       const getAccessTokenData: jwt.JwtPayload = jwt.decode(decryptAccessToken) as any
  121.       const getRefreshTokenData: jwt.JwtPayload = jwt.decode(decryptRefreshToken) as any
  122.  
  123.       const newAccessToken: string = jwt.sign({ ...getAccessTokenData }, secretKey, {
  124.         expiresIn: `${options.expiredAt}${typeTime[options.type]}`,
  125.         audience: 'book-store-api'
  126.       })
  127.  
  128.       const newRefreshToken: string = jwt.sign({ ...getRefreshTokenData }, secretKey, {
  129.         expiresIn: '30d',
  130.         audience: 'book-store-api'
  131.       })
  132.  
  133.       const setAccessToken: boolean | undefined = nds.set('accessToken', newAccessToken)
  134.       const setRefreshToken: boolean | undefined = nds.set('refreshToken', newRefreshToken)
  135.  
  136.       if (!setAccessToken || !setRefreshToken) {
  137.         throw { code: Status.BAD_REQUEST, message: 'Store accessToken and refreshToken into disk failed' }
  138.       }
  139.  
  140.       token = {
  141.         status: 'AccessToken Not Health, and this is new accessToken and refreshToken',
  142.         accessToken: await encrypt(newAccessToken, 20),
  143.         refreshToken: await encrypt(newRefreshToken, 20),
  144.         accessTokenExpired: `${convertTime(options.expiredAt as number, 'days')} Days`,
  145.         refreshTokenExpired: `${convertTime(30, 'days')} Days`
  146.       }
  147.     } else {
  148.       token = {
  149.         status: 'AccessToken Is Health, and this is old accessToken and refreshToken',
  150.         accessToken: await encrypt(decryptAccessToken, 20),
  151.         refreshToken: await encrypt(decryptRefreshToken, 20),
  152.         accessTokenExpired: `${convertTime(options.expiredAt as number, 'days')} Days`,
  153.         refreshTokenExpired: `${convertTime(30, 'days')} Days`
  154.       }
  155.     }
  156.  
  157.     return token
  158.   } catch (e: any) {
  159.     return Promise.reject(new BookStoreError('Generate new accessToken and refreshToken failed' || e.message))
  160.   }
  161. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement