Getting user_metadata with auth0-spa-js?

I have a MEAN stack application (Angular 8)

I used this tutorial as an authentication model and it works great: https://auth0.com/docs/quickstart/spa/angular2

However, I now need to access user_metadata and app_metadata so from what I can tell that requires accessing the Management API.

Does the spa-js not have a way of doing this natively? Do I need to redo my application auth to use NodeJS apis instead? I feel like if there’s a way to do it, it would be in the getUser function

getUser$(options?): Observable {
return this.auth0Client$.pipe(
concatMap((client: Auth0Client) => from(client.getUser(options))),
tap(user => this.userProfileSubject$.next(user))
);
}

If I have to rebuild, what method should I do? Would it be the “Regular Web App” using Express to handle the auth?

It’s just that using the Auth Service and AuthGuards works so well I really don’t want to have to refactor everything.

Thanks!

2 Likes

So, I found i was able to get the roles if I hit the v2 api:
mysite.auth0.com/api/v2/users/[user id]/roles

However, it required a BearerToken that expires.

Looking at this page, it seems like I have to make a post everytime I need to get this token?

Is this really going to generate a new token everytime someone hits the page?

I’m using Angular and it is getting really frustrating because it seems like this is all node based and not actually SPA based. Do I need to redo my application’s authentication in order to work with Auth0? I want to use the auth0-spa-js because it seems like that’s what its written for.

This seems like a serious hurdle to overcome if I have to implement my own node api backend for these types of calls.

1 Like

Hey there. Having the same issue. I hope there is a way to do it natively

I’ve gone down the same rabbit hole of Auth0 docs with no end in sight. I don’t know why the user_meta and app_meta don’t come down in the getUser method but I can’t find a way to get it using the SPA SDK.

Any help from someone at Auth0 would be super helpful! Currently having to store partial records in our own DB so we could launch and it’s a pain.

Hi @Cobalt,

it’s important to understand the difference between app_metadata and user_metadata as described here.

That said, it is possible for an end user to request an access token that is valid for the Auth0 Management API from within a SPA, however, that comes with limited scopes, i.e. you can only request user_metadata of your own user but not app_metadata, and obviously also no information about other users.

Therefore, two approaches, the first one being the easiest imo:

  1. Option: Create a rule and add the info as custom claims into the ID token.

  2. Option (but unnecessary more complex): Request an access token for the Auth0 management API (via respective audience option parameter in the request), with the value of https://YOUR_TENANT.auth0.com/api/v2/, which can be used to request user information.

Here’s what I ended up doing: Create an Empty Rule in Auth0.

I think what we’re doing it just extending the current Auth0 function.

Here’s the Rule I created in Auth0:

function (user, context, callback) {
  var namespace = 'https://www.yournamespace.com';
	const assignedRoles = (context.authorization || {}).roles;
 
	let idTokenClaims = context.idToken || {};
  let accessTokenClaims = context.accessToken || {};
   
  idTokenClaims[`${namespace}/roles`] = assignedRoles;
  accessTokenClaims[`${namespace}/roles`] = assignedRoles;
  
  context.accessToken = context.accessToken || {};  
  
  if (user.user_metadata) {
    // context.idToken[`${namespace}/user_metadata`] = user.user_metadata;
    idTokenClaims[`${namespace}/user_metadata`] = user.user_metadata;
  }
  if (user.app_metadata) {
    // context.idToken[`${namespace}/app_metadata`] = user.app_metadata;
    idTokenClaims[`${namespace}/app_metadata`] = user.app_metadata;
  }
  
  // context.idToken[namespace + 'roles'] = assignedRoles;
  context.idToken = idTokenClaims;
  context.accessToken = accessTokenClaims;
 
  callback(null, user, context);
}

here’s the node.js code that calls this:

module.exports.getUserRoles = async(req, res) => {
    userId = req.params.id;
    rolesByUserUrl = `${usersUrl}/${userId}/roles`

    return new Promise((resolve, reject) => {
        axios.post(authUrl, authHeaders)
        .then((authResponse) => {
            var mgmtHeaders = {
                headers: { 
                    "Authorization": `Bearer ${authResponse.data.access_token}`, 
                    'Content-Type': 'application/json',
                }
            }
            axios.get(rolesByUserUrl, mgmtHeaders) 
                .then((userRoles) => {                                     
                    resolve(userRoles.data);
                })
                .catch(function(err) {
                    console.log("Mgmt Error: " + err);
                });
        }).catch(function(err) {
            console.log("Auth Error: " + err);
            reject(err);
        });
    }) 
}
1 Like

Thank you, @jasoncascadia!

This may sound kind of over the top, but with everything else going on in the world right now you reaching out with help means a lot more than you might imagine.

I’m going to check out writing a rule like you suggested in the next day and hopefully it will get me to where I need to be.

Thanks again.