Improved passing of req, res, and tokens

This commit is contained in:
aronmal 2023-04-10 18:51:38 +02:00
parent a8e5b57363
commit 39ddf8fde9
Signed by: aronmal
GPG key ID: 816B7707426FC612
17 changed files with 134 additions and 170 deletions

View file

@ -1,19 +1,17 @@
import { Player } from "@prisma/client"
import bcrypt from "bcrypt"
import errors from "../errors"
import sendError from "./sendError"
import { NextApiRequest, NextApiResponse } from "next"
import sendError, { API } from "./sendError"
export default async function checkPasswordIsValid(
req: NextApiRequest,
res: NextApiResponse,
context: API,
player: Player,
password: string,
next: () => void
) {
// Validate for correct password
const result = await bcrypt.compare(password, player.passwordHash)
if (!result) return sendError(req, res, errors.wrongPassword)
if (!result) return sendError(context, errors.wrongPassword)
return next()
}

View file

@ -1,34 +1,29 @@
import { TokenType } from "@prisma/client"
import jwt from "jsonwebtoken"
import errors from "../errors"
import jwtVerifyCatch from "../jwtVerifyCatch"
import { NextApiRequest, NextApiResponse } from "next"
import sendError from "./sendError"
import sendError, { API } from "./sendError"
import { IdToken, RawToken } from "./createTokenDB"
async function checkTokenIsValid(
req: NextApiRequest,
res: NextApiResponse,
rawToken: [string, TokenType],
next: (tokenBody: jwt.JwtPayload) => void
context: API,
rawToken: RawToken,
next: (token: IdToken) => void
) {
const [tokenValue, tokenType] = rawToken
const { value, type } = rawToken
// Verify the token and get the payload
let tokenData: string | jwt.JwtPayload
let data: string | jwt.JwtPayload
try {
tokenData = jwt.verify(
tokenValue,
process.env.ACCESS_TOKEN_SECRET as string
)
data = jwt.verify(value, process.env.ACCESS_TOKEN_SECRET as string)
} catch (err: any) {
// Deal with the problem in more detail
return sendError(req, res, jwtVerifyCatch(tokenType, err))
return sendError(context, jwtVerifyCatch(type, err))
}
// Making sure the token data is not a string (because it should be an object)
if (typeof tokenData === "string")
return sendError(req, res, errors.tokenWasString(tokenType, tokenValue))
if (typeof data === "string")
return sendError(context, errors.tokenWasString(type, value))
return next(tokenData)
return next({ id: data.id, type })
}
export default checkTokenIsValid

View file

@ -2,6 +2,15 @@ import { Player, Token, TokenType } from "@prisma/client"
import jwt from "jsonwebtoken"
import prisma from "../../prisma"
export interface RawToken {
value: string
type: TokenType
}
export interface IdToken {
id: string
type: TokenType
}
const tokenLifetime = {
REFRESH: 172800,
ACCESS: 15,
@ -10,7 +19,7 @@ const tokenLifetime = {
export default async function createTokenDB(
player: Player,
newTokenType: TokenType,
next: (token: [string, Token]) => void
next: (newToken: RawToken, newTokenDB: Token) => void
) {
// Create token entry in DB
const newTokenDB = await prisma.token.create({
@ -32,5 +41,5 @@ export default async function createTokenDB(
{ expiresIn: tokenLifetime[newTokenType] }
)
return next([newToken, newTokenDB])
return next({ value: newToken, type: "ACCESS" }, newTokenDB)
}

View file

@ -1,12 +1,10 @@
import { Player, Token } from "@prisma/client"
import errors from "../errors"
import prisma from "../../prisma"
import { NextApiRequest, NextApiResponse } from "next"
import sendError from "./sendError"
import sendError, { API } from "./sendError"
export default async function getPlayerByIdDB(
req: NextApiRequest,
res: NextApiResponse,
context: API,
tokenDB: Token,
next: (player: Player) => void
) {
@ -17,7 +15,7 @@ export default async function getPlayerByIdDB(
},
})
if (!player) {
return sendError(req, res, errors.playerNotFound)
return sendError(context, errors.playerNotFound)
}
return next(player)

View file

@ -1,12 +1,10 @@
import { Player } from "@prisma/client"
import errors from "../errors"
import prisma from "../../prisma"
import sendError from "./sendError"
import { NextApiRequest, NextApiResponse } from "next"
import sendError, { API } from "./sendError"
export default async function getPlayerByNameDB(
req: NextApiRequest,
res: NextApiResponse,
context: API,
username: string,
next: (player: Player) => void
) {
@ -23,7 +21,7 @@ export default async function getPlayerByNameDB(
},
}),
]).catch(() => null)
if (player === null) return sendError(req, res, errors.playerNotFound)
if (player === null) return sendError(context, errors.playerNotFound)
return next(player)
}

