Migrating from Auth0
Auris provides feature parity with Auth0 across authentication, authorization, and user management, plus capabilities that Auth0 charges extra for or does not offer (Zanzibar-style FGA, built-in SCIM provisioning, Actions Engine with visual editor, and self-hosted deployment). This guide walks you through a complete migration from Auth0 to Auris with minimal downtime.
Feature Mapping
The following table maps Auth0 features to their Auris equivalents:
| Auth0 Feature | Auris Equivalent | Notes |
|---|---|---|
| Universal Login | Hosted Login Pages | OAuth2 Authorization Code + PKCE, tenant-branded |
| Rules / Actions | Actions Engine | Sandboxed JavaScript, 6 trigger points, visual Blueprint editor |
| RBAC (Roles + Permissions) | Roles & Permissions V2 | Tri-state ALLOW/DENY, per-application scoping |
| Organizations | Organizations B2B | Multi-org with 4 member roles, token-based invitations |
| Auth0 FGA (OpenFGA) | Auris FGA | Zanzibar-style ReBAC, OpenFGA-compatible DSL |
| Social Connections | Social Login | 9 providers (Google, GitHub, Microsoft, Apple, Facebook, Discord, LinkedIn, Twitter/X, Slack) |
| Enterprise Connections (SAML/OIDC) | Enterprise SSO | SAML 2.0 + OIDC via Keycloak IdP brokering, domain verification |
| Machine-to-Machine | Client Credentials | OAuth2 client_credentials grant, scope-based, JWT with type: 'm2m' |
| Hooks / Webhooks | Webhooks | HMAC-SHA256 signed, retry with exponential backoff |
| Custom Claims | Custom JWT Claims | Per-application, 5 value types (static, user attribute, role-based, expression) |
| MFA | Multi-Factor Auth | TOTP, SMS OTP, WebAuthn/Passkeys, adaptive MFA with risk scoring |
| Passwordless (Email) | Magic Links | Token-based email login with auto-signup |
| Passwordless (SMS) | SMS OTP | Twilio provider, rate-limited with cooldown |
| Attack Protection | Attack Protection | IP rules, brute-force lockout, CAPTCHA (Turnstile/hCaptcha/reCAPTCHA), suspicious login detection |
| Log Streaming | Log Streaming | Webhook, S3, Datadog, Splunk |
| User Import/Export | User Import/Export | CSV and JSON, async processing with progress tracking |
| SCIM | SCIM 2.0 Provisioning | RFC 7644 compliant, attribute mapping, bulk operations |
| Custom Domains | Custom Domains | White-label auth pages, automatic SSL |
| Rate Limiting | Rate Limiting | 4 tiers, sliding window, standard headers |
| Branding | Branding | Logo, colors, favicon, company name |
Migration Steps
Step 1: Export Users from Auth0
Use the Auth0 Management API to export your users. You can use the Auth0 Dashboard (User Management > Import/Export) or the API directly:
# Create an export job
curl -X POST https://YOUR_AUTH0_DOMAIN/api/v2/jobs/users-exports \
-H "Authorization: Bearer $AUTH0_MANAGEMENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"format": "json",
"fields": [
{ "name": "user_id" },
{ "name": "email" },
{ "name": "email_verified" },
{ "name": "name" },
{ "name": "given_name" },
{ "name": "family_name" },
{ "name": "created_at" },
{ "name": "app_metadata" },
{ "name": "user_metadata" }
]
}'# Check export status
curl https://YOUR_AUTH0_DOMAIN/api/v2/jobs/JOB_ID \
-H "Authorization: Bearer $AUTH0_MANAGEMENT_TOKEN"
# When status is "completed", download the file from the location URLAuth0 does not export password hashes for users who signed up with email/password. These users will need to reset their password after migration, or you can use the lazy migration pattern described below.
Step 2: Transform and Import Users into Auris
Transform the Auth0 export format to the Auris import format:
// transform-auth0-users.ts
import fs from 'fs'
interface Auth0User {
user_id: string
email: string
email_verified: boolean
name: string
given_name: string
family_name: string
created_at: string
app_metadata: Record<string, unknown>
user_metadata: Record<string, unknown>
}
interface AurisImportUser {
email: string
firstName: string
lastName: string
emailVerified: boolean
// Password is omitted — users will set a new one via password reset
}
const auth0Users: Auth0User[] = JSON.parse(
fs.readFileSync('auth0-export.json', 'utf-8')
)
const aurisUsers: AurisImportUser[] = auth0Users.map((user) => ({
email: user.email,
firstName: user.given_name || user.name?.split(' ')[0] || '',
lastName: user.family_name || user.name?.split(' ').slice(1).join(' ') || '',
emailVerified: user.email_verified,
}))
fs.writeFileSync('auris-import.json', JSON.stringify(aurisUsers, null, 2))
console.log(`Transformed ${aurisUsers.length} users`)Upload to Auris:
curl -X POST https://auth.yourdomain.com/api/users/import \
-H "Authorization: Bearer $AURIS_ACCESS_TOKEN" \
-H "x-tenant: your-tenant-id" \
-F "[email protected]" \
-F "format=json"Monitor import progress:
curl https://auth.yourdomain.com/api/users/import/IMPORT_JOB_ID \
-H "Authorization: Bearer $AURIS_ACCESS_TOKEN" \
-H "x-tenant: your-tenant-id"Step 3: Create Applications in Auris Console
For each Auth0 Application, create a corresponding Application in Auris:
- Open Console then Applications then Create Application
- Set the Application Type to match the Auth0 application type:
- Auth0 “Single Page Application” or “Regular Web Application” = Auris WEB
- Auth0 “Native” = Auris MOBILE
- Auth0 “Machine to Machine” = Auris M2M
- Configure Allowed Callback URLs with the same redirect URIs from Auth0
- Configure Allowed Origins with the same origins from Auth0
- Note the new Client ID (and Client Secret for M2M apps)
Step 4: Replace the Auth0 SDK with Auris SDK
Install the Auris SDK and remove the Auth0 SDK:
# Remove Auth0
npm uninstall @auth0/auth0-spa-js @auth0/auth0-react @auth0/nextjs-auth0
# Install Auris
npm install @auris/js @auris/react
# For Next.js:
npm install @auris/nextjsStep 5: Update Your Application Code
The Auris SDK API is intentionally similar to Auth0’s to minimize migration effort.
Provider Setup (React):
// BEFORE (Auth0)
import { Auth0Provider } from '@auth0/auth0-react'
function App() {
return (
<Auth0Provider
domain="your-tenant.auth0.com"
clientId="auth0-client-id"
authorizationParams={{
redirect_uri: window.location.origin + '/callback',
}}
>
<MyApp />
</Auth0Provider>
)
}
// AFTER (Auris)
import { AurisProvider } from '@auris/react'
function App() {
return (
<AurisProvider
domain="auth.yourcompany.com"
clientId="auris-client-id"
redirectUri={window.location.origin + '/callback'}
>
<MyApp />
</AurisProvider>
)
}Authentication Hook:
// BEFORE (Auth0)
import { useAuth0 } from '@auth0/auth0-react'
function Profile() {
const {
loginWithRedirect,
logout,
user,
isAuthenticated,
isLoading,
getAccessTokenSilently,
} = useAuth0()
if (isLoading) return <p>Loading...</p>
if (!isAuthenticated) return <button onClick={loginWithRedirect}>Log in</button>
return (
<div>
<p>Welcome, {user.name}</p>
<button onClick={() => logout({ logoutParams: { returnTo: window.location.origin } })}>
Log out
</button>
</div>
)
}
// AFTER (Auris)
import { useAuris } from '@auris/react'
function Profile() {
const {
loginWithRedirect,
logout,
user,
isAuthenticated,
isLoading,
getAccessToken,
} = useAuris()
if (isLoading) return <p>Loading...</p>
if (!isAuthenticated) return <button onClick={loginWithRedirect}>Log in</button>
return (
<div>
<p>Welcome, {user.name}</p>
<button onClick={() => logout({ returnTo: window.location.origin })}>
Log out
</button>
</div>
)
}API Calls with Access Token:
// BEFORE (Auth0)
const token = await getAccessTokenSilently()
// AFTER (Auris)
const token = await getAccessToken()
// The rest of your API call code stays the same
const response = await fetch('/api/protected', {
headers: { Authorization: `Bearer ${token}` },
})Permission Check (React):
// BEFORE (Auth0 — manual claim check)
const { user } = useAuth0()
const hasPermission = user?.['https://myapp.com/permissions']?.includes('read:data')
// AFTER (Auris — built-in permission hook)
import { useCheckPermission } from '@auris/react'
const canReadData = useCheckPermission('read:data')Next.js Middleware:
// BEFORE (Auth0)
import { withMiddlewareAuthRequired } from '@auth0/nextjs-auth0/edge'
export default withMiddlewareAuthRequired()
// AFTER (Auris)
import { aurisMiddleware } from '@auris/nextjs/middleware'
export default aurisMiddleware({
protectedPaths: ['/dashboard(.*)'],
publicPaths: ['/', '/about', '/pricing'],
loginUrl: '/auth/login',
})Step 6: Update Redirect URIs
Update the Callback URLs in your Auris application to match what your app sends:
- Replace
https://your-tenant.auth0.com/withhttps://auth.yourcompany.com/in all redirect URIs - If you were using Auth0’s default callback path (
/api/auth/callback), update it to your Auris callback path
Step 7: Migrate Rules to Actions
Auth0 Rules and Actions map to Auris Actions Engine triggers:
| Auth0 Trigger | Auris Trigger |
|---|---|
post-login | post-login |
pre-user-registration | pre-register |
post-user-registration | post-register |
post-change-password | post-change-password |
Auth0 Action example:
// Auth0 Action (post-login)
exports.onExecutePostLogin = async (event, api) => {
if (!event.user.email_verified) {
api.access.deny('Please verify your email before logging in.')
}
api.idToken.setCustomClaim('https://myapp.com/role', event.user.app_metadata.role)
}Auris Action equivalent:
// Auris Action (post-login trigger)
// Created via Console → Actions → New Action
async function action(context) {
if (!context.user.emailVerified) {
return { deny: true, message: 'Please verify your email before logging in.' }
}
return {
claims: {
role: context.user.metadata?.role || 'user'
}
}
}Create actions in the Console under Actions, or use the API:
curl -X POST https://auth.yourdomain.com/api/actions \
-H "Authorization: Bearer $AURIS_ACCESS_TOKEN" \
-H "x-tenant: your-tenant-id" \
-H "Content-Type: application/json" \
-d '{
"name": "Enforce email verification",
"trigger": "post-login",
"code": "async function action(context) {\n if (!context.user.emailVerified) {\n return { deny: true, message: \"Please verify your email before logging in.\" }\n }\n return {}\n}",
"status": "ACTIVE",
"order": 1
}'Step 8: Migrate RBAC
Export roles and permissions from Auth0 and recreate them in Auris:
# List Auth0 roles
curl https://YOUR_AUTH0_DOMAIN/api/v2/roles \
-H "Authorization: Bearer $AUTH0_MANAGEMENT_TOKEN"For each role, create it in Auris:
curl -X POST https://auth.yourdomain.com/api/roles \
-H "Authorization: Bearer $AURIS_ACCESS_TOKEN" \
-H "x-tenant: your-tenant-id" \
-H "Content-Type: application/json" \
-d '{
"name": "Editor",
"description": "Can edit content",
"color": "#3B82F6"
}'Then assign permissions to the role using the Auris Console (Roles > [Role] > Permissions) or API. Auris supports tri-state permissions (ALLOW/DENY) which is more granular than Auth0’s binary model.
Step 9: DNS Cutover
Once everything is tested:
- Set up a custom domain in Auris (e.g.,
auth.yourcompany.com) - Update DNS to point your auth domain to Auris
- Update all applications to use the new domain
- Monitor the Auris audit logs for the first few hours after cutover
Handling Passwords (Lazy Migration)
Since Auth0 does not export password hashes, you have two options:
Option A: Force Password Reset (Simpler)
After importing users, trigger a bulk password reset email:
- All imported users receive a “Set your password” email
- Users click the link and set a new password on Auris
- No code changes required in your application
Option B: Lazy Migration (Zero Friction)
Run Auth0 and Auris in parallel temporarily. When a user logs in:
- Auris attempts to authenticate the user
- If the user has no password in Auris (imported without one), Auris returns a specific error
- Your application catches this error and attempts authentication against Auth0 as a fallback
- If Auth0 succeeds, your application creates the password in Auris via the Admin API
- Subsequent logins go directly through Auris
This approach migrates passwords transparently as users log in, with zero friction.
The lazy migration pattern requires keeping your Auth0 tenant active during the migration period. Plan to decommission it after a reasonable period (e.g., 30-90 days) when the majority of active users have migrated.
Rollback Plan
If issues arise during migration:
- Keep Auth0 active for at least 30 days after cutover
- DNS rollback: Point your auth domain back to Auth0 (TTL permitting, this takes effect within minutes to hours)
- SDK rollback: Revert the
@auris/*packages to@auth0/*in your application code and redeploy - No data loss: Users created in Auris during the migration period can be exported and re-imported into Auth0 if needed
To minimize rollback risk, run both systems in parallel for a testing period before the full DNS cutover.
Post-Migration Checklist
After completing the migration, verify:
- All users can log in (test with multiple accounts)
- Social login providers work (Google, GitHub, etc.)
- MFA enrollment and verification work
- Role-based access control is enforced correctly
- Webhooks are delivering events to your endpoints
- Log streaming is active (if configured)
- Custom domains and SSL are working
- Mobile apps (if any) are updated with new SDK
- M2M applications can obtain tokens
- SCIM provisioning is connected (if used)
Related Guides
- Hosted Login (PKCE) — Implementing the login flow
- Roles & Permissions — Setting up RBAC
- Fine-Grained Authorization — Migrating from Auth0 FGA
- User Import/Export — Bulk user migration
- Custom Domains — White-label your auth pages
- Migrating from Firebase — Alternative migration guide