Nextjs-auth0: redirectUri and postLogoutRedirectUri based on headers

Created on 15 May 2020  路  9Comments  路  Source: auth0/nextjs-auth0

Description

Now and Vercel CLI allows to deploy app dynamically (preview mode). Unfortunately, there is no way to get the deployment URL on build time. Is it possible to add behavior for redirectUri and postLogoutRedirectUri to be set as the value of header eg. host or authority?

enhancement

Most helpful comment

This is what I did to get this working, hopefully it might help someone. I think it works... have done some basic testing for custom domains in vercel + preview urls and local dev.

const audience = process.env.AUTH0_AUDIENCE;
const scope = process.env.AUTH0_SCOPE;

function getUrls(req: NextApiRequest) {
  const host = req.headers['host'];
  const protocol = process.env.VERCEL_URL ? 'https' : 'http';
  const redirectUri = `${protocol}://${host}/api/auth/callback`;
  const returnTo = `${protocol}://${host}`;
  return {
    redirectUri,
    returnTo
  };
}

export default handleAuth({
  async callback(req: NextApiRequest, res: NextApiResponse) {
    try {
      const { redirectUri } = getUrls(req);
      await handleCallback(req, res, { redirectUri: redirectUri });
    } catch (error) {
      res.status(error.status || 500).end(error.message);
    }
  },

  async login(req: NextApiRequest, res: NextApiResponse) {
    try {
      const { redirectUri, returnTo } = getUrls(req);

      await handleLogin(req, res, {
        authorizationParams: {
          audience: audience,
          scope: scope,
          redirect_uri: redirectUri
        },
        returnTo: returnTo
      });
    } catch (error) {
      res.status(error.status || 400).end(error.message);
    }
  },

  async logout(req: NextApiRequest, res: NextApiResponse) {
    const { returnTo } = getUrls(req);
    await handleLogout(req, res, {
      returnTo: returnTo
    });
  }
});

In Auth0 dev environment I had the following settings:

Allowed Callback URLs:

http://localhost:3000/api/auth/callback, https://mycooldomain.xyz/api/auth/callback, https://*.vercel.app/api/auth/callback

Allowed Logout URLs:

http://localhost:3000, https://mycooldomain.xyz, https://*.vercel.app

All 9 comments

This is what I quickly hacked together so I could use Vercel

auth0.js

import { initAuth0 } from "@auth0/nextjs-auth0";

const defaultSettings = {
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_CLIENT_ID,
  ...
};

export function preAuth0(req) {

  const deploymentUrl = req.headers
    ? req.headers["x-custom-host"] || req.headers?.host || null
    : null;

  const settings = {
    redirectUri:
      process.env.REDIRECT_URI || "https://" + deploymentUrl + "/api/callback",
    postLogoutRedirectUri:
      process.env.POST_LOGOUT_REDIRECT_URI || "https://" + deploymentUrl + "/"
  };

  return initAuth0({
    ...defaultSettings,
    ...settings
  });
}

someOtherFile.js - use preAuth0 and pass request to it

   import { preAuth0 } from "../../lib/auth0";
   ...
   const auth0 = preAuth0(req);

@nodabladam I'm aware of this solution. The thing is, each time you call this function, you actually reinitialize auth0. So in case of calling the same endpoint twice, you init auth0 twice, but there's no domain changed, so it's pointless. I know it's not so bad in case of serverless as it is in a traditional server. However, while lambda is still active, it's simply a pointless calculation.

A good option could be to memorize the result, so actual initialization would be performed once, at first shot. But it's still a bunch of hacking.

I believe this library should simply allow setting up custom options during runtime or simply to use relative paths (redirectUri - /api/callback)

Also, because of wrapping initAuth0 in function with req, you can't use requireAuthentication

@krystian50 Did you ever figure out a solution to this issue?