View file

@ -1,30 +1,28 @@
import prisma from "../../prisma"
import jwt from "jsonwebtoken"
import { Token, TokenType } from "@prisma/client"
import { Token } from "@prisma/client"
import errors from "../errors"
import sendError from "./sendError"
import { NextApiRequest, NextApiResponse } from "next"
import sendError, { API } from "./sendError"
import { IdToken } from "./createTokenDB"
async function getTokenDB(
req: NextApiRequest,
res: NextApiResponse,
tokenBody: jwt.JwtPayload,
tokenType: TokenType,
context: API,
token: IdToken,
next: (tokenDB: Token) => void
) {
const { id, type } = token
// Find refresh token in DB
const tokenDB = await prisma.token.findUnique({
where: {
id: tokenBody.id,
id,
},
})
if (!tokenDB) return sendError(req, res, errors.tokenNotFound(tokenType))
if (!tokenDB) return sendError(context, errors.tokenNotFound(type))
if (tokenDB.used) return sendError(req, res, errors.tokenUsed)
if (tokenDB.used) return sendError(context, errors.tokenUsed)
await prisma.token.update({
where: {
id: tokenBody.id,
id,
},
data: {
used: true,

View file

@ -1,13 +1,11 @@
import { TokenType } from "@prisma/client"
import { NextApiRequest, NextApiResponse } from "next"
import sendError from "./sendError"
import sendError, { API } from "./sendError"
import { RawToken } from "./createTokenDB"
async function getTokenFromBody(
req: NextApiRequest,
res: NextApiResponse,
next: (refreshToken: [string, TokenType]) => void
context: API,
next: (accessToken: RawToken) => void
) {
const body = JSON.parse(req.body)
const body = JSON.parse(context.req.body)
// Checking for cookie presens, because it is necessary
if (
typeof body !== "object" ||
@ -15,14 +13,14 @@ async function getTokenFromBody(
!("token" in body) ||
typeof body.token !== "string"
)
return sendError(req, res, {
return sendError(context, {
message: "Unauthorized. No Access-Token.",
statusCode: 401,
solved: true,
})
const tokenValue = body.token
const value = body.token
return next([tokenValue, "ACCESS"])
return next({ value, type: "ACCESS" })
}
export default getTokenFromBody

View file

@ -1,19 +1,17 @@
import { TokenType } from "@prisma/client"
import { NextApiRequest, NextApiResponse } from "next"
import errors from "../errors"
import sendError from "./sendError"
import { RawToken } from "./createTokenDB"
import sendError, { API } from "./sendError"
async function getTokenFromCookie(
req: NextApiRequest,
res: NextApiResponse,
next: (refreshToken: [string, TokenType]) => void
context: API,
next: (refreshToken: RawToken) => void
) {
const tokenValue = req.cookies.token
const value = context.req.cookies.token
// Checking for cookie presens, because it is necessary
if (!tokenValue) return sendError(req, res, errors.noCookie)
if (!value) return sendError(context, errors.noCookie)
return next([tokenValue, "REFRESH"])
return next({ value, type: "REFRESH" })
}
export default getTokenFromCookie

View file

@ -1,23 +1,21 @@
import { NextApiRequest, NextApiResponse } from "next"
import sendError from "./sendError"
import sendError, { API } from "./sendError"
import errors from "../errors"
async function getUserFromBody(
req: NextApiRequest,
res: NextApiResponse,
context: API,
next: (username: string, password: string) => void
) {
const body = JSON.parse(req.body)
const body = JSON.parse(context.req.body)
if (
typeof body !== "object" ||
!body ||
!("username" in body) ||
typeof body.username !== "string"
)
return sendError(req, res, errors.noUsername)
return sendError(context, errors.noUsername)
const { username } = body
if (!("password" in body) || typeof body.password !== "string")
return sendError(req, res, errors.noPassword)
return sendError(context, errors.noPassword)
const { password } = body
return next(username, password)

View file

@ -1,11 +1,13 @@
import { NextApiRequest, NextApiResponse } from "next"
import logging from "../logging"
export default function sendError(
req: NextApiRequest,
res: NextApiResponse,
err: any
) {
export interface API {
req: NextApiRequest
res: NextApiResponse
}
export default function sendError(context: API, err: any) {
const { res, req } = context
// If something went wrong, let the client know with status 500
res.status(err.statusCode ?? 500).end()
logging(err.message, [err.type ?? (err.solved ? "debug" : "error")], req)

View file

@ -1,5 +1,5 @@
import { NextApiRequest, NextApiResponse } from "next"
import logging, { Logging } from "../logging"
import type { API } from "./sendError"
export interface Result<T> {
message: string
@ -8,11 +8,8 @@ export interface Result<T> {
type?: Logging[]
}
export default function sendResponse<T>(
req: NextApiRequest,
res: NextApiResponse<T>,
result: Result<T>
) {
export default function sendResponse<T>(context: API, result: Result<T>) {
const { req, res } = context
res.status(result.statusCode ?? 200)
result.body ? res.json(result.body) : res.end()
logging(result.message, result.type ?? ["debug"], req)

View file

@ -1,30 +0,0 @@
import { GetServerSidePropsContext, PreviewData } from "next"
import { ParsedUrlQuery } from "querystring"
import getTokenFromCookie from "../backend/components/getTokenFromCookie"
import checkTokenIsValid from "../backend/components/checkTokenIsValid"
import getTokenDB from "../backend/components/getTokenDB"
import getPlayerByIdDB from "../backend/components/getPlayerByIdDB"
import logging from "../backend/logging"
export default async function checkIsLoggedIn(
context: GetServerSidePropsContext<ParsedUrlQuery, PreviewData>
) {
const req: any = context.req
const res: any = context.res
const isLoggedIn = await getTokenFromCookie(req, res, (token) =>
checkTokenIsValid(req, res, token, (tokenBody) =>
getTokenDB(req, res, tokenBody, token[1], (tokenDB) =>
getPlayerByIdDB(req, res, tokenDB, (player) => !!player)
)
)
).catch(() => false)
logging(
"loginCheck " + (isLoggedIn ? true : "-> loggedIn: " + false),
["debug", "infoCyan"],
req
)
return isLoggedIn
}

View file

@ -6,7 +6,9 @@ import getTokenFromCookie from "../../lib/backend/components/getTokenFromCookie"
import checkTokenIsValid from "../../lib/backend/components/checkTokenIsValid"
import getTokenDB from "../../lib/backend/components/getTokenDB"
import getPlayerByIdDB from "../../lib/backend/components/getPlayerByIdDB"
import createTokenDB from "../../lib/backend/components/createTokenDB"
import createTokenDB, {
RawToken,
} from "../../lib/backend/components/createTokenDB"
import sendResponse from "../../lib/backend/components/sendResponse"
interface Data {
@ -17,26 +19,26 @@ export default async function auth(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
getTokenFromCookie(req, res, (token) => {
checkTokenIsValid(req, res, token, (tokenBody) => {
getTokenDB(req, res, tokenBody, token[1], (tokenDB) => {
getPlayerByIdDB(req, res, tokenDB, (player) => {
createTokenDB(player, "ACCESS", (newToken) => {
sendResponse(req, res, authResponse(newToken, tokenDB))
const context = { req, res }
getTokenFromCookie(context, (refreshToken) => {
checkTokenIsValid(context, refreshToken, (token) => {
getTokenDB(context, token, (tokenDB) => {
getPlayerByIdDB(context, tokenDB, (player) => {
createTokenDB(player, "ACCESS", (newToken, newTokenDB) => {
sendResponse(context, authResponse(newToken, newTokenDB, tokenDB))
})
})
})
})
}).catch((err) => sendError(req, res, err))
}).catch((err) => sendError(context, err))
}
function authResponse(newToken: [string, Token], tokenDB: Token) {
const [newTokenValue, newTokenDB] = newToken
function authResponse(newToken: RawToken, newTokenDB: Token, tokenDB: Token) {
// Successfull response
return {
message: `Access-Token generated: ${newTokenDB.id} with Refreshtoken-Token: ${tokenDB.id}`,
body: { token: newTokenValue },
body: { token: newToken.value },
type: ["debug", "info.cyan"] as Logging[],
}
}

View file

@ -16,15 +16,17 @@ export default async function data(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
return getTokenFromBody(req, res, (token) => {
checkTokenIsValid(req, res, token, (tokenBody) => {
getTokenDB(req, res, tokenBody, token[1], (tokenDB) => {
getPlayerByIdDB(req, res, tokenDB, (player) => {
sendResponse(req, res, dataResponse(player, tokenDB))
const context = { req, res }
return getTokenFromBody(context, (accessToken) => {
checkTokenIsValid(context, accessToken, (token) => {
getTokenDB(context, token, (tokenDB) => {
getPlayerByIdDB(context, tokenDB, (player) => {
sendResponse(context, dataResponse(player, tokenDB))
})
})
})
}).catch((err) => sendError(req, res, err))
}).catch((err) => sendError(context, err))
}
function dataResponse<T>(player: Player, tokenDB: Token) {

View file

@ -2,14 +2,17 @@ import { NextApiRequest, NextApiResponse } from "next"
import logging, { Logging } from "../../lib/backend/logging"
import getPlayerByNameDB from "../../lib/backend/components/getPlayerByNameDB"
import checkPasswordIsValid from "../../lib/backend/components/checkPasswordIsValid"
import createTokenDB from "../../lib/backend/components/createTokenDB"
import createTokenDB, {
IdToken,
RawToken,
} from "../../lib/backend/components/createTokenDB"
import sendResponse from "../../lib/backend/components/sendResponse"
import { setCookie } from "cookies-next"
import { Player, Token } from "@prisma/client"
import prisma from "../../lib/prisma"
import jwt from "jsonwebtoken"
import errors from "../../lib/backend/errors"
import sendError from "../../lib/backend/components/sendError"
import sendError, { API } from "../../lib/backend/components/sendError"
import getUserFromBody from "../../lib/backend/components/getUserFromBody"
interface Data {
@ -18,30 +21,32 @@ interface Data {
export default async function login(
req: NextApiRequest,
res: NextApiResponse<any>
res: NextApiResponse<Data>
) {
return preCheck(req, res, () =>
getUserFromBody(req, res, (username, password) =>
getPlayerByNameDB(req, res, username, (player) => {
checkPasswordIsValid(req, res, player, password, () => {
createTokenDB(player, "REFRESH", (newToken) => {
sendResponse(req, res, loginResponse(player, newToken, req, res))
const context = { req, res }
return preCheck(context, () =>
getUserFromBody(context, (username, password) =>
getPlayerByNameDB(context, username, (player) => {
checkPasswordIsValid(context, player, password, () => {
createTokenDB(player, "REFRESH", (newToken, newTokenDB) => {
sendResponse(
context,
loginResponse(context, player, newToken, newTokenDB)
)
})
})
})
)
).catch((err) => sendError(req, res, err))
).catch((err) => sendError(context, err))
}
async function preCheck(
req: NextApiRequest,
res: NextApiResponse,
next: () => void
) {
async function preCheck(context: API, next: () => void) {
const { req } = context
const oldRefreshToken = req.cookies.token
try {
if (!oldRefreshToken) return sendError(req, res, errors.noCookie)
if (!oldRefreshToken) return sendError(context, errors.noCookie)
// Check for old cookie, if unused invalidate it
const tokenData = jwt.verify(
oldRefreshToken,
@ -76,17 +81,18 @@ async function preCheck(
return next()
}
function loginResponse<T>(
function loginResponse(
context: API,
player: Player,
newToken: [string, Token],
req: NextApiRequest,
res: NextApiResponse<T>
newToken: RawToken,
newTokenDB: Token
) {
const { req, res } = context
// const { player, req, res } = payload
const [newTokenValue, newTokenDB] = newToken
const { value } = newToken
// Set login cookie
setCookie("token", newTokenValue, {
setCookie("token", value, {
req,
res,
maxAge: 172800,
@ -96,15 +102,6 @@ function loginResponse<T>(
path: "/api",
})
// deleteCookie("token", {
// req,
// res,
// httpOnly: true,
// sameSite: true,
// secure: true,
// path: "/api",
// })
// Successfull response
return {
message:

View file

@ -16,13 +16,15 @@ export default async function logout(
req: NextApiRequest,
res: NextApiResponse<any>
) {
return getTokenFromCookie(req, res, (token) => {
checkTokenIsValid(req, res, token, (tokenBody) => {
getTokenDB(req, res, tokenBody, token[1], (tokenDB) => {
sendResponse(req, res, logoutResponse(tokenDB, req, res))
const context = { req, res }
return getTokenFromCookie(context, (refreshToken) => {
checkTokenIsValid(context, refreshToken, (token) => {
getTokenDB(context, token, (tokenDB) => {
sendResponse(context, logoutResponse(tokenDB, req, res))
})
})
}).catch((err) => sendError(req, res, err))
}).catch((err) => sendError(context, err))
}
function logoutResponse<T>(

View file

@ -14,10 +14,12 @@ export default async function register(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
return getUserFromBody(req, res, (username, password) =>
const context = { req, res }
return getUserFromBody(context, (username, password) =>
createPlayerDB(username, password, (player) => {
sendResponse(req, res, registerResponse(player))
}).catch((err) => sendError(req, res, err))
sendResponse(context, registerResponse(player))
}).catch((err) => sendError(context, err))
)
}