Tutorials / Backend integrations / Auth with Auth0
πŸ“ Written ● Intermediate Updated 2026-05-22

How do I add auth to my app with Auth0?

TL;DR: Auth0 is the enterprise-grade auth provider (SAML, SSO, SCIM, custom rules). Create a tenant at auth0.com, create an Application, configure callback URLs, drop in their SDK, redirect. Pick over Clerk when you need IT-grade identity-provider plug-in.

Auth0 is the enterprise-grade auth provider β€” owned by Okta, used wherever SAML / SSO / SCIM matters. Heavier UI than Clerk; more rule customization; the right call when your auth requirements include "Fortune 500 IT can plug in their identity provider."

When to pick Auth0

0
  • Pick Auth0 when β€” you sell to enterprises that demand SAML / Microsoft Entra ID / Okta SSO, you need SCIM user provisioning, your auth has complex per-tenant rules, or you need an OIDC provider that other apps consume.
  • Pick Clerk for the same problem with a friendlier DX and lower price ceiling at modest scale.
  • Pick Supabase Auth if you're already on Supabase.
  • Pick manual OAuth if you only need 1–2 providers and want full control without a vendor.

Auth0 is overkill for an indie SaaS. It's the right answer for B2B sold to IT departments.

Sign up + create a tenant

1

Sign up at auth0.com/signup β†—. Free tier: 7,500 monthly active users for B2C, or 50 MAU for B2B with SSO enabled.

You're prompted to pick a tenant name and region. The tenant is the top-level container; everything else (Applications, Connections, Rules) lives under it. Most teams have one tenant per environment (dev, staging, prod).

Create an Application

2

Auth0 console β†’ Applications β†’ Create Application. Pick:

  • Name β€” your app's name.
  • Application type:
    • Single Page Application (SPA) β€” React, Vue, Angular client-rendered apps.
    • Regular Web Application β€” Next.js, Express, server-rendered apps.
    • Machine to Machine β€” backend-to-backend service auth, no user.
    • Native β€” iOS, Android, Electron.

Click Create. You get a Domain, Client ID, and (for confidential apps) Client Secret.

AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_CLIENT_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
AUTH0_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx   # NEVER in browser
AUTH0_AUDIENCE=https://yourapp.com/api                  # for API auth (Step 8)

Configure callback URLs

3

Application settings β†’ scroll to Application URIs. Add:

  • Allowed Callback URLs β€” comma-separated: http://localhost:3000/api/auth/callback, https://yourapp.com/api/auth/callback
  • Allowed Logout URLs β€” where to send users after logout: http://localhost:3000, https://yourapp.com
  • Allowed Web Origins β€” needed for SPA token refresh: http://localhost:3000, https://yourapp.com

Exact match required. Trailing slash, http vs https, port number β€” all matter. Save.

Enable identity providers

4

Auth0 console β†’ Authentication β†’ Database for email/password. Or:

Authentication β†’ Social for Google, GitHub, Apple, Microsoft, Facebook, etc. β€” toggle on; Auth0 provides default dev credentials, or paste your own from the provider's console (recommended for production).

Authentication β†’ Enterprise for SAML, ADFS, Azure AD / Microsoft Entra ID, Google Workspace, Okta, OneLogin β€” the things your enterprise customers' IT departments will want to plug in.

Identity providers docs β†—.

Install the SDK (Next.js example)

5
npm install @auth0/nextjs-auth0

Add env vars (see Step 2). Create the API route:

// app/api/auth/[auth0]/route.ts
import { handleAuth } from '@auth0/nextjs-auth0';

export const GET = handleAuth();

Wrap your app:

// app/layout.tsx
import { UserProvider } from '@auth0/nextjs-auth0/client';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <UserProvider>{children}</UserProvider>
      </body>
    </html>
  );
}

Other quickstarts: React, Vue, Angular, Express, Python, Java, .NET, iOS, Android β†—.

Add sign-in / sign-out links

6
<a href="/api/auth/login">Sign in</a>
<a href="/api/auth/logout">Sign out</a>

Clicking Sign in redirects to Auth0's Universal Login page β€” the hosted sign-in UI. Users authenticate there; Auth0 redirects back to your app with the session cookie set.

Universal Login is customizable: console β†’ Branding β†’ Universal Login. Match colors, logo, copy. For deeper customization (your own UI), use the embedded login flow (more complex; embedded docs β†—).

Read the current user

7
// Server side (Next.js)
import { getSession } from '@auth0/nextjs-auth0';

