Auth0 Home Blog Docs

Access Hook-created app_metadata (Javascript)


So I created a pre-registration hook:

module.exports = function (user, context, cb) {

var response = {};
// External packages
uniqid = require(‘uniqid’);
keygen = require(‘keygenerator’);
// Add app metadata to the newly created user
response.user = {
app_metadata: { id: uniqid.time(), key: keygen.session_id() }
// Return to callback
cb(null, response);

Which works:

But for the life of me, I cannot figure out how to get that data in my app:

handleAuthentication () {
webAuth.parseHash(async (err, authResult) => {

Which outputs:

I feel like a mad man going through the docs for Auth0. What am I doing wrong?


I solved the problem by creating a rule as follows:

function (user, context, callback) {
context.idToken[‘https://id’] =;
context.idToken[‘https://key’] = user.app_metadata.key;
callback(null, user, context);

Read more about the issue (and why you need to prepend http) here:

I feel this is a bit hack-y, so would still like a better way if there is one.


Hi @chisnallio

Custom claims in JWTs should be namespaced and is enforced by Auth0 (so you won’t see your data if it’s not namespaced).

The recommendation is to use something like your domain as the namespace so the rule would be something like:

function (user, context, callback) {
    context.idToken[''] =;
    context.idToken[''] = user.app_metadata.key;
    callback(null, user, context);

In our system we use the API host as our namespace as you can have custom claims namespaced per API (e.g.


Thanks for the reply. My exclusion of the actual domain was on account of having this on a test server so it will change. I did try and add a context property to automate this, but couldn’t quite get it to work (I think because the property I was using had auth0 as the domain which is ignored).
Somethings like:

function (user, context, callback) {
context.idToken[context.SOMETHING + ‘/id’] =;
context.idToken[context.SOMETHING + ‘/key’] = user.app_metadata.key;
callback(null, user, context);

That way, my app could always access it even if I reused the code elsewhere… I hate having strings like that to update when I reuse code.


You were on the right track though you’ve made one small mistake which in my opinion is very easy to make as you have to read the docs very carefully, I made the same mistake as well.

The configuration properties do not live in the context object but instead live in a goal configuration object.

To show you what I mean here’s a simple rule from our system that adds the user’s email to the access_token:

function addEmailToAccessToken(user, context, callback) {
    context.accessToken[configuration.AUTH_NAMESPACE + '/email'] =;
    return callback(null, user, context);

Note the use of configuration and not context when building the claim name.


Hmm, if you attach to idToken instead of accessToken, configuration.AUTH_NAMESPACE comes out as undefined which is a funny little quirk I came across in testing before because it does work. For some reason, unidentified is allowed through even through it doesn’t begin with https://

undefined/id: “jp3pbgcx”
undefined/key: “sGe27EjeDPa1Yoova0F6RXUwHmS3e6ab”

Incase this gets fixed, it’s a less preferable hack.

I couldn’t test the accessToken though because it wasn’t attached to the result from webAuth.parseHash()? How do you read that?


To answer your query about getting the access token, your request should have the responseType set to token id_token

In regards to the value coming our as undefined, have you made sure to add a configuration property to your rules? (The AUTH_NAMESPACE is not an inbuilt value, it’s a custom value)

You can do this in the Settings section of the Rules page in the Management dashboard.

Here’s a screenshot of some of our configurations that we have in our staging environment:

1 Like

Great. Thanks. Not quite as automated as I want, but I will use those constants when I settle on the domain name!

1 Like

Thanks a lot @charsleysa for bringing so much valuable contribution to this thread!

closed #10

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.