mirror of
https://github.com/dsoaress/link-free.git
synced 2026-08-16 22:46:04 +01:00
feat: add auth middleware
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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 }
|
||||
|
||||
41
src/utils/authMiddleware.ts
Normal file
41
src/utils/authMiddleware.ts
Normal 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()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { User } from '@prisma/client'
|
||||
import type { User } from '@prisma/client'
|
||||
import jwt from 'jsonwebtoken'
|
||||
|
||||
import { prisma } from '../services/prisma'
|
||||
|
||||
18
yarn.lock
18
yarn.lock
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user