-
I'm trying to use JWT verification for authentication with middleware, but unfortunately, I'm getting some errors that cannot find a solution for.
package.json {
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@emotion/react": "^11.9.3",
"@emotion/styled": "^11.9.3",
"@mui/material": "^5.8.6",
"@prisma/client": "^4.0.0",
"axios": "^0.27.2",
"buffer": "^6.0.3",
"cookie": "^0.5.0",
"jsonwebtoken": "^8.5.1",
"next": "12.2.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "^7.33.0"
},
"devDependencies": {
"@types/node": "18.0.0",
"@types/react": "18.0.14",
"@types/react-dom": "18.0.5",
"eslint": "8.18.0",
"eslint-config-next": "12.2.0",
"prisma": "^4.0.0",
"typescript": "4.7.4"
}
} middleware.ts import { NextResponse } from "next/server";
import verify from "jsonwebtoken/verify";
import { urlToHttpOptions } from "url";
import type { NextRequest } from 'next/server'
const secret = process.env.SECRET;
export default function middleware(req: NextRequest) {
const { cookies } = req;
const { search, protocol, host } = req.nextUrl
const jwt = cookies.OutsiteJWT;
const url = req.url;
if (url.includes('/dashboard')) {
if (jwt === undefined) {
return NextResponse.redirect("http://localhost:3000/login");
}
try {
verify(jwt, secret); // <---- ERROR COMES FROM HERE
return NextResponse.next();
} catch (error) {
return NextResponse.redirect("/login");
}
}
return NextResponse.next();
} /pages/api/auth/login.ts /* eslint-disable import/no-anonymous-default-export */
import { sign } from "jsonwebtoken";
import { serialize } from "cookie";
const secret = process.env.SECRET;
export default async function (req, res) {
const { username, password } = req.body;
// Check in the database
// if a user with this username
// and password exists
if (username === "Admin" && password === "Admin") {
const token = sign(
{
exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 30, // 30 days
username: username,
},
secret
);
const serialised = serialize("OursiteJWT", token, {
httpOnly: true,
secure: process.env.NODE_ENV !== "development",
sameSite: "strict",
maxAge: 60 * 60 * 24 * 30,
path: "/",
});
res.setHeader("Set-Cookie", serialised);
res.status(200).json({ message: "Success!" });
} else {
res.status(401).json({ message: "Invalid credentials!" });
}
} I've tried commenting out the try/catch to check if the token is set, and it works correctly, but when I try to verify in the middleware it fails. In version 12.2.0 the middleware is stable but also has some changes. Does somebody else encounter similar issues or know how to solve this? |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 7 replies
-
Hi,
import {SignJWT, jwtVerify, type JWTPayload} from 'jose';
export async function sign(payload: Token, secret: string): Promise<string> {
const iat = Math.floor(Date.now() / 1000);
const exp = iat + 60* 60; // one hour
return new SignJWT({...payload})
.setProtectedHeader({alg: 'HS256', typ: 'JWT'})
.setExpirationTime(exp)
.setIssuedAt(iat)
.setNotBefore(iat)
.sign(new TextEncoder().encode(secret));
}
export async function verify(token: string, secret: string): Promise<Token> {
const {payload} = await jwtVerify(token, new TextEncoder().encode(secret));
// run some checks on the returned payload, perhaps you expect some specific values
// if its all good, return it, or perhaps just return a boolean
return payload;
} A short explanation is that the edge runtime is a different runtime than Node.js itself. The edge is made out of the V8 engine, but in order to keep it small, fast, and portable, a lot of API's are not included, or in the case of crypto, only a very small subset is included. |
Beta Was this translation helpful? Give feedback.
-
I am also experiencing the same issue. After conducting a thorough search on the internet, I came across similar results related to 'jose' However, I encountered a few issues initially. After spending some time, I discovered a solution. Please take a look at these two links from the
import { type NextRequest, NextResponse } from 'next/server'
import { verifyAuth } from '@lib/auth'
export const config = {
matcher: [ '/api/protected', '/protected' ],
}
export async function middleware(req: NextRequest) {
// validate the user is authenticated
const verifiedToken = await verifyAuth(req).catch((err) => {
console.error(err.message)
})
if (!verifiedToken) {
// if this an API request, respond with JSON
if (req.nextUrl.pathname.startsWith('/api/')) {
return new NextResponse(
JSON.stringify({ 'error': { message: 'authentication required' } }),
{ status: 401 });
}
// otherwise, redirect to the set token page
else {
return NextResponse.redirect(new URL('/', req.url))
}
}
}
import type { NextRequest, NextResponse } from 'next/server'
import { nanoid } from 'nanoid'
import { SignJWT, jwtVerify } from 'jose'
import { USER_TOKEN, getJwtSecretKey } from './constants'
interface UserJwtPayload {
jti: string
iat: number
}
export class AuthError extends Error {}
/**
* Verifies the user's JWT token and returns its payload if it's valid.
*/
export async function verifyAuth(req: NextRequest) {
const token = req.cookies.get(USER_TOKEN)?.value
if (!token) throw new AuthError('Missing user token')
try {
const verified = await jwtVerify(
token,
new TextEncoder().encode(getJwtSecretKey())
)
return verified.payload as UserJwtPayload
} catch (err) {
throw new AuthError('Your token has expired.')
}
}
/**
* Adds the user token cookie to a response.
*/
export async function setUserCookie(res: NextResponse) {
const token = await new SignJWT({})
.setProtectedHeader({ alg: 'HS256' })
.setJti(nanoid())
.setIssuedAt()
.setExpirationTime('2h')
.sign(new TextEncoder().encode(getJwtSecretKey()))
res.cookies.set(USER_TOKEN, token, {
httpOnly: true,
maxAge: 60 * 60 * 2, // 2 hours in seconds
})
return res
}
/**
* Expires the user token cookie
*/
export function expireUserCookie(res: NextResponse) {
res.cookies.set(USER_TOKEN, '', { httpOnly: true, maxAge: 0 })
return res
} |
Beta Was this translation helpful? Give feedback.
-
This is my solution |
Beta Was this translation helpful? Give feedback.
Hi,
jsonwebtoken
cannot run on the Edge environment. You have to use a library that does. I am usingjose
, https://www.npmjs.com/package/jose, here's a small snippet that can get you started quickly: