Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds
Torna alle collezioni
Guide31 min read

Auth0

Auth0 is an enterprise identity platform by Okta with SSO, MFA, social logins, and advanced identity management. Enterprise standard for authentication and authorization.

Auth0 - Kompletny Przewodnik po Enterprise Identity Platform

Czym jest Auth0?

Auth0 to enterprise-grade Identity Platform, która zapewnia kompleksowe rozwiązanie do autentykacji i autoryzacji użytkowników. Od 2021 roku Auth0 jest częścią Okta - lidera w branży zarządzania tożsamością. Platforma obsługuje wszystko: od prostych social loginów, przez enterprise SSO, po zaawansowane MFA i passwordless authentication.

Auth0 jest używany przez tysiące firm na całym świecie - od startupów po Fortune 500. Platforma wyróżnia się rozbudowanymi możliwościami customizacji, doskonałą dokumentacją i wsparciem dla praktycznie każdej platformy programistycznej.

Kluczowe cechy Auth0:

  • Universal Login - Centralna strona logowania z pełną customizacją
  • Social Connections - Google, GitHub, Facebook, Apple i 30+ providerów
  • Enterprise SSO - SAML, OIDC, Azure AD, Google Workspace
  • MFA/Adaptive MFA - SMS, Email, Authenticator, WebAuthn
  • Passwordless - Magic links, WebAuthn/Passkeys
  • Extensibility - Actions, Rules, Hooks

Dlaczego Auth0?

Kluczowe zalety

  1. Enterprise-ready - Certyfikaty SOC2, HIPAA, GDPR compliance
  2. Uniwersalność - Obsługuje każdy scenariusz auth
  3. Customizacja - Pełna kontrola nad UI i flow
  4. Skalowalność - Miliardy logowań miesięcznie
  5. Security - Breach detection, anomaly detection, bot protection
  6. Dokumentacja - Najlepsza w branży
  7. SDK dla wszystkiego - Web, Mobile, SPA, Server

Auth0 vs Clerk vs Kinde

CechaAuth0ClerkKinde
Free tier7,500 MAU10,000 MAU10,500 MAU
Enterprise SSO✅ Full✅ Full✅ Basic
MFA✅ Advanced✅ Basic✅ Basic
Customization✅ FullOgraniczonaŚrednia
Actions/Hooks✅ ActionsWebhooksWebhooks
Machine-to-Machine✅ Tak❌ Nie✅ Tak
ComplianceSOC2, HIPAASOC2SOC2
PricingOd $35/moOd $25/moOd $25/mo
Learning curveStromaŁatwaŚrednia

Instalacja i Setup

Next.js App Router

Code
Bash
npm install @auth0/nextjs-auth0

Konfiguracja

.env.local
ENV
# .env.local
AUTH0_SECRET='use [openssl rand -hex 32] to generate a 32 bytes value'
AUTH0_BASE_URL='http://localhost:3000'
AUTH0_ISSUER_BASE_URL='https://YOUR_DOMAIN.auth0.com'
AUTH0_CLIENT_ID='YOUR_CLIENT_ID'
AUTH0_CLIENT_SECRET='YOUR_CLIENT_SECRET'

API Route Handler

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

export const GET = handleAuth()

To automatycznie tworzy endpointy:

  • /api/auth/login - Rozpoczyna flow logowania
  • /api/auth/logout - Wylogowanie
  • /api/auth/callback - Callback po auth
  • /api/auth/me - Dane użytkownika (JSON)

Layout z UserProvider

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

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="pl">
      <UserProvider>
        <body>{children}</body>
      </UserProvider>
    </html>
  )
}

Authentication Flows

Client-Side Authentication

TScomponents/LoginButton.tsx
TypeScript
// components/LoginButton.tsx
'use client'

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

export function LoginButton() {
  const { user, error, isLoading } = useUser()

  if (isLoading) return <div>Loading...</div>
  if (error) return <div>{error.message}</div>

  if (user) {
    return (
      <div className="flex items-center gap-4">
        <img
          src={user.picture}
          alt={user.name || ''}
          className="w-8 h-8 rounded-full"
        />
        <span>{user.name}</span>
        <a href="/api/auth/logout" className="btn">
          Wyloguj
        </a>
      </div>
    )
  }

  return (
    <a href="/api/auth/login" className="btn btn-primary">
      Zaloguj się
    </a>
  )
}

Server-Side Authentication

TSapp/dashboard/page.tsx
TypeScript
// app/dashboard/page.tsx
import { getSession } from '@auth0/nextjs-auth0'
import { redirect } from 'next/navigation'

export default async function Dashboard() {
  const session = await getSession()

  if (!session?.user) {
    redirect('/api/auth/login')
  }

  const { user } = session

  return (
    <div>
      <h1>Dashboard</h1>
      <p>Witaj, {user.name}!</p>
      <p>Email: {user.email}</p>
      <pre>{JSON.stringify(user, null, 2)}</pre>
    </div>
  )
}

Middleware Protection

TSmiddleware.ts
TypeScript
// middleware.ts
import { withMiddlewareAuthRequired, getSession } from '@auth0/nextjs-auth0/edge'
import { NextResponse } from 'next/server'

export default withMiddlewareAuthRequired(async function middleware(req) {
  const res = NextResponse.next()
  const session = await getSession(req, res)

  // Dodaj custom headers
  if (session?.user) {
    res.headers.set('x-user-id', session.user.sub)
  }

  return res
})

export const config = {
  matcher: ['/dashboard/:path*', '/api/protected/:path*'],
}

API Route Protection

TSapp/api/protected/route.ts
TypeScript
// app/api/protected/route.ts
import { withApiAuthRequired, getSession } from '@auth0/nextjs-auth0'
import { NextResponse } from 'next/server'

export const GET = withApiAuthRequired(async function handler(req) {
  const session = await getSession()

  if (!session) {
    return NextResponse.json({ error: 'Not authenticated' }, { status: 401 })
  }

  return NextResponse.json({
    user: session.user,
    message: 'This is protected data',
  })
})

Social Logins

Konfiguracja Social Connections

W Auth0 Dashboard → Authentication → Social:

Code
TypeScript
// Dostępne social providers:
const socialProviders = [
  'google-oauth2',
  'facebook',
  'github',
  'apple',
  'twitter',
  'linkedin',
  'microsoft',
  'discord',
  'spotify',
  'twitch',
  'slack',
  'dropbox',
  // ... i wiele więcej
]

Wymuszenie konkretnego providera

Code
TypeScript
// Logowanie tylko przez Google
<a href="/api/auth/login?connection=google-oauth2">
  Zaloguj przez Google
</a>

// Logowanie tylko przez GitHub
<a href="/api/auth/login?connection=github">
  Zaloguj przez GitHub
</a>

Custom Social Connection

