feat: add auth middleware

This commit is contained in:
Daniel Soares
2021-12-15 15:38:39 -03:00
parent e071c0a19b
commit 5138b4dfc2
7 changed files with 88 additions and 8 deletions

View File

@@ -29,6 +29,7 @@
"lodash": "^4.17.21",
"next": "12.0.7",
"next-connect": "^0.11.0",
"nookies": "^2.5.2",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-icons": "^4.3.1",

View File

@@ -3,6 +3,7 @@ import type { NextApiRequest, NextApiResponse } from 'next'
import nc from 'next-connect'
import { prisma } from '../../services/prisma'
import { authMiddleware } from '../../utils/authMiddleware'
import { ExceptionError } from '../../utils/error'
const handler = nc<NextApiRequest, NextApiResponse>({
@@ -28,6 +29,7 @@ const handler = nc<NextApiRequest, NextApiResponse>({
}
}
})
.use(authMiddleware)
.get(async (_req, res) => {
const response = await prisma.data.findUnique({
where: { id: 1 }
@@ -39,7 +41,6 @@ const handler = nc<NextApiRequest, NextApiResponse>({
res.status(200).json(response?.data)
})
.put(async (req, res) => {
const { data } = req.body

View File

@@ -6,7 +6,12 @@ import nc from 'next-connect'
import { prisma } from '../../../services/prisma'
import { ExceptionError } from '../../../utils/error'
const handler = nc<NextApiRequest, NextApiResponse>({
interface Request extends NextApiRequest {
userId: string
userRole: string
}
const handler = nc<Request, NextApiResponse>({
onNoMatch: (_req, res) => {
res.status(404).json({ error: 'Not found' })
},
@@ -51,7 +56,6 @@ const handler = nc<NextApiRequest, NextApiResponse>({
res.status(200).json(response)
})
.patch(async (req, res) => {
const { id } = req.query
const { username, password, newPassword, role } = req.body
@@ -69,6 +73,10 @@ const handler = nc<NextApiRequest, NextApiResponse>({
throw new ExceptionError('No id provided')
}
if (req.userRole !== 'ADMIN' && req.userId !== id) {
throw new ExceptionError('Only admins can update other users')
}
if (username) {
const usernameExists = await prisma.user.findUnique({
where: { username }
@@ -118,7 +126,6 @@ const handler = nc<NextApiRequest, NextApiResponse>({
throw new ExceptionError(err)
}
})
.delete(async (req, res) => {
const { id } = req.query
@@ -130,6 +137,10 @@ const handler = nc<NextApiRequest, NextApiResponse>({
throw new ExceptionError('Cannot delete admin', 403)
}
if (req.userRole !== 'ADMIN' && req.userId !== id) {
throw new ExceptionError('Only admins can delete other users')
}
try {
await prisma.data.delete({
where: { id: +id }

View File

@@ -4,9 +4,15 @@ import type { NextApiRequest, NextApiResponse } from 'next'
import nc from 'next-connect'
import { prisma } from '../../../services/prisma'
import { authMiddleware } from '../../../utils/authMiddleware'
import { ExceptionError } from '../../../utils/error'
const handler = nc<NextApiRequest, NextApiResponse>({
interface Request extends NextApiRequest {
userId: string
userRole: string
}
const handler = nc<Request, NextApiResponse>({
onNoMatch: (_req, res) => {
res.status(404).json({ error: 'Not found' })
},
@@ -29,6 +35,7 @@ const handler = nc<NextApiRequest, NextApiResponse>({
}
}
})
.use(authMiddleware)
.get(async (_req, res) => {
const response = await prisma.user.findMany({
select: {
@@ -44,7 +51,6 @@ const handler = nc<NextApiRequest, NextApiResponse>({
res.status(200).json(response)
})
.post(async (req, res) => {
const { username, password, role } = req.body
@@ -56,7 +62,9 @@ const handler = nc<NextApiRequest, NextApiResponse>({
throw new ExceptionError('Role must be either ADMIN or EDITOR')
}
// TODO implement role validation
if (req.userRole !== 'ADMIN') {
throw new ExceptionError('Only admins can create users')
}
const usernameExists = await prisma.user.findUnique({
where: { username }

View File

@@ -0,0 +1,41 @@
import jwt from 'jsonwebtoken'
import type { NextApiRequest, NextApiResponse } from 'next'
import { ExceptionError } from './error'
interface Request extends NextApiRequest {
userId: string
userRole: string
}
type Token = {
sub: string
role: string
}
export async function authMiddleware(req: Request, _res: NextApiResponse, next: () => void) {
const token = req.headers['authorization']
if (!token) {
throw new ExceptionError('No token provided', 401)
}
const [, tokenValue] = token.split(' ')
const { JWT_SECRET } = process.env
if (!JWT_SECRET) {
throw new ExceptionError('JWT_SECRET is not defined')
}
const isValidToken = jwt.verify(tokenValue, JWT_SECRET) as Token
if (!isValidToken) {
throw new ExceptionError('Invalid token', 401)
}
req.userId = isValidToken.sub
req.userRole = isValidToken.role
next()
}

View File

@@ -1,4 +1,4 @@
import { User } from '@prisma/client'
import type { User } from '@prisma/client'
import jwt from 'jsonwebtoken'
import { prisma } from '../services/prisma'

View File

@@ -1238,6 +1238,11 @@ convert-source-map@1.7.0:
dependencies:
safe-buffer "~5.1.1"
cookie@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1"
integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==
core-js-pure@^3.19.0:
version "3.19.3"
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.19.3.tgz#c69b2b36b58927317824994b532ec3f0f7e49607"
@@ -3126,6 +3131,14 @@ node-releases@^1.1.71:
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.77.tgz#50b0cfede855dd374e7585bf228ff34e57c1c32e"
integrity sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==
nookies@^2.5.2:
version "2.5.2"
resolved "https://registry.yarnpkg.com/nookies/-/nookies-2.5.2.tgz#cc55547efa982d013a21475bd0db0c02c1b35b27"
integrity sha512-x0TRSaosAEonNKyCrShoUaJ5rrT5KHRNZ5DwPCuizjgrnkpE5DRf3VL7AyyQin4htict92X1EQ7ejDbaHDVdYA==
dependencies:
cookie "^0.4.1"
set-cookie-parser "^2.4.6"
normalize-package-data@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
@@ -3860,6 +3873,11 @@ set-blocking@~2.0.0:
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
set-cookie-parser@^2.4.6:
version "2.4.8"
resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.4.8.tgz#d0da0ed388bc8f24e706a391f9c9e252a13c58b2"
integrity sha512-edRH8mBKEWNVIVMKejNnuJxleqYE/ZSdcT8/Nem9/mmosx12pctd80s2Oy00KNZzrogMZS5mauK2/ymL1bvlvg==
setimmediate@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"