How to validate a token on Next.js backend from a separate frontend

I host a backend via Next.js on Vercel. In a separate hosted frontend which is not in the same Next.js repo the user authenticates via Auth0. Therefor, I created a Single Page Application in my Auth0 account. I get the token in the frontend and send it with the API call to the backend. Everything works smoothly. Now I want to protect the backend API.

With Next.js I created a middleware in which I want to validate the token for every API call. The token also arrives. How can I validate the token now?

I have not found a solution that I understood right away.

  1. I created an API in my Auth0 account. In the quick start tutorial, a Node.js solution is shown. Since I use Next.js I didn’t manage to enforce the endpoints via app.use(jwtCheck). I don’t know how to do this because I don’t have access to the express app on Next.js?

  2. All other solutions I found work with the Next.js session. Since my frontend is hosted on another repo separately, I can’t do anything with the solutions.

React Frontend (localhost:4040)


import {useAuth0} from "@auth0/auth0-react";
const auth = useAuth0();
const token = await auth.getAccessTokenSilently();
await axios.get('http://localhost:3000/api/users',
  {
    headers: {Authorization: `Bearer ${token}`}
  }
).then(console.log).catch(console.log);

Next.js Backend - middleware.ts - (localhost:3000)

import {NextRequest, NextResponse} from 'next/server'
export async function middleware(req: NextRequest) {
    const token = req.headers.get("Authorization");
    console.log(token)
}
1 Like

I have just discovered Jose and managed the problem.

import {NextRequest, NextResponse} from 'next/server'
import * as jose from 'jose';

export async function middleware(req: NextRequest) {
  if (req.method === 'OPTIONS') {
    return NextResponse.json({});
  }

  const token = req.headers.get("Authorization");
  if (token) {
    await authenticateRequest(token);
  } else {
    return new NextResponse(
            JSON.stringify({success: false, message: 'Authentication failed: No token given'}),
            {status: 401, headers: {'content-type': 'application/json'}}
        );
    }
}

async function authenticateRequest(token: string) {
    // Load public key from authentication provider
    const jwks = jose.createRemoteJWKSet(new URL(process.env.AUTH0_JWKS_URI!));
    try {
        // Verify the given token
        const result = await jose.jwtVerify(token.replace('Bearer ', ''), jwks);
        return NextResponse.next();
    } catch (e) {
        console.error('Authentication failed: Token could not be verified')
        return new NextResponse(
            JSON.stringify({success: false, message: 'Authentication failed: Token could not be verified'}),
            {status: 401, headers: {'content-type': 'application/json'}}
        );
    }
}
1 Like

Hey there @DavidTheo thanks for following up on this one with your solution - That’s correct though, we typically recommend using a library here (jose included) or something like:

Cheers!

Yes I came across that too, but I didn’t understand how to use middleware in Next.js at first to build something similar without express.

Yes but that means asking the Jwt keys on each request. Those keys should be cached.

2 Likes

Good point. I am using a serverless architecture. The public key does not change, does it? So I could use it as an environment variable.

Sure. That applies only if you rotate the signin key regularly.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.