TSapp/api/auth/[auth0]/route.ts
TypeScript
// app/api/auth/[auth0]/route.ts
import { handleAuth, handleLogin } from '@auth0/nextjs-auth0'

export const GET = handleAuth({
  login: handleLogin({
    authorizationParams: {
      // Wymuś konkretny provider
      connection: 'google-oauth2',
      // Dodatkowe scopes
      scope: 'openid profile email',
      // Screen hint dla Google
      login_hint: 'user@example.com',
    },
  }),
})

Enterprise SSO

SAML Configuration

Code
TypeScript
// W Auth0 Dashboard → Authentication → Enterprise → SAML

// Metadata URL od Identity Provider
const samlConfig = {
  signInEndpoint: 'https://idp.example.com/saml/sso',
  signOutEndpoint: 'https://idp.example.com/saml/logout',
  signingCertificate: '-----BEGIN CERTIFICATE-----...',
  entityId: 'urn:example:idp',
}

// Mapowanie atrybutów SAML
const attributeMapping = {
  email: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress',
  name: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name',
  given_name: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname',
  family_name: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname',
}

Azure AD Integration

Code
TypeScript
// Konfiguracja Azure AD jako Enterprise Connection

// W Azure Portal:
// 1. App registrations → New registration
// 2. Redirect URI: https://YOUR_DOMAIN.auth0.com/login/callback
// 3. Certificates & secrets → New client secret

// W Auth0 Dashboard:
// Authentication → Enterprise → Microsoft Azure AD
const azureConfig = {
  domain: 'your-tenant.onmicrosoft.com',
  clientId: 'AZURE_CLIENT_ID',
  clientSecret: 'AZURE_CLIENT_SECRET',
}

Organization-based Login

TSapp/api/auth/[auth0]/route.ts
TypeScript
// app/api/auth/[auth0]/route.ts
import { handleAuth, handleLogin } from '@auth0/nextjs-auth0'

export const GET = handleAuth({
  login: handleLogin({
    authorizationParams: {
      // Kieruj do konkretnej organizacji
      organization: 'org_acme_corp',
      // Lub pozwól wybrać
      organization_name: 'acme-corp',
    },
  }),
})

// Pobierz organizację z sesji
import { getSession } from '@auth0/nextjs-auth0'

const session = await getSession()
const orgId = session?.user.org_id
const orgName = session?.user.org_name

Multi-Factor Authentication

Konfiguracja MFA

Code
TypeScript
// Auth0 Dashboard → Security → Multi-factor Auth

// Dostępne faktory:
const mfaFactors = {
  otp: true,      // Authenticator apps (Google Authenticator, Authy)
  email: true,    // Email OTP
  sms: true,      // SMS OTP
  webauthn: true, // WebAuthn/Passkeys
  push: true,     // Auth0 Guardian push notifications
  duo: true,      // Duo Security
}

// Polityki MFA:
const mfaPolicies = {
  always: 'Zawsze wymagaj MFA',
  adaptive: 'Wymagaj przy podejrzanej aktywności',
  never: 'Nie wymagaj MFA',
}

Adaptive MFA z Actions

Code
JavaScript
// Auth0 Dashboard → Actions → Flows → Login

exports.onExecutePostLogin = async (event, api) => {
  const { user, request } = event

  // Sprawdź ryzyko
  const isHighRisk =
    request.geoip.countryCode !== user.user_metadata?.usual_country ||
    request.user_agent !== user.user_metadata?.usual_device

  if (isHighRisk && !event.authentication.methods.includes('mfa')) {
    // Wymuś MFA
    api.multifactor.enable('any', {
      allowRememberBrowser: false,
    })
  }

  // Zapisz informacje o logowaniu
  api.user.setUserMetadata({
    usual_country: request.geoip.countryCode,
    usual_device: request.user_agent,
    last_login: new Date().toISOString(),
  })
}

WebAuthn/Passkeys

Code
TypeScript
// Włącz WebAuthn w Auth0 Dashboard → Security → Multi-factor Auth

// Frontend - rejestracja passkey
async function registerPasskey() {
  // Auth0 automatycznie obsługuje to przez Universal Login
  window.location.href = '/api/auth/login?prompt=login&acr_values=http://schemas.openid.net/pape/policies/2007/06/multi-factor'
}

// Sprawdź czy user ma passkey
const hasPasskey = user.authenticators?.some(
  auth => auth.type === 'webauthn-roaming' || auth.type === 'webauthn-platform'
)

Passwordless Authentication

Magic Links

Code
TypeScript
// Włącz w Auth0 Dashboard → Authentication → Passwordless → Email

// Frontend - wyślij magic link
async function sendMagicLink(email: string) {
  const response = await fetch('/api/auth/passwordless', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email }),
  })

  if (response.ok) {
    return { success: true, message: 'Check your email!' }
  }
}

// API route - Auth0 Authentication API
// app/api/auth/passwordless/route.ts
import { NextResponse } from 'next/server'

export async function POST(request: Request) {
  const { email } = await request.json()

  const response = await fetch(
    `https://${process.env.AUTH0_ISSUER_BASE_URL}/passwordless/start`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        client_id: process.env.AUTH0_CLIENT_ID,
        client_secret: process.env.AUTH0_CLIENT_SECRET,
        connection: 'email',
        email,
        send: 'link',
        authParams: {
          scope: 'openid profile email',
          redirect_uri: `${process.env.AUTH0_BASE_URL}/api/auth/callback`,
        },
      }),
    }
  )

  if (!response.ok) {
    return NextResponse.json({ error: 'Failed to send magic link' }, { status: 500 })
  }

  return NextResponse.json({ success: true })
}

SMS OTP

Code
TypeScript
// Włącz w Auth0 Dashboard → Authentication → Passwordless → SMS

async function sendSmsCode(phone: string) {
  const response = await fetch(
    `https://${process.env.AUTH0_DOMAIN}/passwordless/start`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        client_id: process.env.AUTH0_CLIENT_ID,
        client_secret: process.env.AUTH0_CLIENT_SECRET,
        connection: 'sms',
        phone_number: phone, // Format: +48123456789
        send: 'code',
      }),
    }
  )

  return response.json()
}

async function verifySmsCode(phone: string, code: string) {
  const response = await fetch(
    `https://${process.env.AUTH0_DOMAIN}/oauth/token`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        grant_type: 'http://auth0.com/oauth/grant-type/passwordless/otp',
        client_id: process.env.AUTH0_CLIENT_ID,
        client_secret: process.env.AUTH0_CLIENT_SECRET,
        realm: 'sms',
        username: phone,
        otp: code,
        scope: 'openid profile email',
      }),
    }
  )

  return response.json()
}

Auth0 Actions

Actions to serverless functions wykonywane w różnych punktach auth flow.

Login Action

Code
JavaScript
// Actions → Flows → Login → Add Action

