token.ts (2288B)
1 import { getAccessTokenCookieName } from '@/lib/env' 2 import { sign, verify, JwtPayload } from 'jsonwebtoken' 3 import { NextApiRequest } from 'next' 4 5 const TOKEN_ALGORITHM = 'HS256' 6 export const TOKEN_EXPIRES_IN = 5 * 60 // 5분 7 export const REFRESH_TOKEN_EXPIRES_IN = 30 * 24 * 60 * 60 // 30일 8 9 export interface AccessTokenPayload extends JwtPayload { 10 tid?: string 11 uid?: number 12 } 13 14 export function getTokenSecret () { 15 return process.env.WIKI_JWT_SECRET ?? 'dangerously_insecure_s3cr3t' 16 } 17 18 /** 19 * JWT를 발급합니다. 20 * @returns [accessToken, refreshToken, tokenId] 21 */ 22 export function signToken ( 23 tid: string, 24 uid: number, 25 ): [string, string] { 26 const accessToken = sign( 27 { tid, uid }, 28 getTokenSecret(), 29 { 30 algorithm: TOKEN_ALGORITHM, 31 expiresIn: TOKEN_EXPIRES_IN, 32 }, 33 ) 34 35 const refreshToken = sign( 36 { 37 tid: tid, 38 uid: uid, 39 refresh: true, 40 }, 41 getTokenSecret(), 42 { 43 algorithm: TOKEN_ALGORITHM, 44 expiresIn: REFRESH_TOKEN_EXPIRES_IN, 45 }, 46 ) 47 48 return [ 49 accessToken, 50 refreshToken, 51 ] 52 } 53 54 export async function verifyToken (token: string) { 55 return new Promise((resolve, reject) => { 56 verify(token, getTokenSecret(), { 57 algorithms: [TOKEN_ALGORITHM], 58 }, (err, decoded) => { 59 if (err != null) { 60 reject(err) 61 return 62 } 63 64 resolve(decoded) 65 }) 66 }) 67 } 68 69 export async function authenticationFromCookies (cookies: NextApiRequest['cookies']) { 70 const accessToken = cookies[getAccessTokenCookieName()] 71 if (accessToken == null) { 72 return null 73 } 74 75 try { 76 const decoded = await verifyToken(accessToken) 77 return decoded as AccessTokenPayload 78 } catch (e) { 79 console.log('token: failed to verify access token', e) 80 return null 81 } 82 } 83 84 export async function getUserIdFromAccessToken (accessToken?: string): Promise<number | null> { 85 if (accessToken == null) { 86 return null 87 } 88 89 const decoded = await new Promise((resolve, reject) => { 90 verify(accessToken, getTokenSecret(), (err, decoded) => { 91 if (err) { 92 reject(err) 93 } else { 94 resolve(decoded) 95 } 96 }) 97 }) 98 99 if (typeof decoded !== 'object' || decoded == null || !('uid' in decoded)) { 100 return null 101 } 102 103 return parseInt(decoded.uid as string, 10) 104 }