My Preview URLs work when I use the canonical URL (e.g. https://project-d686nh684.vercel.app/), rather than the branch preview URL (e.g. https://project-git-preview-branch-name.project.vercel.app/) which doesn't work. The Preview URL is given to the PR, but the canonical URL is the one given by VERCEL_URL.

I setup a redirect on my /api/login, which will redirect first to the canonical URL before tryna login.

import absoluteUrl from 'next-absolute-url'

export default async function login(request, response) {
  try {
    const baseUrl = process.env.VERCEL_URL
      ? `https://${process.env.VERCEL_URL}`
      : 'http://localhost:3000'
    const requestUrl = absoluteUrl(request).origin
    if (baseUrl !== requestUrl) {
      response.writeHead(301, { Location: `${baseUrl}${request.url}` })
      response.end()
    } else {
      await auth0.handleLogin(request, response)
    }
  } catch (error) {
    console.error(error)
    response.status(error.status || 500).end(error.message)
  }
}

This unfortunately works for preview domains, but fails for my production domain.

I added BASE_URL as environment variable in production (on vercel dashboard) and left it blank in preview mode.

In my code I check if process.env.BASE_URL is defined else I set it to https://${process.env.VERCEL_URL}

That way it will set ti the cannonical url in preview, but in production it will use the production url.

I do not currently use the git integration (and manually deploy through CLI with git hooks) due to having a monorepo (waiting on vercel monorepo support to be finished). If I were to use git, I'm sure you could figure the url out with the branch name (process.env.VERCEL_GITHUB_COMMIT_REF)
See https://vercel.com/docs/v2/build-step#system-environment-variables

It's a pain to have to manually do it, but for now that works.

Auth0 requires you to set these domains in the allowed origins and callbacks etc too. Seeing as you dont know up front what random url will be generated, use a wildcard, with your project name in front and vercel.app at the end.

Hi @krystian50 - thanks for raising this

Unfortunately, there is no way to get the deployment URL on build time.

With the new Beta you will not need the url details at build time.

We recommend you check it out here https://github.com/auth0/nextjs-auth0/tree/beta/src

There is still an issue with one named export from the beta requiring env vars at build time and we're leaving #154 open to track that work to fix it

This is what I did to get this working, hopefully it might help someone. I think it works... have done some basic testing for custom domains in vercel + preview urls and local dev.

const audience = process.env.AUTH0_AUDIENCE;
const scope = process.env.AUTH0_SCOPE;

function getUrls(req: NextApiRequest) {
  const host = req.headers['host'];
  const protocol = process.env.VERCEL_URL ? 'https' : 'http';
  const redirectUri = `${protocol}://${host}/api/auth/callback`;
  const returnTo = `${protocol}://${host}`;
  return {
    redirectUri,
    returnTo
  };
}

export default handleAuth({
  async callback(req: NextApiRequest, res: NextApiResponse) {
    try {
      const { redirectUri } = getUrls(req);
      await handleCallback(req, res, { redirectUri: redirectUri });
    } catch (error) {
      res.status(error.status || 500).end(error.message);
    }
  },

  async login(req: NextApiRequest, res: NextApiResponse) {
    try {
      const { redirectUri, returnTo } = getUrls(req);

      await handleLogin(req, res, {
        authorizationParams: {
          audience: audience,
          scope: scope,
          redirect_uri: redirectUri
        },
        returnTo: returnTo
      });
    } catch (error) {
      res.status(error.status || 400).end(error.message);
    }
  },

  async logout(req: NextApiRequest, res: NextApiResponse) {
    const { returnTo } = getUrls(req);
    await handleLogout(req, res, {
      returnTo: returnTo
    });
  }
});

In Auth0 dev environment I had the following settings:

Allowed Callback URLs:

http://localhost:3000/api/auth/callback, https://mycooldomain.xyz/api/auth/callback, https://*.vercel.app/api/auth/callback

Allowed Logout URLs:

http://localhost:3000, https://mycooldomain.xyz, https://*.vercel.app

This is what I did to get this working, hopefully it might help someone. I think it works... have done some basic testing for custom domains in vercel + preview urls and local dev.

const audience = process.env.AUTH0_AUDIENCE;
const scope = process.env.AUTH0_SCOPE;

function getUrls(req: NextApiRequest) {
  const host = req.headers['host'];
  const protocol = process.env.VERCEL_URL ? 'https' : 'http';
  const redirectUri = `${protocol}://${host}/api/auth/callback`;
  const returnTo = `${protocol}://${host}`;
  return {
    redirectUri,
    returnTo
  };
}

export default handleAuth({
  async callback(req: NextApiRequest, res: NextApiResponse) {
    try {
      const { redirectUri } = getUrls(req);
      await handleCallback(req, res, { redirectUri: redirectUri });
    } catch (error) {
      res.status(error.status || 500).end(error.message);
    }
  },

  async login(req: NextApiRequest, res: NextApiResponse) {
    try {
      const { redirectUri, returnTo } = getUrls(req);

      await handleLogin(req, res, {
        authorizationParams: {
          audience: audience,
          scope: scope,
          redirect_uri: redirectUri
        },
        returnTo: returnTo
      });
    } catch (error) {
      res.status(error.status || 400).end(error.message);
    }
  },

  async logout(req: NextApiRequest, res: NextApiResponse) {
    const { returnTo } = getUrls(req);
    await handleLogout(req, res, {
      returnTo: returnTo
    });
  }
});

In Auth0 dev environment I had the following settings:

Allowed Callback URLs:

http://localhost:3000/api/auth/callback, https://mycooldomain.xyz/api/auth/callback, https://*.vercel.app/api/auth/callback

Allowed Logout URLs:

http://localhost:3000, https://mycooldomain.xyz, https://*.vercel.app

Awesome. This works nicely! Thanks @jakejscott 馃槃

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mustafaKamal-fe picture mustafaKamal-fe  路  5Comments

jkjustjoshing picture jkjustjoshing  路  4Comments

lunchboxav picture lunchboxav  路  5Comments

jvorcak picture jvorcak  路  4Comments

GeeWee picture GeeWee  路  5Comments