exports.onExecutePostLogin = async (event, api) => {
  const { user, connection, request } = event

  // 1. Dodaj custom claims do tokena
  api.idToken.setCustomClaim('https://myapp.com/roles', user.app_metadata?.roles || [])
  api.accessToken.setCustomClaim('https://myapp.com/permissions', user.app_metadata?.permissions || [])

  // 2. Zablokuj podejrzane logowania
  if (request.geoip.countryCode === 'XX') {
    api.access.deny('Access denied from this region')
    return
  }

  // 3. First login - wyślij welcome email
  if (event.stats.logins_count === 1) {
    await sendWelcomeEmail(user.email)
  }

  // 4. Zaktualizuj metadata
  api.user.setUserMetadata({
    last_login_ip: request.ip,
    last_login_time: new Date().toISOString(),
    login_count: (user.user_metadata?.login_count || 0) + 1,
  })

  // 5. Wymuś weryfikację email
  if (!user.email_verified) {
    api.access.deny('Please verify your email before logging in')
  }
}

async function sendWelcomeEmail(email) {
  // Wywołaj zewnętrzny serwis
  await fetch('https://api.resend.com/emails', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${event.secrets.RESEND_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      from: 'welcome@myapp.com',
      to: email,
      subject: 'Welcome to MyApp!',
      html: '<p>Thanks for signing up!</p>',
    }),
  })
}

Pre-Registration Action

Code
JavaScript
// Actions → Flows → Pre User Registration

exports.onExecutePreUserRegistration = async (event, api) => {
  const { user, request } = event

  // Zablokuj niektóre domeny email
  const blockedDomains = ['tempmail.com', 'guerrillamail.com']
  const emailDomain = user.email.split('@')[1]

  if (blockedDomains.includes(emailDomain)) {
    api.access.deny('Registration not allowed with this email domain')
    return
  }

  // Sprawdź czy email już istnieje (custom logic)
  const existingUser = await checkIfUserExists(user.email)
  if (existingUser) {
    api.access.deny('An account with this email already exists')
    return
  }

  // Ustaw domyślne metadata
  api.user.setUserMetadata({
    registered_at: new Date().toISOString(),
    registration_ip: request.ip,
    registration_country: request.geoip.countryCode,
  })

  api.user.setAppMetadata({
    roles: ['user'],
    tier: 'free',
  })
}

Password Reset Action

Code
JavaScript
// Actions → Flows → Send Phone Message (for custom SMS)

exports.onExecuteSendPhoneMessage = async (event, api) => {
  const { message_options, recipient } = event

  // Użyj własnego providera SMS (np. Twilio)
  await fetch('https://api.twilio.com/2010-04-01/Accounts/xxx/Messages.json', {
    method: 'POST',
    headers: {
      'Authorization': `Basic ${btoa('SID:TOKEN')}`,
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      To: recipient,
      From: '+48123456789',
      Body: message_options.text,
    }),
  })
}

Role-Based Access Control (RBAC)

Konfiguracja ról i uprawnień

Code
TypeScript
// Auth0 Dashboard → User Management → Roles

// Definiuj role
const roles = [
  {
    name: 'admin',
    description: 'Full access to all resources',
    permissions: ['read:users', 'write:users', 'delete:users', 'manage:settings'],
  },
  {
    name: 'editor',
    description: 'Can edit content',
    permissions: ['read:content', 'write:content'],
  },
  {
    name: 'viewer',
    description: 'Read-only access',
    permissions: ['read:content'],
  },
]

// Auth0 Dashboard → Applications → APIs → Permissions
const apiPermissions = [
  { value: 'read:users', description: 'Read user profiles' },
  { value: 'write:users', description: 'Create and update users' },
  { value: 'delete:users', description: 'Delete users' },
  { value: 'read:content', description: 'Read content' },
  { value: 'write:content', description: 'Create and update content' },
  { value: 'manage:settings', description: 'Manage application settings' },
]

Sprawdzanie uprawnień

TSlib/auth.ts
TypeScript
// lib/auth.ts
import { getSession } from '@auth0/nextjs-auth0'

export async function checkPermission(requiredPermission: string): Promise<boolean> {
  const session = await getSession()

  if (!session?.user) return false

  const permissions = session.user['https://myapp.com/permissions'] as string[] || []
  return permissions.includes(requiredPermission)
}

export async function checkRole(requiredRole: string): Promise<boolean> {
  const session = await getSession()

  if (!session?.user) return false

  const roles = session.user['https://myapp.com/roles'] as string[] || []
  return roles.includes(requiredRole)
}

// Użycie w komponencie server-side
// app/admin/page.tsx
import { checkRole, checkPermission } from '@/lib/auth'
import { redirect } from 'next/navigation'

export default async function AdminPage() {
  const isAdmin = await checkRole('admin')
  const canManageUsers = await checkPermission('write:users')

  if (!isAdmin) {
    redirect('/unauthorized')
  }

  return (
    <div>
      <h1>Admin Panel</h1>
      {canManageUsers && <UserManagement />}
    </div>
  )
}

Middleware z RBAC

TSmiddleware.ts
TypeScript
// middleware.ts
import { withMiddlewareAuthRequired, getSession } from '@auth0/nextjs-auth0/edge'
import { NextResponse } from 'next/server'

const routePermissions: Record<string, string[]> = {
  '/admin': ['admin'],
  '/dashboard/users': ['read:users'],
  '/dashboard/settings': ['manage:settings'],
}

export default withMiddlewareAuthRequired(async function middleware(req) {
  const res = NextResponse.next()
  const session = await getSession(req, res)

  if (!session?.user) {
    return NextResponse.redirect(new URL('/api/auth/login', req.url))
  }

  const pathname = req.nextUrl.pathname
  const requiredRoles = routePermissions[pathname]

  if (requiredRoles) {
    const userRoles = session.user['https://myapp.com/roles'] as string[] || []
    const userPermissions = session.user['https://myapp.com/permissions'] as string[] || []

    const hasAccess = requiredRoles.some(
      role => userRoles.includes(role) || userPermissions.includes(role)
    )

    if (!hasAccess) {
      return NextResponse.redirect(new URL('/unauthorized', req.url))
    }
  }

  return res
})

export const config = {
  matcher: ['/admin/:path*', '/dashboard/:path*'],
}

Machine-to-Machine (M2M) Authentication

Konfiguracja M2M

Code
TypeScript
// Dla komunikacji server-to-server (np. microservices, cron jobs)

// 1. Utwórz M2M Application w Auth0 Dashboard
// 2. Autoryzuj API które może wywoływać

// Pobierz access token dla M2M
async function getM2MToken(): Promise<string> {
  const response = await fetch(
    `https://${process.env.AUTH0_DOMAIN}/oauth/token`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        client_id: process.env.AUTH0_M2M_CLIENT_ID,
        client_secret: process.env.AUTH0_M2M_CLIENT_SECRET,
        audience: process.env.AUTH0_API_AUDIENCE,
        grant_type: 'client_credentials',
      }),
    }
  )

  const data = await response.json()
  return data.access_token
}

