Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Protect API routes with withMiddlewareAuthRequired #903

Closed
6 tasks done
Nargonath opened this issue Nov 8, 2022 · 8 comments
Closed
6 tasks done

Protect API routes with withMiddlewareAuthRequired #903

Nargonath opened this issue Nov 8, 2022 · 8 comments
Labels
beta enhancement New feature or request

Comments

@Nargonath
Copy link

Nargonath commented Nov 8, 2022

Checklist

  • I have looked into the README and have not found a suitable solution or answer.
  • I have looked into the examples and have not found a suitable solution or answer.
  • I have looked into the API documentation and have not found a suitable solution or answer.
  • I have searched the issues and have not found a suitable solution or answer.
  • I have searched the Auth0 Community forums and have not found a suitable solution or answer.
  • I agree to the terms within the Auth0 Code of Conduct.

Describe the problem you'd like to have solved

I have a NextJS server that has both page routes and API endpoints under /api. I want to use withMiddlewareAuthRequired to validate session token for all page routes and API endpoints that are not ignored by config.matcher. Once @auth0/nextjs-auth0 did its magic, I want to execute custom code to check for the presence of specific roles and custom claims in the token. If these elements are not present, depending on the entry point (page route or API endpoint) I'd either redirect the user to Auth0 login or return a 401.

I can already craft my custom logic for the middleware by providing withMiddlewareAuthRequired a function. However right now when using withMiddlewareAuthRequired if I attempt to access a protected API route without any authentication information, the client gets redirected to the Auth0 login.

Describe the ideal solution

IMO redirection is the desired behaviour when accessing page routes however when you access any protected endpoint under /api it should return a 401 without redirecting the client.

Alternatives and current workarounds

I don't have any since the way the redirect is handled is outside of the user control. One way would be to ignore all API routes but it defeats the purpose of middleware IMO. I'd have to craft the token validation myself and apply it manually to all protected API endpoints which is error prone.

Additional context

next@12.3.2
@auth0/nextjs-auth0@2.0.0-beta.1
node@16.15.1

@Nargonath
Copy link
Author

Nargonath commented Nov 8, 2022

I forgot to mention that with such behaviour I could use withMiddlewareAuthRequired solely instead of withPageAuthRequired and withApiAuthRequired on each individual routes. I believe the middleware approach is more secured because you're strict by default and then you loosen it only when needed rather than being lax by default.

@adamjmcgrath
Copy link
Contributor

Hi @Nargonath - thanks for raising this

This is tricky because of https://nextjs.org/docs/messages/returning-response-body-in-middleware - the advice is to redirect to an API route in these cases. But that API route will also have to check the session, so there's little point using middleware for this use case - you'd just be checking the session in 2 places rather than 1.

@adamjmcgrath
Copy link
Contributor

@Nargonath - have raised #909 to fix

@adamjmcgrath adamjmcgrath added enhancement New feature or request and removed question Further information is requested labels Nov 15, 2022
@Nargonath
Copy link
Author

Thanks for the explanation @adamjmcgrath. I wasn't aware we couldn't return a body from a middleware anymore. I agree in that case it doesn't make sense to use withMiddlewareAuthRequired for the API endpoints anymore. I'm probably better off creating a custom wrapper around withApiAuthRequired to also check the token claims once withApiAuthRequired has validated the session token, something like:

import { withApiAuthRequired, getSession } from "@auth0/nextjs-auth0";

export function withCustomAuthCheck(nextHandler) {
  return withApiAuthRequired(async (req, res) => {
    const session = await getSession(req, res);

    if (!session.roles.includes("super-role")) {
      res.status(401).end();
      return;
    }

    return nextHandler(req, res);
  })
}

@adamjmcgrath
Copy link
Contributor

Hi @Nargonath - that would work too

I've added support for protecting API routes, because since middleware can be applied to API routes as well as UI pages one would expect them to work for both.

I would say Middleware is great for protecting pages with one caveat, if the page you're protecting needs to access the session, to get user info or an access token, you'll end up doing the work of decrypting the session cookie twice - which is probably not a big deal, but worth bearing in mind.

When I release #909 you'll be able to do something like the following if you still want to use middleware:

import { NextResponse } from 'next/server';
import { withMiddlewareAuthRequired, getSession } from '@auth0/nextjs-auth0/middleware';

export default withMiddlewareAuthRequired(async function middleware(req) {
  const res = NextResponse.next();
  const { user } = await getSession(req, res);

  if (!user.roles.includes('super-role')) {
    return NextResponse.rewrite('/api/auth/401', { status: 401 });
  }

  return res;
});

(I will add some docs about the 401 route when I update the README)

@Nargonath
Copy link
Author

Nargonath commented Nov 17, 2022

Thanks @adamjmcgrath for the explanation.

I would say Middleware is great for protecting pages with one caveat, if the page you're protecting needs to access the session, to get user info or an access token, you'll end up doing the work of decrypting the session cookie twice - which is probably not a big deal, but worth bearing in mind.

Thanks for the heads up. Does it mean I can't do something like below? Or just that the session cookie will be decrypted one time at middleware level and then one time again in the getServerSideProps?

// pages/profile.js
import { getSession } from '@auth0/nextjs-auth0';

export default function Profile({ user }) {
  return <div>Hello {user.name}</div>;
}

export function getServerSideProps(ctx) {
  const { user } = await getSession(ctx.req, ctx.res);

  return { props: { user } };
}

@adamjmcgrath
Copy link
Contributor

Or just that the session cookie will be decrypted one time at middleware level and then one time again in the getServerSideProps?

Yep, this is correct. I don't want to put people off using withMiddlewareAuthRequired, but you can't share the work of decrypting the cookie between the middleware layer and the server layer so you will be decrypting it twice in that scenario you've shared.

@adamjmcgrath
Copy link
Contributor

adamjmcgrath commented Nov 18, 2022

This got released in 2.0.0-beta.4

FPiety0521 pushed a commit to FPiety0521/Auth0-Next.js-SDK that referenced this issue Apr 28, 2023
redsky030428 added a commit to redsky030428/autho_next.js that referenced this issue May 13, 2024
luckyshinystar7 added a commit to luckyshinystar7/nextjs-crysdyazandco that referenced this issue Aug 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
beta enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants