· 3 years ago · Nov 13, 2021, 02:20 PM
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
4import { Request } from 'express'
5import { StatusCodes as Status } from 'http-status-codes'
6import { decrypt, encrypt } from 'jwt-transform'
7import { NodeDiskStorage } from 'node-disk-storage'
8import jwt from 'jsonwebtoken'
9import { BookStoreError } from '@helpers/helper.error'
10import { convertTime } from '@helpers/helper.convertTime'
11
12const nds: InstanceType<typeof NodeDiskStorage> = new NodeDiskStorage({ compress: true })
13const secretKey: string = process.env.JWT_SECRET_KEY || ''
14const typeTime: Record<string, any> = {
15 days: 'd',
16 minute: 'm',
17 second: 's'
18}
19
20interface IToken {
21 accessToken: string
22 refreshToken: string
23 accessTokenExpired: string
24 refreshTokenExpired: string
25}
26
27interface ITokenMixed {
28 accessToken: string
29 refreshToken: string
30 accessTokenExpired: string
31 refreshTokenExpired: string
32 status: string
33}
34
35interface Ioptions {
36 expiredAt: number
37 type: string
38}
39
40const 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
57export 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
89export 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
106export 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}