Skip to content

Commit

Permalink
chore: use hash for refresh token store
Browse files Browse the repository at this point in the history
  • Loading branch information
aseerkt committed Jul 26, 2024
1 parent f6e7837 commit 07787a0
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 16 deletions.
17 changes: 17 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
"pg": "^8.12.0",
"socket.io": "^4.7.5",
"swagger-autogen": "^2.23.7",
"swagger-ui-express": "^5.0.1"
"swagger-ui-express": "^5.0.1",
"uuid": "^10.0.0"
},
"devDependencies": {
"@eslint/js": "^9.6.0",
Expand All @@ -51,6 +52,7 @@
"@types/node": "^20.14.5",
"@types/pg": "^8.11.6",
"@types/swagger-ui-express": "^4.1.6",
"@types/uuid": "^10.0.0",
"drizzle-kit": "^0.22.8",
"eslint": "9.x",
"globals": "^15.7.0",
Expand Down
17 changes: 14 additions & 3 deletions server/src/common/controllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,15 @@ export const recreateAccessToken: RequestHandler = async (req, res) => {
}

try {
const refreshPayload = verifyRefreshToken(refreshToken) as UserPayload
const refreshPayload = verifyRefreshToken(refreshToken) as UserPayload & {
tokenId: string
}

const isValid = await isRefreshTokenValid(refreshPayload.id, refreshToken)
const isValid = await isRefreshTokenValid(
refreshPayload.id,
refreshToken.tokenId,
refreshToken,
)

if (!isValid) {
return notAuthorized(res)
Expand All @@ -61,7 +67,12 @@ export const recreateAccessToken: RequestHandler = async (req, res) => {

export const logout: RequestHandler = async (req, res, next) => {
try {
await invalidateRefreshToken(req.user!.id, req.cookies[config.jwtKey])
const refreshPayload = verifyRefreshToken(
req.cookies[config.jwtKey],
) as UserPayload & {
tokenId: string
}
await invalidateRefreshToken(req.user!.id, refreshPayload.tokenId)
clearRefreshTokenCookie(res)
} catch (error) {
next(error)
Expand Down
25 changes: 18 additions & 7 deletions server/src/redis/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,28 @@ export const redisKeys = {

// USER REFRESH TOKENS

export const addRefreshToken = (userId: number, token: string) => {
return redisClient.sadd(redisKeys.USER_TOKEN(userId), token)
export const addRefreshToken = (
userId: number,
tokenId: string,
token: string,
) => {
return redisClient.hset(redisKeys.USER_TOKEN(userId), tokenId, token)
}

export const invalidateRefreshToken = (userId: number, token: string) => {
return redisClient.srem(redisKeys.USER_TOKEN(userId), token)
export const invalidateRefreshToken = (userId: number, tokenId: string) => {
return redisClient.hdel(redisKeys.USER_TOKEN(userId), tokenId)
}

export const isRefreshTokenValid = async (userId: number, token: string) => {
const value = await redisClient.sismember(redisKeys.USER_TOKEN(userId), token)
return value == 1
export const isRefreshTokenValid = async (
userId: number,
tokenId: string,
token: string,
) => {
const redisToken = await redisClient.hget(
redisKeys.USER_TOKEN(userId),
tokenId,
)
return redisToken === token
}

// MEMBER ROLES
Expand Down
16 changes: 11 additions & 5 deletions server/src/utils/jwt.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { addRefreshToken } from '@/redis/handlers'
import { CookieOptions, Response } from 'express'
import jwt from 'jsonwebtoken'
import { v4 as uuidv4 } from 'uuid'
import { config } from '../config'

const refreshTokenCookieOptions: CookieOptions = {
Expand All @@ -26,11 +27,16 @@ export const verifyRefreshToken = (token: string) =>

export const signTokens = async (res: Response, payload: UserPayload) => {
const accessToken = signAccessToken(payload)
const refreshToken = jwt.sign(payload, config.refreshTokenSecret, {
expiresIn: config.refreshTokenExpiry,
})

await addRefreshToken(payload.id, refreshToken)
const tokenId = uuidv4()
const refreshToken = jwt.sign(
{ ...payload, tokenId },
config.refreshTokenSecret,
{
expiresIn: config.refreshTokenExpiry,
},
)

await addRefreshToken(payload.id, tokenId, refreshToken)

res.cookie(config.jwtKey, refreshToken, refreshTokenCookieOptions)

Expand Down

0 comments on commit 07787a0

Please sign in to comment.