I’m trying to understand how action modules actually work as the information I’ve gotten from the support team does not seem to align with my testing.
We’ve been trying to find a solution for our high M2M usage. We create tokens to call an API to fetch additional data to add to claims, and we cache those tokens in the action cache, but the cache hit ration is very low and we’re using a lot more M2M tokens than we’d expected.
It has been suggested that using Action Modules would help resolve - we’ve been told that they run on a separate smaller pool of resources and although they don’t have a cache API available to them you can just store the values as a var in the module (i.e. a global variable).
I have to admit to being a bit skeptical about that as the documentation does not talk about anything like that and if they’re running as separate compute resources wouldn’t that imply that a call to an action is a remote call and therefore pass-by-value rather than pass-by-reference? In my testing (admittedly with a limited number of logins), I see no evidence of the lifecycle of the module being different to the action, and objects I pass into an action appear to be pass-by-reference.
Before I refactor our token acquisition to use a module and try this caching in a global variable (which I will only really know if it works when it gets to prod), can anyone tell me if it’s really the case that modules run separately and this will help with M2M caching?
And if they are shared and separate from the actions, do I have to worry about concurrency?
Actions Modules do not have their own runtime. They run in the runtime of the Action that includes them.
That seems to confirm that these do not run independently and are just code imported into the running action - still it would be nice to hear that from someone in Auth0 engineering as we were told by support and our Account Manager that we were not allowed to talk to an engineer.
Due to my skepticism about this description of how modules work, what I’ve ended up doing is implementing a module where you pass in the reference to the cache from the action event and the module uses that. That feels more likely to result in the token being reused across action invocations than storing it in a module variable. But without testing at scale it’s hard to know.
Still hoping that someone who actually knows how this works under the hood can chime in.
I am sorry for the delayed response to your question!
You are asking whether Auth0 Action Modules run as separate compute that can hold a token in a module-level global variable to improve M2M token caching, whether objects passed into an Action behave as references or copies, and whether concurrency is a concern.
Summary: The documented Action cache is the supported caching mechanism, its hit rate depends on how Auth0 routes each execution, and module-level variables are not a documented caching method, so do not rely on them for token caching.
Root Cause: Your low cache hit rate and high M2M usage have two likely sources. First, api.cache entries are not guaranteed to be available on the next login or credentials exchange, so many executions fetch a new token. Second, an Action that requests a client credentials token can re-trigger the credentials exchange Action, which issues additional M2M tokens. Auth0 Community responses also confirm that the token the Action caches is a separate token from the one issued to the calling application, so caching the Action's token does not reduce the number of tokens issued to your M2M client.
Solution: Stop caching M2M tokens inside the Action, and reduce how often the Action needs a token at all.
Verify whether your Action requests a client credentials token on each execution. Check the Action logs for repeated token requests in a single flow, and check whether the credentials exchange Action runs during its own token request. If it does, the Action is generating tokens for itself.
Remove token acquisition from onExecuteCredentials-Exchange if the Action is only fetching claim data. Move that data fetch to a path that does not call the credentials exchange trigger, or to the service that issues the token.
Cache the claim data rather than the token. If the additional claims rarely change, store the claim results in your own backend service or a shared store such as Redis, keyed by user ID, with a time-to-live that matches how quickly the data changes. A cache hit then avoids both the token request and the API call.
Reuse the token in your own service. If the Action must call your API, have your API accept a long-lived token from a secure store that your service manages, and refresh it before expiry. Do not rely on the Action cache to persist between executions.
Measure the change in M2M usage in the tenant logs and the Dashboard quota view after each change, so you can confirm the effect in production.
Do not store tokens in module-level global variables. The documentation does not describe module-level state as a supported caching mechanism, and Action execution does not guarantee that the same instance serves successive requests. A cache held in memory may be empty on most requests, and concurrent executions do not share it. Because Auth0 does not document this behavior, treat any performance result from this approach as unverified.
Ask Auth0 Support to confirm the execution model for Action Modules, including whether module state persists between executions and whether concurrent executions can share it, before you refactor the token acquisition.
Some helpful links regarding the matter:
Auth0 Community: Caching M2M token in Actions (confirms that only the Action's token can be cached, that Action caching is limited to the same flow for debugger tests, and that calling client credentials inside onExecuteCredentials-Exchange can loop)
@nik.baleca The reason I asked this here was because an Auth0 support engineer spent nearly two months trying to convince me that Auth0 modules ran independently and could improve caching by using global variables - at no point did I find that credible.
Case #02963752 if you have access to it.
Action Modules run on a smaller set of runtimes, which should increase the cache hits. The scaling of the number of caches applies to the whole prod-us-5 space (depending on the tenant), not just your tenant.
From what I’ve heard from the engineering team about modules, you can store the token in memory and don’t need to use the cache.
I’ll take a look through your suggestions, we are already caching tokens, and the data returned that we use to populate claims (in both the session and refresh token metadata), but authentications for new users mostly do not find a cached token (it’s about a 20% hit rate). It’s manageable, but more M2M tokens than we were expecting to use.