add ms to access token to randomize it

master
Peter Babič 5 years ago
parent 316d8c524f
commit 28666f00f9
Signed by: peter.babic
GPG Key ID: 4BB075BC1884BA40
  1. 1
      src/server/UserResolver.spec.ts
  2. 86
      src/server/userResolver/auth.ts

@ -60,6 +60,7 @@ describe("resolver of user", () => {
expect(jwtLifetime).toBeGreaterThanOrEqual(oneMinute) expect(jwtLifetime).toBeGreaterThanOrEqual(oneMinute)
expect(jwtLifetime).not.toBeGreaterThan(sixteenMinutes) expect(jwtLifetime).not.toBeGreaterThan(sixteenMinutes)
expect(jwtPayload.userId).toBe(user.id) expect(jwtPayload.userId).toBe(user.id)
expect(jwtPayload.ms).toBeLessThan(1000)
expect(response.errors).toBeUndefined() expect(response.errors).toBeUndefined()
}) })
) )

@ -4,84 +4,86 @@ import { sign as jwtSign, verify as jwtVerify } from "jsonwebtoken"
import { AuthChecker } from "type-graphql" import { AuthChecker } from "type-graphql"
export const hashPassword = async (password: string) => export const hashPassword = async (password: string) =>
await argonHash(password, { type: argon2id }) await argonHash(password, { type: argon2id })
export const comparePasswords = async (hash: string, plain: string) => { export const comparePasswords = async (hash: string, plain: string) => {
if (!(await argonVerify(hash, plain, { type: argon2id }))) { if (!(await argonVerify(hash, plain, { type: argon2id }))) {
throw new Error("Passwords do not match") throw new Error("Passwords do not match")
} }
return true return true
} }
export const signAccessToken = (payload: ContextPayload) => { export const signAccessToken = (payload: ContextPayload) => {
const accessTokenSecret = process.env.ACCESS_SECRET as string const accessTokenSecret = process.env.ACCESS_SECRET as string
const payloadWithMs = { ...payload, ms: Date.now() % 1000 }
return jwtSign(payload, accessTokenSecret, { return jwtSign(payloadWithMs, accessTokenSecret, {
expiresIn: parseInt(process.env.ACCESS_EXPIRY as string), expiresIn: parseInt(process.env.ACCESS_EXPIRY as string),
}) })
} }
export const signRefreshToken = (payload: ContextPayload) => { export const signRefreshToken = (payload: ContextPayload) => {
const accessTokenSecret = process.env.REFRESH_SECRET as string const accessTokenSecret = process.env.REFRESH_SECRET as string
return jwtSign(payload, accessTokenSecret, { return jwtSign(payload, accessTokenSecret, {
expiresIn: parseInt(process.env.REFRESH_EXPIRY as string), expiresIn: parseInt(process.env.REFRESH_EXPIRY as string),
}) })
} }
export const verifiedAccessTokenPayload = (token: string) => { export const verifiedAccessTokenPayload = (token: string) => {
const accessTokenSecret = process.env.ACCESS_SECRET as string const accessTokenSecret = process.env.ACCESS_SECRET as string
return jwtVerify(token, accessTokenSecret) as JWTPayload return jwtVerify(token, accessTokenSecret) as JWTPayload
} }
export const verifiedRefreshTokenPayload = (token: string) => { export const verifiedRefreshTokenPayload = (token: string) => {
const refreshTokenSecret = process.env.REFRESH_SECRET as string const refreshTokenSecret = process.env.REFRESH_SECRET as string
return jwtVerify(token, refreshTokenSecret) as JWTPayload return jwtVerify(token, refreshTokenSecret) as JWTPayload
} }
export const accessTokenWithRefreshCookie = (userId: number, res: Response) => { export const accessTokenWithRefreshCookie = (userId: number, res: Response) => {
const accessToken = signAccessToken({ userId }) const accessToken = signAccessToken({ userId })
const refreshExpiryMs = parseInt(process.env.REFRESH_EXPIRY as string) * 1000 const refreshExpiryMs = parseInt(process.env.REFRESH_EXPIRY as string) * 1000
res.cookie("rt", signRefreshToken({ userId }), { res.cookie("rt", signRefreshToken({ userId }), {
httpOnly: true, httpOnly: true,
path: "/refresh_token", path: "/refresh_token",
expires: new Date(new Date().getTime() + refreshExpiryMs), expires: new Date(new Date().getTime() + refreshExpiryMs),
}) })
return accessToken return accessToken
} }
export const customAuthChecker: AuthChecker<Context> = ({ context }) => { export const customAuthChecker: AuthChecker<Context> = ({ context }) => {
try { try {
const authHeader = context.req.headers["authorization"] const authHeader = context.req.headers["authorization"]
const accessToken = authHeader!.split(" ")[1] const accessToken = authHeader!.split(" ")[1]
const accessTokenPayload = verifiedAccessTokenPayload(accessToken) const accessTokenPayload = verifiedAccessTokenPayload(accessToken)
context.payload = accessTokenPayload as ContextPayload context.payload = accessTokenPayload as ContextPayload
return true return true
} catch (error) { } catch (error) {
throw new Error("the valid authorization header is required: " + error) throw new Error("the valid authorization header is required: " + error)
} }
} }
export const contextFunction = ({ req, res }: Context) => ({ req, res }) export const contextFunction = ({ req, res }: Context) => ({ req, res })
export type Context = { export type Context = {
req: Request req: Request
res: Response res: Response
payload?: ContextPayload payload?: ContextPayload
} }
type ContextPayload = { type ContextPayload = {
userId: number userId: number
} }
type JWTPayload = { type JWTPayload = {
userId: number userId: number
iat: number iat: number
exp?: number exp?: number
ms?: number
} }

Loading…
Cancel
Save