Deny PLUS redirect in Action Triggers

When you deny a login with api.access.deny(...) many applications do not know how to handle the Auth0 500 with error_description. So, I would like to redirect to my own deny page.

Which of the following should I do? I do not want to user to be able to onContinuePostLogin after all, they have been DENIED access and should not be able to continue, should not have a session or token. Full-stop.

exports.onExecutePostLogin = async (event, api) => {

    // No `return`, does code stop after this?
    api.access.deny('Deny example.');

    // Return
    return api.access.deny('Deny example.');

    // Deny PLUS redirect, is this even possible?
    api.access.deny('Deny example.');
    api.redirect.sendUserTo("https://my-app.exampleco.com");

    // Found this on the forums, redirect to logout with return
    api.redirect.sendUserTo("https://{auth0_tenant}.eu.auth0.com/v2/logout?returnTo=https%3A%2F%2Fmy-app.exampleco.com");

};

Do I need return? Can someone advise on the above code?

The most correct and complete approach would be:

exports.onExecutePostLogin = async (event, api) => {
  return api.access.deny('Deny example.');
};

Here’s why:

  1. You should always return the api.access.deny() call. This ensures:
  • The action stops executing immediately
  • No token is issued
  • The denial is properly propagated
  1. Trying to use api.redirect.sendUserTo() after api.access.deny() won’t work because:
  • The deny action terminates the authentication flow
  • Any code after the deny won’t execute
  • The redirect would never be reached
  1. If you need custom error handling, you should handle this on the client side. Here’s how:
// In your NextJS app or whatever framwork you using. Customize accordiingly
const { handleRedirectCallback } = useAuth0();

try {
  await handleRedirectCallback();
} catch (error) {
//Customize based on what you see in response also check for error_description in query param when auth0 redirect
  if (error.error === 'access_denied' || error.error === 'unauthorized') { 
    // Redirect to your custom error page
    router.push('/access-denied');
  }
}

If you really need custom redirection on denial, you could use Auth0’s Error Pages customization:

  1. Go to Auth0 Dashboard → Settings → Tenant Settings → General → Error Pages
  2. Choose “Custom” error page
  3. Set your URL (e.g., https://my-app.exampleco.com/access-denied)

This would handle all authentication errors, including denials from your Actions, in a more standard way.

The approach of redirecting to logout isn’t recommended because:

  1. It’s unnecessary - no session was created to logout from

Thanks this is very helpful. The only reason redirecting to logout was appealing to me is that gave me fine-grained control over the returnUrl. For example I could have different access-denied pages PER client.

Regarding return api.access.deny(...) I wish I could just throw and control app flow through exceptions. I know this is generally a bad practice, but controlling flow through undefined/null is even uglier/harder. Consider the following code:

const apiResult = CallAPI(api);
if (!apiResult) {
        return;
}


function CallAPI(api) {
    try {
        const apiResult = fetch(...);
        if (apiResult) {
            return { customerData: 'blah' };
        } else {
            return  api.access.deny("Bad customer.");
        }
    }
    catch (err) {
            return api.access.deny("External API call failed.");
    }
}

In the above code I have to pass “api” around to inner functions.

If I want to stop my process for whatever reason I essentially am just passing undefined (I assume deny() returns undefined) and then exiting the main parent function based on that.