// Użycie tokenu
async function callProtectedApi() {
  const token = await getM2MToken()

  const response = await fetch('https://api.myapp.com/internal/data', {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  })

  return response.json()
}

Weryfikacja M2M tokenu

TSapp/api/internal/route.ts
TypeScript
// app/api/internal/route.ts
import { NextResponse } from 'next/server'
import jwt from 'jsonwebtoken'
import jwksClient from 'jwks-rsa'

const client = jwksClient({
  jwksUri: `https://${process.env.AUTH0_DOMAIN}/.well-known/jwks.json`,
})

function getKey(header: jwt.JwtHeader, callback: jwt.SigningKeyCallback) {
  client.getSigningKey(header.kid, (err, key) => {
    const signingKey = key?.getPublicKey()
    callback(err, signingKey)
  })
}

export async function GET(request: Request) {
  const authHeader = request.headers.get('authorization')

  if (!authHeader?.startsWith('Bearer ')) {
    return NextResponse.json({ error: 'Missing token' }, { status: 401 })
  }

  const token = authHeader.split(' ')[1]

  try {
    const decoded = await new Promise((resolve, reject) => {
      jwt.verify(
        token,
        getKey,
        {
          audience: process.env.AUTH0_API_AUDIENCE,
          issuer: `https://${process.env.AUTH0_DOMAIN}/`,
          algorithms: ['RS256'],
        },
        (err, decoded) => {
          if (err) reject(err)
          else resolve(decoded)
        }
      )
    })

    // Token jest ważny
    return NextResponse.json({ data: 'Protected data', client: decoded })
  } catch (error) {
    return NextResponse.json({ error: 'Invalid token' }, { status: 401 })
  }
}

Management API

Zarządzanie użytkownikami

TSlib/auth0-management.ts
TypeScript
// lib/auth0-management.ts
import { ManagementClient } from 'auth0'

const management = new ManagementClient({
  domain: process.env.AUTH0_DOMAIN!,
  clientId: process.env.AUTH0_M2M_CLIENT_ID!,
  clientSecret: process.env.AUTH0_M2M_CLIENT_SECRET!,
})

// Pobierz wszystkich użytkowników
export async function getUsers(page = 0, perPage = 10) {
  return management.users.getAll({
    page,
    per_page: perPage,
    include_totals: true,
  })
}

// Pobierz użytkownika po ID
export async function getUser(userId: string) {
  return management.users.get({ id: userId })
}

// Utwórz użytkownika
export async function createUser(data: {
  email: string
  password: string
  name?: string
  connection?: string
}) {
  return management.users.create({
    email: data.email,
    password: data.password,
    name: data.name,
    connection: data.connection || 'Username-Password-Authentication',
    email_verified: false,
  })
}

// Zaktualizuj użytkownika
export async function updateUser(userId: string, data: {
  name?: string
  email?: string
  blocked?: boolean
  app_metadata?: Record<string, any>
  user_metadata?: Record<string, any>
}) {
  return management.users.update({ id: userId }, data)
}

// Usuń użytkownika
export async function deleteUser(userId: string) {
  return management.users.delete({ id: userId })
}

// Przypisz role
export async function assignRoles(userId: string, roleIds: string[]) {
  return management.users.assignRoles({ id: userId }, { roles: roleIds })
}

// Pobierz role użytkownika
export async function getUserRoles(userId: string) {
  return management.users.getRoles({ id: userId })
}

// Zresetuj hasło
export async function sendPasswordResetEmail(email: string) {
  return management.tickets.changePassword({
    email,
    connection_id: 'con_xxx', // ID connection
    client_id: process.env.AUTH0_CLIENT_ID!,
  })
}

Custom Domains i Branding

Universal Login Customization

Code
TypeScript
// Auth0 Dashboard → Branding → Universal Login

// Classic Universal Login z custom HTML
const customLoginPage = `
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Login - {{application.name}}</title>
  <link rel="stylesheet" href="https://mycdn.com/auth-styles.css">
</head>
<body>
  <div class="login-container">
    <img src="{{config.logo}}" alt="Logo">
    <h1>Welcome to {{application.name}}</h1>

    <!-- Lock widget -->
    <div id="lock-container"></div>
  </div>

  <script src="https://cdn.auth0.com/js/lock/12.0/lock.min.js"></script>
  <script>
    var lock = new Auth0Lock(config.clientID, config.auth0Domain, {
      container: 'lock-container',
      auth: {
        redirectUrl: config.callbackURL,
        responseType: 'code',
        params: { scope: 'openid profile email' }
      },
      theme: {
        logo: '{{config.logo}}',
        primaryColor: '#0066cc'
      },
      languageDictionary: {
        title: 'Zaloguj się'
      }
    });
    lock.show();
  </script>
</body>
</html>
`

New Universal Login (Page Templates)

Code
LIQUID
<!-- Auth0 Dashboard → Branding → Universal Login → Advanced Options -->

{% if prompt.name == "login" %}
  <div class="custom-header">
    <img src="{{ branding.logo_url }}" alt="Logo">
  </div>
{% endif %}

{{ prompt.screen | safe }}

{% if prompt.name == "login" %}
  <div class="custom-footer">
    <a href="/privacy">Privacy Policy</a>
    <a href="/terms">Terms of Service</a>
  </div>
{% endif %}

Custom Domain

Code
TypeScript
// 1. W Auth0 Dashboard → Branding → Custom Domains
// 2. Dodaj CNAME: auth.yourdomain.com → your-tenant.auth0.com
// 3. Zweryfikuj i włącz

// Aktualizuj konfigurację
const config = {
  AUTH0_ISSUER_BASE_URL: 'https://auth.yourdomain.com', // Zamiast xxx.auth0.com
}

Cennik

PlanCenaMAUFeatures
Free$0/mo7,500Basic auth, Social, MFA
Essentials$35/mo500+Custom domains, Actions
Professional$240/mo1,000+Enterprise SSO, Advanced MFA
EnterpriseCustomCustomDedicated support, SLA

Dodatkowe koszty

  • External Users: included in plan
  • M2M Tokens: ~1,000 included, then $0.xx per 1000
  • Enterprise Connections: Od $500/mo per connection
  • Adaptive MFA: Included in Professional+

FAQ - Najczęściej Zadawane Pytania

Czym się różni Auth0 od Firebase Auth?

Auth0 jest bardziej enterprise-focused z pełnym wsparciem SSO, SAML, zaawansowanym MFA i compliance certyfikatami. Firebase Auth jest prostszy i lepiej zintegrowany z ekosystemem Google/Firebase.

