Skip to main content
Authentication

Auth0 in Production: 12 Gotchas That Bit Me

By Ilir Ivezaj· ·10 min read
Ilir Ivezaj professional portrait

Auth0 is excellent — until you hit the edge cases that the documentation glosses over. After deploying Auth0 across multiple production applications sharing a single tenant, I've catalogued every gotcha that cost me debugging time. Save yourself the pain.

1. Action Namespace Conflicts Between SPAs

If multiple SPAs share an Auth0 tenant, your Post-Login Actions emit custom claims under a namespace (like https://yourdomain.com/roles). Every app sharing that tenant must use the same namespace, or some apps won't see the claims. I had two apps using different namespaces — one saw roles, the other didn't. The fix was standardizing on a single namespace across all Actions.

2. Safari ITP Kills Silent Authentication

Safari's Intelligent Tracking Prevention blocks third-party cookies. If your Auth0 tenant is on yourtenant.auth0.com (not a custom domain), Safari treats it as third-party. loginWithPopup() and silent token refresh via iframes both fail — silently. No error, just an expired session.

The fix: use loginWithRedirect() for Safari users, and set cacheLocation: 'localstorage' to persist tokens without relying on cookies. Better yet, set up a custom domain on Auth0 so it's same-origin.

3. Token Refresh Race Conditions

Multiple browser tabs calling getAccessTokenSilently() at the same time create a race condition. Each tab tries to use the same refresh token, but Auth0's refresh token rotation invalidates it after first use. The second tab gets a "invalid refresh token" error and logs the user out.

Set useRefreshTokens: true with cacheLocation: 'localstorage' so all tabs share the same token cache. Also set the refresh token reuse_interval to 30 seconds in your Auth0 API settings — this allows the same refresh token to be used multiple times within that window.

4. Custom Claims Size Limit

ID tokens have an effective ~2KB limit for custom claims. If your Action stuffs too much data into the token (user permissions, organization data, feature flags), the token silently truncates or Auth0 returns a generic error. Put large data in app_metadata and fetch it via the Management API on the backend.

5. MFA Enrollment UX on Mobile

The default MFA enrollment widget looks terrible on mobile. The QR code for authenticator apps is tiny, and the SMS fallback flow has confusing navigation. Build a Custom Universal Login with acr_values to control the MFA experience. It's more work upfront but the conversion rate on MFA enrollment doubles.

6. Management API Rate Limits

The Management API has aggressive rate limits: 15 requests/second on free tier, even on paid plans it's limited. If you're doing bulk user operations (importing users, updating metadata), you'll hit 429 errors fast. Batch operations with exponential backoff, or use the Management API's bulk endpoints where available.

7. Tenant Region Is Permanent

When you create an Auth0 tenant, you choose a region (US, EU, AU, JP). This cannot be changed later. There is no migration path. If you start with US and your customers are in the EU, you'll need a new tenant. Choose carefully, especially for GDPR compliance.

8. Action Secrets Have a 32KB Total Limit

All Actions share a 32KB secrets pool. If you have multiple Actions each with API keys, database credentials, and webhook URLs, you'll run out. The workaround: store a single encrypted JSON blob as one secret and parse it in each Action.

9. Refresh Token Rotation Needs a Reuse Interval

Enable refresh token rotation for security, but set reuse_interval to at least 30 seconds. Without it, parallel API calls on page load (common in SPAs) will invalidate each other's refresh tokens, creating a logout loop.

10. Custom Domain SSL Takes Days, Not Minutes

The docs say custom domain SSL propagation takes "a few minutes." In practice, it takes 24-48 hours. Plan accordingly and don't switch your production DNS until SSL is confirmed working.

11. Rules Still Execute Before Actions

Rules are deprecated in favor of Actions, but if you have any legacy Rules, they execute before Actions in the login pipeline. If both modify the same claims, the Action overwrites the Rule. If the Rule errors, the Action never runs. Migrate completely or be prepared for confusing behavior.

12. Auth0 CLI in WSL: No Keyring

The Auth0 CLI stores tokens in the system keyring. WSL2 doesn't have a keyring by default, so tokens expire and you must re-authenticate frequently: auth0 login --domain yourtenant.auth0.com. There's no persistent session workaround in WSL — just accept the re-auth cycle.

About the author: Ilir Ivezaj deploys Auth0 across multiple production applications for workflow automation platforms. He's a technology executive and entrepreneur based in Michigan. Get in touch.