-
Notifications
You must be signed in to change notification settings - Fork 402
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
v4: Calls to Auth0 middleware or getAccessToken in Edge runtime can cause unhandled 403 errors #1862
Comments
@gyaneshgouraw-okta @nandan-bhat @arpit-jn @guabu Apologies for pinging y'all directly, but this is a serious production issue that leaves users in an unrecoverable error state. I've escalated the issue to Auth0 support in hopes they can help us understand the underlying OAuth issues, but regardless my understanding is that the |
Hey @WilHall 👋 From the error you shared, it looks like the user needs to complete MFA before they can refresh their token set (via You might be able to wrap the calls in your app to |
@guabu 👋🏻 Thanks for the reply 🙂 Redirecting the user to The middleware does call We are wrapping the call to In our current implementation, we do not call Any thoughts on:
Thanks, |
Are you calling |
@guabu We call We additionally call |
I was trying to reproduce the error you mentioned but I was able to successfully wrap the import { redirect } from "next/navigation"
import { auth0 } from "@/lib/auth0"
export default async function Home() {
const session = await auth0.getSession()
if (!session) {
return <main>Hello world!</main>
}
try {
const at = await auth0.getAccessToken()
console.log(at)
} catch (e: any) {
console.log(e.code) // failed_to_refresh_token
redirect("/auth/login")
}
return (
<main>
<h1>Welcome, {session.user.email}!</h1>
</main>
)
} Once the user completed MFA for their session, you will be able to refresh the token (if it was expired). |
@guabu Let's take a few steps back here. I think some important details were missed when reviewing my initial bug report.
The bug report is specifically about error handling in our Next.js middleware, not in a server component as in your example.
I think you're getting caught up on how to fix the MFA issue specifically, but this bug report is not about the MFA issue or any specific issue - it's about how this library handles issues when they occur. As I mentioned in the report, another example of an underlying OAuth error which causes this error is
Here is a code sample that should reproduce the issue for you, assuming you trigger one of the underlying OAuth issues I mentioned and then visit a page handled by the middleware: export async function middleware(request: NextRequest) {
let authResponse: NextResponse;
try {
authResponse = await auth0.middleware(request);
if (request.nextUrl.pathname.startsWith('/auth')) {
return authResponse;
}
} catch (error) {
// When an underlying OAuth issue occurs, this never happens. Instead, the user sees a "403 Forbidden" page in
// their browser
return NextResponse.redirect('/auth/login');
}
const response = NextResponse.next();
for (const [key, value] of authResponse.headers) {
response.headers.set(key, value);
}
return response;
} As far as I can tell, this is no different than the examples in the documentation of this library, except the introduction of the
When the underlying OAuth issues in my report occur, the At first glance, this would appear to be because this library handles the error and returns an unauthorized response: https://github.com/auth0/nextjs-auth0/blob/v4.0.0-beta.14/src/server/auth-client.ts#L486-L500 const [error, updatedTokenSet] = await this.getTokenSet(session.tokenSet)
if (error) {
return NextResponse.json(
{
error: {
message: error.message,
code: error.code,
},
},
{
status: 401,
}
)
} However, this is a 401, not a 403. And if this was the source of the error, we could handle it with the following adjustment to my middleware code sample, but this also does not handle the error: export async function middleware(request: NextRequest) {
let authResponse: NextResponse;
try {
authResponse = await auth0.middleware(request);
if (request.nextUrl.pathname.startsWith('/auth')) {
if (request.nextUrl.pathname === '/auth/access-token') {
return authResponse;
}
if (authResponse.status >= 400 && authResponse.status <= 599) {
// When an underlying OAuth issue occurs, this never happens. Instead, the user sees a "403 Forbidden" page in
// their browser
return NextResponse.redirect('/auth/login');
}
return authResponse;
}
} catch (error) {
// When an underlying OAuth issue occurs, this never happens. Instead, the user sees a "403 Forbidden" page in
// their browser
return NextResponse.redirect('/auth/login');
}
const response = NextResponse.next();
for (const [key, value] of authResponse.headers) {
response.headers.set(key, value);
}
return response;
} *In the above code, we never receive an Please let me know if you need any additional details. Thanks, |
Thanks for the additional context @WilHall. I understand the root of the issue you're reporting is around error handling in the middleware. I was using the MFA error as a more concrete starting point to reproduce the issue since it was easier to trigger. The goal is to reproduce the Unfortunately, I haven't had much success in triggering the case where a 403 page is returned using the default SDK setup with middleware. Would you be able to share:
This would help us narrow down the issue and make sure there aren't other factors at play that might be different between our setups. Thanks for your patience with this! |
@guabu Thanks for the response 🙂 I was finally able to reliably reproduce this issue with my own account (previously we were only able to reproduce with real users) and I believe I've identified the root cause as it related to this Auth0 library. Effectively what was happening for users experiencing this was they were ending up in a login redirect loop between our app and Auth0. This had multiple causes which we have identified and are not the fault of this library. Within that redirect loop Auth0 sets So a user who experienced a login redirect loop would loop until the browser terminated the loop or until their cookies accumulated to 10kb, which caused our AWS WAF to return a 403 error because the cookies exceeded its maximum allowed size of 10kb. Once a user is in this state, the only solution is to clear their browser cookies because there is nothing we can do since their requests don't even hit our app. We're applying a fix to our middleware which resolves the root causes of our redirect loops with Auth0, and also that removes extraneous As it pertains to this bug report, my question is: should this library be cleaning up extraneous Thanks! |
That's great to hear, I'm glad you managed to get to the root of the issue! And thanks for sharing the context.
The transaction cookies are generally short-lived. We set them to have a max age of 1 hour so the browser should clear them shortly after, if they have not already been consumed. However, if the transaction is successful, they'll be cleared out on callback as you mentioned. We definitely look into capping the number of |
@guabu Yes, I'm glad we were able to finally figure it out 🙂 Although the transaction cookies have a short lifetime, that won't prevent this issue in the case os a redirect loop. Effectively what we have done as a fix is both in our middleware and in the Auth0 client
Could this cleanup or similar be added to the library? Otherwise, the issue still exists without this manual fix to our middleware and |
Checklist
Description
What happens
The problem we are experiencing appears to ultimately be caused by this library's calls to
oauth
resulting from our middleware calling eitherauth0.middleware
orauth0.getAccessToken
. When it happens, users see a403
page, and we are unable to catch and handle the error in our middleware because it is unhandled in the Auth0 middleware / route handlers.The actual cause of the underlying OAuth errors are issues with the OAuth providers we use, and are not the focus of this bug report.
What we would expect to happen
We would expect calls to
auth0.middleware
orauth0.getAccessToken
to be safe to call from our middleware, either not erroring or erroring in a way which we are able to handle. But because these errors are unhandled in the Auth0 middleware / route handlers we cannot handle them with atry...catch
; my understanding here is that this is a limitation of error handling in Next.js/ the Edge runtime.Additionally, the 403 page users wee when this happens is just a plain, unstyled
403 Forbidden
page, and we don't appear to have any control over this page or the ability to override this behavior.Reproduction
This issue can be reproduced in the Next.js Edge runtime by calling
auth0.middleware
orauth0.getAccessToken
from the middleware in a configuration which would produce an OAuth error. Reproduction of the underlying OAuth error would depend on the OAuth error you are attempting to reproduce, your Auth0 configuration, your SSO configuration, etc. In our experience, any OAuth error reproduces the issue.Additional context
OAuth errors that cause this issue
I assume any OAuth error could cause this issue, but the ones we have seen are:
{ code: 'OAUTH_RESPONSE_BODY_ERROR', error: 'mfa_required', status: 403, error_description: 'Multifactor authentication required' }
{ code: 'OAUTH_RESPONSE_IS_NOT_CONFORM' }
When caused by calls to
auth0.middleware
await auth0.middleware(request)
in our middlewarehandleAccessToken
handleAccessToken
calls:getTokenSet
getTokenSet
callsoauth
which raises this errorWhen caused by calls to
auth0.getAccessToken
await auth0.getAccessToken()
in our middleware2.
getAccessToken
definition:getAccessToken
getAccessToken
calls:getTokenSet
getTokenSet
callsoauth
which raises this errornextjs-auth0 version
4.0.0-beta.13
Next.js version
15.1.3
Node.js version
22.12.0
The text was updated successfully, but these errors were encountered: