Auth0 Rules and Hooks Are Read-Only. Which End-of-Life Date Applies Depends on Whose Page You Read.
Roundups say 18 November 2026. Auth0's own docs say 2027 and 2028. Either way, the migration is not a find-and-replace.
Two dates, one read-only freeze
Developer deadline trackers circulating this month list 18 November 2026 as the day Auth0 Rules and Hooks stop executing. That date sits close to a real one: 18 November 2024, when Auth0 made both features read-only. The documentation we read gives much later end-of-life dates.
| Milestone | Rules | Hooks |
|---|---|---|
| Unavailable in new tenants | 16 October 2023 | 16 October 2023 |
| Read-only (no code edits) | 18 November 2024 | 18 November 2024 |
| Stop running and removed | 31 January 2028 | 30 April 2027 |
We cannot see your tenant, and Auth0 has revised these timelines before. Treat the dates above as what the public docs said when this was written, check the banner in your own dashboard, and do not let a third-party calendar set your sprint. If you have a contract or an enterprise support channel, ask there.
The source pages are Auth0's own: Hooks overview and the Rules to Actions migration guide. Check both before quoting a date to your security team.
What read-only means in practice
Active Rules and Hooks keep running. You can enable or disable them and rotate configuration values and secrets. You cannot change their code through the Dashboard, the Management API, the CLI, the Deploy CLI or Terraform. So a bug in a Rule today is not fixable in place. The only repair is to write an Action.
That is the real deadline for most teams: the next time a Rule needs a change, the change is a migration.
Why you can migrate one Rule at a time
Actions attach to the Post-Login trigger and run after active Rules. That ordering lets you keep a 1:1 mapping, one Rule to one Action, and cut over each block independently. Auth0 suggests starting from the end of the Rules pipeline and working backwards, because later Rules depend on earlier ones, not the other way round.
The cutover step needs care. Deploy the Action, attach it to the trigger and disable the matching Rule back to back. A gap leaves either both running, which can double-apply a side effect, or neither running, which can skip a control.
Five behaviours that change
The signature change is the visible part. The quieter changes are where logins break.
| Area | Rules | Actions |
|---|---|---|
| Signature | function (user, context, callback) | async (event, api) |
| Objects | Mutable user and context | Read-only event; changes go through api |
| Finishing | Call callback() | Return; api.access.deny() to fail |
| Secrets | One global configuration object, readable by every Rule | Per-Action secrets via event.secrets |
| Dependencies | Approved packages and versions only | Any npm package, versions set outside the editor |
| Management API | Global auth0 object with a token | Neither; create an M2M app and fetch a token yourself |
| Metadata writes | One API call per update | Queued; one call at the end of the flow |
Take secrets first. In Rules, a leaked or overbroad value in configuration was visible to every Rule. In Actions each secret belongs to one Action, which is better, but it means you must decide which Action owns each value instead of copying the whole object across.
Then take the Management API. A Rule that called the Management API with the injected token has no equivalent. You create a Machine-to-Machine application, store its credentials as secrets, and fetch a token inside the Action. Actions cannot cache that token between executions, so every login pays an extra authentication request, and the calls count against rate limits. Move that work off the login path where you can, for example to a queue or a post-login webhook.
A minimal conversion
A Rule that denies logins for unverified emails and adds a custom claim shows the pattern. The Action drops the callback, reads from event and writes through api.
function (user, context, callback) {
if (!user.email_verified) {
return callback(new UnauthorizedError('Verify your email first.'));
}
context.idToken['https://example.com/tier'] = user.app_metadata.tier;
callback(null, user, context);
}exports.onExecutePostLogin = async (event, api) => {
if (!event.user.email_verified) {
api.access.deny('Verify your email first.');
return;
}
api.idToken.setCustomClaim(
'https://example.com/tier',
event.user.app_metadata.tier
);
};Note what is missing: no callback, no returned user, no mutation of context. If your Rule also changed user.app_metadata and then read it again later in the same Rule, the queued write in Actions means the second read still sees the old value.
A migration order that holds up
- List every active Rule and Hook, with the secrets and configuration keys each one reads.
- Rebuild each in a non-production tenant as one Action, using Auth0's Deploy CLI to script it.
- Replace global configuration reads with per-Action secrets, and note any npm packages for the dependency list.
- Test the paths that fail rarely: denied logins, MFA enrolment, redirects and rate-limit errors.
- Cut over in production at low traffic, Action on and Rule off in one step.
Finish before a Rule needs a hotfix, not after. A planned migration with a rollback is a week of work. An emergency one is a login outage.
What to watch next
Auth0 has moved these dates in the past, and the read-only freeze means the pressure arrives through bugs rather than the calendar. The practical signal to watch is your own backlog: the first change request that touches a Rule is the start of your migration.
Frequently asked questions
Related reading
Your Postgres Outbox Can Silently Skip Events. Sequence Order Is Not Commit Order.
The usual outbox relay reads rows with an id above its cursor. Postgres assigns ids before commit, so a slow transaction can fall behind the cursor and never be published.
Node.js Makes Every Release LTS From Version 27. The Odd-Version Skip Becomes an Alpha Test.
From Node.js 27, there is one major release a year and every one becomes LTS. The old "skip the odd versions" habit is replaced by an alpha channel your CI should test.
TLS Certificates Drop to 100 Days on 15 March 2027. Automating Renewal Is the Easy Half.
The CA/Browser Forum schedule cuts certificate lifetimes to 100 days in March 2027 and 47 days in 2029. Inventory, not ACME, is where teams get caught.