Czy mogę migrować użytkowników do Auth0?

Tak, Auth0 wspiera import użytkowników przez Management API lub bulk import. Hasła mogą być migrowane jeśli używasz kompatybilnego algorytmu hashowania (bcrypt).

Jak działa pricing per MAU?

Monthly Active Users to unikalni użytkownicy którzy zalogowali się w danym miesiącu. Jeśli ten sam user loguje się 100 razy, liczy się jako 1 MAU.

Czy Auth0 wspiera multi-tenancy?

Tak, przez Organizations feature. Możesz mieć jedną aplikację obsługującą wiele organizacji/firm z oddzielnymi connection i branding.

Jak wygląda uptime i SLA?

Auth0 oferuje 99.99% uptime SLA dla planów Enterprise. Status można sprawdzić na status.auth0.com.


Auth0 - Complete Guide to the Enterprise Identity Platform

What is Auth0?

Auth0 is an enterprise-grade Identity Platform that provides a comprehensive solution for user authentication and authorization. Since 2021, Auth0 has been part of Okta - the leader in the identity management industry. The platform handles everything: from simple social logins, through enterprise SSO, to advanced MFA and passwordless authentication.

Auth0 is used by thousands of companies worldwide - from startups to Fortune 500. The platform stands out with its extensive customization capabilities, excellent documentation, and support for virtually every development platform.

Key features of Auth0:

  • Universal Login - Centralized login page with full customization
  • Social Connections - Google, GitHub, Facebook, Apple, and 30+ providers
  • Enterprise SSO - SAML, OIDC, Azure AD, Google Workspace
  • MFA/Adaptive MFA - SMS, Email, Authenticator, WebAuthn
  • Passwordless - Magic links, WebAuthn/Passkeys
  • Extensibility - Actions, Rules, Hooks

Why Auth0?

Key advantages

  1. Enterprise-ready - SOC2, HIPAA, GDPR compliance certifications
  2. Universality - Handles every auth scenario
  3. Customization - Full control over UI and flow
  4. Scalability - Billions of logins per month
  5. Security - Breach detection, anomaly detection, bot protection
  6. Documentation - Best in the industry
  7. SDKs for everything - Web, Mobile, SPA, Server

Auth0 vs Clerk vs Kinde

FeatureAuth0ClerkKinde
Free tier7,500 MAU10,000 MAU10,500 MAU
Enterprise SSO✅ Full✅ Full✅ Basic
MFA✅ Advanced✅ Basic✅ Basic
Customization✅ FullLimitedMedium
Actions/Hooks✅ ActionsWebhooksWebhooks
Machine-to-Machine✅ Yes❌ No✅ Yes
ComplianceSOC2, HIPAASOC2SOC2
PricingFrom $35/moFrom $25/moFrom $25/mo
Learning curveSteepEasyMedium

Installation and setup

Next.js App Router

Code
Bash
npm install @auth0/nextjs-auth0

Configuration

.env.local
ENV
# .env.local
AUTH0_SECRET='use [openssl rand -hex 32] to generate a 32 bytes value'
AUTH0_BASE_URL='http://localhost:3000'
AUTH0_ISSUER_BASE_URL='https://YOUR_DOMAIN.auth0.com'
AUTH0_CLIENT_ID='YOUR_CLIENT_ID'
AUTH0_CLIENT_SECRET='YOUR_CLIENT_SECRET'

API Route Handler

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

export const GET = handleAuth()

This automatically creates the following endpoints:

  • /api/auth/login - Initiates the login flow
  • /api/auth/logout - Logs out the user
  • /api/auth/callback - Callback after authentication
  • /api/auth/me - User data (JSON)

Layout with UserProvider

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

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <UserProvider>
        <body>{children}</body>
      </UserProvider>
    </html>
  )
}

Authentication flows

Client-side authentication

TScomponents/LoginButton.tsx
TypeScript
// components/LoginButton.tsx
'use client'

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

export function LoginButton() {
  const { user, error, isLoading } = useUser()

  if (isLoading) return <div>Loading...</div>
  if (error) return <div>{error.message}</div>

  if (user) {
    return (
      <div className="flex items-center gap-4">
        <img
          src={user.picture}
          alt={user.name || ''}
          className="w-8 h-8 rounded-full"
        />
        <span>{user.name}</span>
        <a href="/api/auth/logout" className="btn">
          Logout
        </a>
      </div>
    )
  }

  return (
    <a href="/api/auth/login" className="btn btn-primary">
      Log in
    </a>
  )
}

Server-side authentication

TSapp/dashboard/page.tsx
TypeScript
// app/dashboard/page.tsx
import { getSession } from '@auth0/nextjs-auth0'
import { redirect } from 'next/navigation'

export default async function Dashboard() {
  const session = await getSession()

  if (!session?.user) {
    redirect('/api/auth/login')
  }

  const { user } = session

  return (
    <div>
      <h1>Dashboard</h1>
      <p>Welcome, {user.name}!</p>
      <p>Email: {user.email}</p>
      <pre>{JSON.stringify(user, null, 2)}</pre>
    </div>
  )
}

Middleware protection

TSmiddleware.ts
TypeScript
// middleware.ts
import { withMiddlewareAuthRequired, getSession } from '@auth0/nextjs-auth0/edge'
import { NextResponse } from 'next/server'

export default withMiddlewareAuthRequired(async function middleware(req) {
  const res = NextResponse.next()
  const session = await getSession(req, res)

  // Add custom headers
  if (session?.user) {
    res.headers.set('x-user-id', session.user.sub)
  }

  return res
})

export const config = {
  matcher: ['/dashboard/:path*', '/api/protected/:path*'],
}

API route protection

TSapp/api/protected/route.ts
TypeScript
// app/api/protected/route.ts
import { withApiAuthRequired, getSession } from '@auth0/nextjs-auth0'
import { NextResponse } from 'next/server'

export const GET = withApiAuthRequired(async function handler(req) {
  const session = await getSession()

  if (!session) {
    return NextResponse.json({ error: 'Not authenticated' }, { status: 401 })
  }

  return NextResponse.json({
    user: session.user,
    message: 'This is protected data',
  })
})

Social logins

Social connections configuration

In the Auth0 Dashboard, navigate to Authentication, then Social:

Code
TypeScript
// Available social providers:
const socialProviders = [
  'google-oauth2',
  'facebook',
  'github',
  'apple',
  'twitter',
  'linkedin',
  'microsoft',
  'discord',
  'spotify',
  'twitch',
  'slack',
  'dropbox',
  // ... and many more
]

Forcing a specific provider

Code
TypeScript
// Login with Google only
<a href="/api/auth/login?connection=google-oauth2">
  Log in with Google
</a>

// Login with GitHub only
<a href="/api/auth/login?connection=github">
  Log in with GitHub
</a>

Custom social connection

