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
- Enterprise-ready - Certyfikaty SOC2, HIPAA, GDPR compliance
- Uniwersalność - Obsługuje każdy scenariusz auth
- Customizacja - Pełna kontrola nad UI i flow
- Skalowalność - Miliardy logowań miesięcznie
- Security - Breach detection, anomaly detection, bot protection
- Dokumentacja - Najlepsza w branży
- SDK dla wszystkiego - Web, Mobile, SPA, Server
Auth0 vs Clerk vs Kinde
| Cecha | Auth0 | Clerk | Kinde |
|---|---|---|---|
| Free tier | 7,500 MAU | 10,000 MAU | 10,500 MAU |
| Enterprise SSO | ✅ Full | ✅ Full | ✅ Basic |
| MFA | ✅ Advanced | ✅ Basic | ✅ Basic |
| Customization | ✅ Full | Ograniczona | Średnia |
| Actions/Hooks | ✅ Actions | Webhooks | Webhooks |
| Machine-to-Machine | ✅ Tak | ❌ Nie | ✅ Tak |
| Compliance | SOC2, HIPAA | SOC2 | SOC2 |
| Pricing | Od $35/mo | Od $25/mo | Od $25/mo |
| Learning curve | Stroma | Łatwa | Średnia |
Instalacja i Setup
Next.js App Router
npm install @auth0/nextjs-auth0Konfiguracja
# .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
// 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
// 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
// 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
// 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
// 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
// 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:
// 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
// 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
// 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
// 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
// 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
// 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_nameMulti-Factor Authentication
Konfiguracja MFA
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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ń
// 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ń
// 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
// 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
// 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
// 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
// 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
// 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)
<!-- 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
// 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
| Plan | Cena | MAU | Features |
|---|---|---|---|
| Free | $0/mo | 7,500 | Basic auth, Social, MFA |
| Essentials | $35/mo | 500+ | Custom domains, Actions |
| Professional | $240/mo | 1,000+ | Enterprise SSO, Advanced MFA |
| Enterprise | Custom | Custom | Dedicated 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
- Enterprise-ready - SOC2, HIPAA, GDPR compliance certifications
- Universality - Handles every auth scenario
- Customization - Full control over UI and flow
- Scalability - Billions of logins per month
- Security - Breach detection, anomaly detection, bot protection
- Documentation - Best in the industry
- SDKs for everything - Web, Mobile, SPA, Server
Auth0 vs Clerk vs Kinde
| Feature | Auth0 | Clerk | Kinde |
|---|---|---|---|
| Free tier | 7,500 MAU | 10,000 MAU | 10,500 MAU |
| Enterprise SSO | ✅ Full | ✅ Full | ✅ Basic |
| MFA | ✅ Advanced | ✅ Basic | ✅ Basic |
| Customization | ✅ Full | Limited | Medium |
| Actions/Hooks | ✅ Actions | Webhooks | Webhooks |
| Machine-to-Machine | ✅ Yes | ❌ No | ✅ Yes |
| Compliance | SOC2, HIPAA | SOC2 | SOC2 |
| Pricing | From $35/mo | From $25/mo | From $25/mo |
| Learning curve | Steep | Easy | Medium |
Installation and setup
Next.js App Router
npm install @auth0/nextjs-auth0Configuration
# .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
// 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
// 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
// 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
// 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
// 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
// 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:
// 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
// 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
// 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
// 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
// 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
// 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_nameMulti-factor authentication
MFA configuration
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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)
<!-- 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
// 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
| Plan | Price | MAU | Features |
|---|---|---|---|
| Free | $0/mo | 7,500 | Basic auth, Social, MFA |
| Essentials | $35/mo | 500+ | Custom domains, Actions |
| Professional | $240/mo | 1,000+ | Enterprise SSO, Advanced MFA |
| Enterprise | Custom | Custom | Dedicated 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.