export default async function Dashboard() {
  const session = await getSession();
  if (!session) redirect('/api/auth/login');
  return <div>Hello {session.user.name}</div>;
}

// Client side
import { useUser } from '@auth0/nextjs-auth0/client';

export function Profile() {
  const { user, isLoading } = useUser();
  if (isLoading) return null;
  if (!user) return <a href="/api/auth/login">Sign in</a>;
  return <img src={user.picture} alt={user.name} />;
}

Protect an API with JWT

8

For separate backend APIs (not just app sessions), Auth0 issues signed JWTs your server validates.

Auth0 console β†’ Applications β†’ APIs β†’ Create API. Set an Identifier (e.g., https://yourapp.com/api) β€” used as the JWT audience.

In your API:

npm install jose
import { jwtVerify, createRemoteJWKSet } from 'jose';

const JWKS = createRemoteJWKSet(new URL(
  `https://${process.env.AUTH0_DOMAIN}/.well-known/jwks.json`
));

export async function verifyAuth(req) {
  const auth = req.headers.get('authorization');
  if (!auth?.startsWith('Bearer ')) return null;
  const token = auth.slice(7);
  try {
    const { payload } = await jwtVerify(token, JWKS, {
      issuer: `https://${process.env.AUTH0_DOMAIN}/`,
      audience: process.env.AUTH0_AUDIENCE,
    });
    return payload;   // contains sub (user ID), permissions, etc.
  } catch {
    return null;
  }
}

JWKS rotation, signature verification β€” all handled by jose. JWT docs β†—.

Add custom logic with Actions

9

Auth0's Actions run server-side JavaScript at key points in the auth flow β€” post-login, pre-registration, sending password change emails, etc. Use them for:

  • Adding custom claims to the JWT (e.g., role, plan).
  • Syncing the user to your database on signup.
  • Blocking sign-up for emails on a deny list.
  • Enforcing MFA for specific user groups.

Auth0 console β†’ Actions β†’ Flows β†’ Login β†’ Add Action β†’ Build Custom. Example:

exports.onExecutePostLogin = async (event, api) => {
  // Add a custom claim to the token
  api.idToken.setCustomClaim('https://yourapp.com/role', event.user.app_metadata?.role || 'member');
};

Actions docs β†—.

Organizations (for B2B / multi-tenant)

10

If your customers are companies (and each company has multiple users), use Organizations. Each org has its own connection settings, members, branding, and admin role.

Auth0 console β†’ Organizations β†’ Create Organization. Members sign in to a specific org via ?organization=org_xxx URL param.

This is one of Auth0's strongest B2B features β€” Clerk and Supabase Auth have similar concepts now, but Auth0's is deepest. Organizations docs β†—.

SAML / SSO for enterprise customers

11

The reason most teams pick Auth0: enterprise SSO works out of the box.

Auth0 console β†’ Authentication β†’ Enterprise β†’ SAML β†’ Create Connection. Configure with the customer's IdP metadata. Tie it to an Organization. Users from that org now sign in via their company's IdP (Okta, Microsoft Entra ID, OneLogin, etc.).

SCIM provisioning (auto-creating/disabling users from the IdP) is also supported on Enterprise plans. SAML docs β†— Β· SCIM docs β†—.

Pricing climbs faster than Clerk. Free tier looks generous (7,500 MAU B2C) but production features like Organizations, custom domains, MFA enforcement, and SAML are paywalled on the Essentials plan ($35+/mo). At meaningful scale, Auth0 routinely costs 3–10Γ— what Clerk costs for similar features. Run the math before committing. Auth0 pricing β†—.

Custom domain

12

By default users see your-tenant.auth0.com in the sign-in URL. For production, set up a custom domain (e.g., login.yourapp.com):

Auth0 console β†’ Settings β†’ Custom Domains β†’ Add Domain. Add the required CNAME at your DNS provider. Wait for verification.

Requires Essentials plan or higher.

Common pitfalls

13
  • Callback URL mismatch β€” the #1 first-time bug. Add every variant (localhost, prod, with and without trailing slash) to Allowed Callback URLs.
  • SPA token in JS bundle β€” SPA apps don't get a Client Secret; only the Client ID is shipped to the browser. If your "SPA" needs a secret, you actually want a Regular Web Application type.
  • Tokens expiring mid-session β€” Auth0 default token lifetime is short (1 hour). For longer sessions, use refresh tokens or extend the lifetime in Application settings.
  • Forgot to add the API audience β€” calling your own API from the front end requires passing audience: AUTH0_AUDIENCE in the login call. Without it, you get an opaque token instead of a JWT, and your API can't validate it.

Official references

What's next