TSapp/api/auth/[auth0]/route.ts
TypeScript
// app/api/auth/[auth0]/route.ts
import { handleAuth, handleLogin } from '@auth0/nextjs-auth0'

export const GET = handleAuth({
  login: handleLogin({
    authorizationParams: {
      // Force a specific provider
      connection: 'google-oauth2',
      // Additional scopes
      scope: 'openid profile email',
      // Screen hint for Google
      login_hint: 'user@example.com',
    },
  }),
})

Enterprise SSO

SAML configuration

Code
TypeScript
// In Auth0 Dashboard, navigate to Authentication, then Enterprise, then SAML

// Metadata URL from the Identity Provider
const samlConfig = {
  signInEndpoint: 'https://idp.example.com/saml/sso',
  signOutEndpoint: 'https://idp.example.com/saml/logout',
  signingCertificate: '-----BEGIN CERTIFICATE-----...',
  entityId: 'urn:example:idp',
}

// SAML attribute mapping
const attributeMapping = {
  email: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress',
  name: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name',
  given_name: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname',
  family_name: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname',
}

Azure AD integration

Code
TypeScript
// Configuring Azure AD as an Enterprise Connection

// In Azure Portal:
// 1. App registrations, then New registration
// 2. Redirect URI: https://YOUR_DOMAIN.auth0.com/login/callback
// 3. Certificates & secrets, then New client secret

// In Auth0 Dashboard:
// Authentication, then Enterprise, then Microsoft Azure AD
const azureConfig = {
  domain: 'your-tenant.onmicrosoft.com',
  clientId: 'AZURE_CLIENT_ID',
  clientSecret: 'AZURE_CLIENT_SECRET',
}

Organization-based login

TSapp/api/auth/[auth0]/route.ts
TypeScript
// app/api/auth/[auth0]/route.ts
import { handleAuth, handleLogin } from '@auth0/nextjs-auth0'

export const GET = handleAuth({
  login: handleLogin({
    authorizationParams: {
      // Direct to a specific organization
      organization: 'org_acme_corp',
      // Or let the user choose
      organization_name: 'acme-corp',
    },
  }),
})

// Retrieve the organization from the session
import { getSession } from '@auth0/nextjs-auth0'

const session = await getSession()
const orgId = session?.user.org_id
const orgName = session?.user.org_name

Multi-factor authentication

MFA configuration

Code
TypeScript
// Auth0 Dashboard, navigate to Security, then Multi-factor Auth

// Available factors:
const mfaFactors = {
  otp: true,      // Authenticator apps (Google Authenticator, Authy)
  email: true,    // Email OTP
  sms: true,      // SMS OTP
  webauthn: true, // WebAuthn/Passkeys
  push: true,     // Auth0 Guardian push notifications
  duo: true,      // Duo Security
}

// MFA policies:
const mfaPolicies = {
  always: 'Always require MFA',
  adaptive: 'Require on suspicious activity',
  never: 'Do not require MFA',
}

Adaptive MFA with Actions

Code
JavaScript
// Auth0 Dashboard, navigate to Actions, then Flows, then Login

exports.onExecutePostLogin = async (event, api) => {
  const { user, request } = event

  // Check risk level
  const isHighRisk =
    request.geoip.countryCode !== user.user_metadata?.usual_country ||
    request.user_agent !== user.user_metadata?.usual_device

  if (isHighRisk && !event.authentication.methods.includes('mfa')) {
    // Force MFA
    api.multifactor.enable('any', {
      allowRememberBrowser: false,
    })
  }

  // Save login information
  api.user.setUserMetadata({
    usual_country: request.geoip.countryCode,
    usual_device: request.user_agent,
    last_login: new Date().toISOString(),
  })
}

WebAuthn/Passkeys

Code
TypeScript
// Enable WebAuthn in Auth0 Dashboard, navigate to Security, then Multi-factor Auth

// Frontend - passkey registration
async function registerPasskey() {
  // Auth0 handles this automatically through Universal Login
  window.location.href = '/api/auth/login?prompt=login&acr_values=http://schemas.openid.net/pape/policies/2007/06/multi-factor'
}

// Check if the user has a passkey
const hasPasskey = user.authenticators?.some(
  auth => auth.type === 'webauthn-roaming' || auth.type === 'webauthn-platform'
)

Passwordless authentication

Magic links

Code
TypeScript
// Enable in Auth0 Dashboard, navigate to Authentication, then Passwordless, then Email

// Frontend - send magic link
async function sendMagicLink(email: string) {
  const response = await fetch('/api/auth/passwordless', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email }),
  })

  if (response.ok) {
    return { success: true, message: 'Check your email!' }
  }
}

// API route - Auth0 Authentication API
// app/api/auth/passwordless/route.ts
import { NextResponse } from 'next/server'

export async function POST(request: Request) {
  const { email } = await request.json()

  const response = await fetch(
    `https://${process.env.AUTH0_ISSUER_BASE_URL}/passwordless/start`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        client_id: process.env.AUTH0_CLIENT_ID,
        client_secret: process.env.AUTH0_CLIENT_SECRET,
        connection: 'email',
        email,
        send: 'link',
        authParams: {
          scope: 'openid profile email',
          redirect_uri: `${process.env.AUTH0_BASE_URL}/api/auth/callback`,
        },
      }),
    }
  )

  if (!response.ok) {
    return NextResponse.json({ error: 'Failed to send magic link' }, { status: 500 })
  }

  return NextResponse.json({ success: true })
}

SMS OTP

Code
TypeScript
// Enable in Auth0 Dashboard, navigate to Authentication, then Passwordless, then SMS

async function sendSmsCode(phone: string) {
  const response = await fetch(
    `https://${process.env.AUTH0_DOMAIN}/passwordless/start`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        client_id: process.env.AUTH0_CLIENT_ID,
        client_secret: process.env.AUTH0_CLIENT_SECRET,
        connection: 'sms',
        phone_number: phone, // Format: +48123456789
        send: 'code',
      }),
    }
  )

  return response.json()
}

async function verifySmsCode(phone: string, code: string) {
  const response = await fetch(
    `https://${process.env.AUTH0_DOMAIN}/oauth/token`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        grant_type: 'http://auth0.com/oauth/grant-type/passwordless/otp',
        client_id: process.env.AUTH0_CLIENT_ID,
        client_secret: process.env.AUTH0_CLIENT_SECRET,
        realm: 'sms',
        username: phone,
        otp: code,
        scope: 'openid profile email',
      }),
    }
  )

  return response.json()
}

Auth0 Actions

Actions are serverless functions executed at various points in the auth flow.

Login Action

Code
JavaScript
// Actions, then Flows, then Login, then Add Action

