nahuel.luca()

How to authenticate HTTP requests to APIs with NextJS + Auth0

In this little blog-post we are going to cover some things about how Auth0 works and specifically how it works with NextJS to handle sessions and authentications.

What is Auth0?

In short Auth0 is an authentication provider or as it is known authentication-as-a-service provider. What it does is provide different types of authentication and authorization solutions such as multi-factor authentication (MFA), Magic Link, OAuth, etc. It also allows us to manage tokens, and sessions among other things. Auth0 offers an SDK for our NextJS app to interact in a friendly way with Auth0.

Token-based authentication

This method is used to share information between services without sharing credentials using the Open ID Connect (OIDC) protocol.

Token-based authentication uses JSON Web Tokens (JWTs). JSON Web Tokens are most commonly used to identify an authenticated user. They are issued by an authentication server and are consumed by the client-server (to secure its APIs).

Types of Tokens

Auth0 have different tokens for different utilities

  • Access Tokens: are used in token-based authentication to allow an application to access an API. The application receives an access token after a user successfully authenticates and authorizes access, then passes the access token as a credential when it calls the target API.
  • ID Tokens: are used in token-based authentication to cache user profile information and provide it to a client application, thereby providing better performance and experience. The application receives an ID token after a user successfully authenticates, then consumes the ID token and extracts user information from it, which it can then use to personalize the user's experience.
  • Refresh Tokens: Refresh tokens are used to obtain new access and ID tokens without requiring the user to (manually) re-authenticate.

Login Sessions

The tokens are stored in the login session. Sessions will be stored at three different locations when using Next.js like Application-level session, SSO session, Identity Provider (IdP) session. Are stored in the user's browser as an encrypted cookie. The session can only be decrypted on the server side and therefore the contents (tokens and other metadata) of the session are only accessible on the server side.

Authenticate HTTP requests to APIs

Basically to communicate with an external API what the Auth0 SDK does is to set the Access Token in an Authorization header. This can be achieved in different ways, here are some examples:

With middleware:

We create a middleware that next executes before each request and then in config we set the routes that will be protected (if we wish).

import {withMiddlewareAuthRequired} from "@auth0/nextjs-auth0/edge";

export default withMiddlewareAuthRequired();

export const config = {
  matcher: "/protected-route",
};

SSR-protected

WithPageAuthRequired we will wrap our RSC and then redirect it to the desired path.

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

import SessionButton from "@/components/session-button";
import UserCard from "@/components/user-card";

export default withPageAuthRequired(
  async function Profile() {
    const dataSession = await getSession();
    const user = dataSession?.user;

    return (
      <section className="flex gap-5 min-h-screen flex-col items-center p-24">
        {user ? (
          <UserCard user={user} />
        ) : (
          <>
            <h3 className="text-2xl font-semibold">Login for see your user</h3>
            <SessionButton />
          </>
        )}
      </section>
    );
  },
  {returnTo: "/ssr-protected"},
);

Route API

We will create an API route in NextJS that will use withApiAuthRequired and then call it in a client component

// api/protected/route.ts
import {withApiAuthRequired, getSession} from "@auth0/nextjs-auth0";
import {NextResponse} from "next/server";

export const GET = withApiAuthRequired(async function myApiRoute(req) {
  const res = new NextResponse();
  const dataSession = await getSession(req, res);
  const user = dataSession?.user;

  return NextResponse.json({protected: "My Secret", id: user?.sub}, res);
});

Client Component:

// route-api/page.tsx
"use client";
import useSWR from "swr";
import {withPageAuthRequired} from "@auth0/nextjs-auth0/client";

const fetcher = async (uri: string) => {
  const response = await fetch(uri);

  return response.json();
};

export default withPageAuthRequired(function Products() {
  const {data, error} = useSWR("/api/protected", fetcher);

  if (error) return <div>oops... {error.message}</div>;
  if (data === undefined) return <div>Loading...</div>;

  return (
    <div className="flex min-h-screen gap-5 flex-col items-center p-24">
      <h1 className="text-2xl font-semibold">{data.protected}</h1>
      <h3>{data.id}</h3>
    </div>
  );
});

The end

Thanks for reading, you have the code of the app here and its deployed version here and you can also read my other blog-post for using nextJS with Auth0.

Resourses