exports.onExecutePostLogin = async (event, api) => {
  const { user, connection, request } = event

  // 1. Add custom claims to the token
  api.idToken.setCustomClaim('https://myapp.com/roles', user.app_metadata?.roles || [])
  api.accessToken.setCustomClaim('https://myapp.com/permissions', user.app_metadata?.permissions || [])

  // 2. Block suspicious logins
  if (request.geoip.countryCode === 'XX') {
    api.access.deny('Access denied from this region')
    return
  }

  // 3. First login - send welcome email
  if (event.stats.logins_count === 1) {
    await sendWelcomeEmail(user.email)
  }

  // 4. Update metadata
  api.user.setUserMetadata({
    last_login_ip: request.ip,
    last_login_time: new Date().toISOString(),
    login_count: (user.user_metadata?.login_count || 0) + 1,
  })

  // 5. Enforce email verification
  if (!user.email_verified) {
    api.access.deny('Please verify your email before logging in')
  }
}

async function sendWelcomeEmail(email) {
  // Call an external service
  await fetch('https://api.resend.com/emails', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${event.secrets.RESEND_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      from: 'welcome@myapp.com',
      to: email,
      subject: 'Welcome to MyApp!',
      html: '<p>Thanks for signing up!</p>',
    }),
  })
}

Pre-registration Action

Code
JavaScript
// Actions, then Flows, then Pre User Registration

exports.onExecutePreUserRegistration = async (event, api) => {
  const { user, request } = event

  // Block certain email domains
  const blockedDomains = ['tempmail.com', 'guerrillamail.com']
  const emailDomain = user.email.split('@')[1]

  if (blockedDomains.includes(emailDomain)) {
    api.access.deny('Registration not allowed with this email domain')
    return
  }

  // Check if the email already exists (custom logic)
  const existingUser = await checkIfUserExists(user.email)
  if (existingUser) {
    api.access.deny('An account with this email already exists')
    return
  }

  // Set default metadata
  api.user.setUserMetadata({
    registered_at: new Date().toISOString(),
    registration_ip: request.ip,
    registration_country: request.geoip.countryCode,
  })

  api.user.setAppMetadata({
    roles: ['user'],
    tier: 'free',
  })
}

Password reset Action

Code
JavaScript
// Actions, then Flows, then Send Phone Message (for custom SMS)

exports.onExecuteSendPhoneMessage = async (event, api) => {
  const { message_options, recipient } = event

  // Use your own SMS provider (e.g. Twilio)
  await fetch('https://api.twilio.com/2010-04-01/Accounts/xxx/Messages.json', {
    method: 'POST',
    headers: {
      'Authorization': `Basic ${btoa('SID:TOKEN')}`,
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      To: recipient,
      From: '+48123456789',
      Body: message_options.text,
    }),
  })
}

Role-Based Access Control (RBAC)

Configuring roles and permissions

Code
TypeScript
// Auth0 Dashboard, navigate to User Management, then Roles

// Define roles
const roles = [
  {
    name: 'admin',
    description: 'Full access to all resources',
    permissions: ['read:users', 'write:users', 'delete:users', 'manage:settings'],
  },
  {
    name: 'editor',
    description: 'Can edit content',
    permissions: ['read:content', 'write:content'],
  },
  {
    name: 'viewer',
    description: 'Read-only access',
    permissions: ['read:content'],
  },
]

// Auth0 Dashboard, navigate to Applications, then APIs, then Permissions
const apiPermissions = [
  { value: 'read:users', description: 'Read user profiles' },
  { value: 'write:users', description: 'Create and update users' },
  { value: 'delete:users', description: 'Delete users' },
  { value: 'read:content', description: 'Read content' },
  { value: 'write:content', description: 'Create and update content' },
  { value: 'manage:settings', description: 'Manage application settings' },
]

Checking permissions

TSlib/auth.ts
TypeScript
// lib/auth.ts
import { getSession } from '@auth0/nextjs-auth0'

export async function checkPermission(requiredPermission: string): Promise<boolean> {
  const session = await getSession()

  if (!session?.user) return false

  const permissions = session.user['https://myapp.com/permissions'] as string[] || []
  return permissions.includes(requiredPermission)
}

export async function checkRole(requiredRole: string): Promise<boolean> {
  const session = await getSession()

  if (!session?.user) return false

  const roles = session.user['https://myapp.com/roles'] as string[] || []
  return roles.includes(requiredRole)
}

// Usage in a server-side component
// app/admin/page.tsx
import { checkRole, checkPermission } from '@/lib/auth'
import { redirect } from 'next/navigation'

export default async function AdminPage() {
  const isAdmin = await checkRole('admin')
  const canManageUsers = await checkPermission('write:users')

  if (!isAdmin) {
    redirect('/unauthorized')
  }

  return (
    <div>
      <h1>Admin Panel</h1>
      {canManageUsers && <UserManagement />}
    </div>
  )
}

Middleware with RBAC

TSmiddleware.ts
TypeScript
// middleware.ts
import { withMiddlewareAuthRequired, getSession } from '@auth0/nextjs-auth0/edge'
import { NextResponse } from 'next/server'

const routePermissions: Record<string, string[]> = {
  '/admin': ['admin'],
  '/dashboard/users': ['read:users'],
  '/dashboard/settings': ['manage:settings'],
}

export default withMiddlewareAuthRequired(async function middleware(req) {
  const res = NextResponse.next()
  const session = await getSession(req, res)

  if (!session?.user) {
    return NextResponse.redirect(new URL('/api/auth/login', req.url))
  }

  const pathname = req.nextUrl.pathname
  const requiredRoles = routePermissions[pathname]

  if (requiredRoles) {
    const userRoles = session.user['https://myapp.com/roles'] as string[] || []
    const userPermissions = session.user['https://myapp.com/permissions'] as string[] || []

    const hasAccess = requiredRoles.some(
      role => userRoles.includes(role) || userPermissions.includes(role)
    )

    if (!hasAccess) {
      return NextResponse.redirect(new URL('/unauthorized', req.url))
    }
  }

  return res
})

export const config = {
  matcher: ['/admin/:path*', '/dashboard/:path*'],
}

Machine-to-Machine (M2M) authentication

M2M configuration

Code
TypeScript
// For server-to-server communication (e.g. microservices, cron jobs)

// 1. Create an M2M Application in the Auth0 Dashboard
// 2. Authorize the APIs it can call

// Obtain an access token for M2M
async function getM2MToken(): Promise<string> {
  const response = await fetch(
    `https://${process.env.AUTH0_DOMAIN}/oauth/token`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        client_id: process.env.AUTH0_M2M_CLIENT_ID,
        client_secret: process.env.AUTH0_M2M_CLIENT_SECRET,
        audience: process.env.AUTH0_API_AUDIENCE,
        grant_type: 'client_credentials',
      }),
    }
  )

  const data = await response.json()
  return data.access_token
}

// Using the token
async function callProtectedApi() {
  const token = await getM2MToken()

  const response = await fetch('https://api.myapp.com/internal/data', {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  })

  return response.json()
}

M2M token verification

TSapp/api/internal/route.ts
TypeScript
// app/api/internal/route.ts
import { NextResponse } from 'next/server'
import jwt from 'jsonwebtoken'
import jwksClient from 'jwks-rsa'

const client = jwksClient({
  jwksUri: `https://${process.env.AUTH0_DOMAIN}/.well-known/jwks.json`,
})

function getKey(header: jwt.JwtHeader, callback: jwt.SigningKeyCallback) {
  client.getSigningKey(header.kid, (err, key) => {
    const signingKey = key?.getPublicKey()
    callback(err, signingKey)
  })
}

export async function GET(request: Request) {
  const authHeader = request.headers.get('authorization')

  if (!authHeader?.startsWith('Bearer ')) {
    return NextResponse.json({ error: 'Missing token' }, { status: 401 })
  }

  const token = authHeader.split(' ')[1]

  try {
    const decoded = await new Promise((resolve, reject) => {
      jwt.verify(
        token,
        getKey,
        {
          audience: process.env.AUTH0_API_AUDIENCE,
          issuer: `https://${process.env.AUTH0_DOMAIN}/`,
          algorithms: ['RS256'],
        },
        (err, decoded) => {
          if (err) reject(err)
          else resolve(decoded)
        }
      )
    })

    // Token is valid
    return NextResponse.json({ data: 'Protected data', client: decoded })
  } catch (error) {
    return NextResponse.json({ error: 'Invalid token' }, { status: 401 })
  }
}

Management API

User management

TSlib/auth0-management.ts
TypeScript
// lib/auth0-management.ts
import { ManagementClient } from 'auth0'

const management = new ManagementClient({
  domain: process.env.AUTH0_DOMAIN!,
  clientId: process.env.AUTH0_M2M_CLIENT_ID!,
  clientSecret: process.env.AUTH0_M2M_CLIENT_SECRET!,
})

// Get all users
export async function getUsers(page = 0, perPage = 10) {
  return management.users.getAll({
    page,
    per_page: perPage,
    include_totals: true,
  })
}

// Get a user by ID
export async function getUser(userId: string) {
  return management.users.get({ id: userId })
}

// Create a user
export async function createUser(data: {
  email: string
  password: string
  name?: string
  connection?: string
}) {
  return management.users.create({
    email: data.email,
    password: data.password,
    name: data.name,
    connection: data.connection || 'Username-Password-Authentication',
    email_verified: false,
  })
}

// Update a user
export async function updateUser(userId: string, data: {
  name?: string
  email?: string
  blocked?: boolean
  app_metadata?: Record<string, any>
  user_metadata?: Record<string, any>
}) {
  return management.users.update({ id: userId }, data)
}

// Delete a user
export async function deleteUser(userId: string) {
  return management.users.delete({ id: userId })
}

// Assign roles
export async function assignRoles(userId: string, roleIds: string[]) {
  return management.users.assignRoles({ id: userId }, { roles: roleIds })
}

// Get user roles
export async function getUserRoles(userId: string) {
  return management.users.getRoles({ id: userId })
}

// Reset password
export async function sendPasswordResetEmail(email: string) {
  return management.tickets.changePassword({
    email,
    connection_id: 'con_xxx', // Connection ID
    client_id: process.env.AUTH0_CLIENT_ID!,
  })
}

Custom domains and branding

Universal Login customization

Code
TypeScript
// Auth0 Dashboard, navigate to Branding, then Universal Login

// Classic Universal Login with custom HTML
const customLoginPage = `
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Login - {{application.name}}</title>
  <link rel="stylesheet" href="https://mycdn.com/auth-styles.css">
</head>
<body>
  <div class="login-container">
    <img src="{{config.logo}}" alt="Logo">
    <h1>Welcome to {{application.name}}</h1>

    <!-- Lock widget -->
    <div id="lock-container"></div>
  </div>

  <script src="https://cdn.auth0.com/js/lock/12.0/lock.min.js"></script>
  <script>
    var lock = new Auth0Lock(config.clientID, config.auth0Domain, {
      container: 'lock-container',
      auth: {
        redirectUrl: config.callbackURL,
        responseType: 'code',
        params: { scope: 'openid profile email' }
      },
      theme: {
        logo: '{{config.logo}}',
        primaryColor: '#0066cc'
      },
      languageDictionary: {
        title: 'Log in'
      }
    });
    lock.show();
  </script>
</body>
</html>
`

New Universal Login (Page Templates)

Code
LIQUID
<!-- Auth0 Dashboard, navigate to Branding, then Universal Login, then Advanced Options -->

{% if prompt.name == "login" %}
  <div class="custom-header">
    <img src="{{ branding.logo_url }}" alt="Logo">
  </div>
{% endif %}

{{ prompt.screen | safe }}

{% if prompt.name == "login" %}
  <div class="custom-footer">
    <a href="/privacy">Privacy Policy</a>
    <a href="/terms">Terms of Service</a>
  </div>
{% endif %}

Custom domain

Code
TypeScript
// 1. In Auth0 Dashboard, navigate to Branding, then Custom Domains
// 2. Add a CNAME record: auth.yourdomain.com pointing to your-tenant.auth0.com
// 3. Verify and enable

// Update configuration
const config = {
  AUTH0_ISSUER_BASE_URL: 'https://auth.yourdomain.com', // Instead of xxx.auth0.com
}

Pricing

PlanPriceMAUFeatures
Free$0/mo7,500Basic auth, Social, MFA
Essentials$35/mo500+Custom domains, Actions
Professional$240/mo1,000+Enterprise SSO, Advanced MFA
EnterpriseCustomCustomDedicated support, SLA

Additional costs

  • External Users: included in plan
  • M2M Tokens: ~1,000 included, then $0.xx per 1000
  • Enterprise Connections: From $500/mo per connection
  • Adaptive MFA: Included in Professional+

FAQ - Frequently Asked Questions

How does Auth0 differ from Firebase Auth?

Auth0 is more enterprise-focused with full SSO support, SAML, advanced MFA, and compliance certifications. Firebase Auth is simpler and better integrated with the Google/Firebase ecosystem.

Can I migrate users to Auth0?

Yes, Auth0 supports importing users via the Management API or bulk import. Passwords can be migrated if you use a compatible hashing algorithm (bcrypt).

How does MAU-based pricing work?

Monthly Active Users are unique users who logged in during a given month. If the same user logs in 100 times, they count as 1 MAU.

Does Auth0 support multi-tenancy?

Yes, through the Organizations feature. You can have a single application serving multiple organizations/companies with separate connections and branding.

What does the uptime and SLA look like?

Auth0 offers a 99.99% uptime SLA for Enterprise plans. Status can be checked at status.auth0.com.