Accesing metadata

Hello, wonderful Auth0 community, please help me!

So, I’m a complete beginner here, both in terms of using Auth0, and kind of in terms of computer/coding in general, so forgive me if my questions seem obvious. I’ve been reading hours every day for weeks now trying to solve this, reading and watching online guides, and reading the auth0 pages and questions other people have asked on stackoverflow etc, I don’t even feel I’m close to an answer, and I’m starting to lose my mind, so somebody please at least point me in the right direction here. I’d also appreciate feedback on what I intend to use as my method to make my app work. hopefully just feedback that my idea sounds sensical, considering how auth0 functions if that’s possible as well. So I think what my problem is here is that I’m having trouble accessing the user.user_metadata value

I’m building a bug-tracker web app, which I envision as basically just a to-do list app where every user has a personal to-do list as well as the potential to create or join groups that share their own group to-do lists. To keep track of all that I think I just need to stick values into the users’ app_metadata to keep track of who is in what groups and what permissions they have in those groups. My problem is that I don’t know how to access the user.app_metadata value. In case it’s important, I’m also using Angular/typescript to build my app with, so it’s using the RxJS subscription things, and I’m using json-server as a fake database as I develop it.

So far I can get users to log in/logout to my application no problem, and I can use the user$ observable in the typescript file to do:

ngOnInit(): void {
this.auth.user$.subscribe(
(profile) => (this.profileJson = JSON.stringify(profile, null, 2))
);
}

and then I can use the interpolation thing where you put {{ user.name }} and {{ user.email }} into the html, and that all shows up just fine.

What I CANNOT figure out how to do is to access the user.user_metadata. In a .ts file where I’ve imported { AuthService } from ‘@auth0/auth0-angular’, Visual Studio Code gives me an error if I declare variable “user” and suggests “user$” instead. If I use user$ though, it doesn’t recognize app_metadata. so if I type:

this.auth.user$.user_metadata

it gives red squiggles under the user_metadata.

Does this mean user and user$ are different things? or is it that I haven’t imported some sort of data that let’s me access the app_metadata? And if so, what exactly is going on with the interpolated {{ name.email }}, because that works fine and I haven’t defined “name” in my code anywhere.

Anyway, I know it’s possible this is a super huge question, so if I can get a short sweet clear simple answer, great, if not, then please point in the right direction for the things I need to learn to make sense of this problem and I’ll happily do that as well.

And thank you so much for any help any of you can provide!

Hi @fullmetalpinky,

Welcome to the Community!

Your plan looks good. Since it sounds like you have permission tied to roles, you may want to consider using Auth0’s Core RBAC:

The user’s app_metadata won’t be exposed to your app by default. The user profile info comes from the ID Token which just has basic profile info by default (ID Token Structure)

You can extend the information included in the ID Token by adding Custom Claims.

To do this, you can create a post-login Action:

exports.onExecutePostLogin = async (event, api) => {
  const namespace = 'https://my-app.example.com';
  api.idToken.setCustomClaim(`${namespace}/groups`, event.user.app_metadata.groups);
}

In your app, you should be able to access the custom claim just like you would any other claim:

{{ user[“https://my-app.example.com/groups”] }}

Thank you for your help, @stephanie.chamblee !
I have to learn about the stuff you told me before I can implement it, but it looks like you gave me everything I needed. Thank you very much :slight_smile:

1 Like

actually, what is that syntax with {{ user[https://my-app.example.com/groups] }}?

I don’t understand what it means at all. It sorta looks like Angular2 interpolation with the {{}}, but I don’t understand anything inside those curly braces.

Actually, I just looked up custom claim. I’ll figure it out I think.

Sorry, I had a typo (forgot the quotation marks originally). Custom claims must be namespaced with a URI. You’d reference the namespaced custom claim like this in the template:

user[“https://my-app.example.com/groups”]

Ok, I’ve been trying to figure out what this is all about, but I am still stumped. I want to stress that I am really really a beginner. I haven’t coded for very long and I know little about auth0 beyond what I’ve learned from some intro to auth0 YouTube vids and what little I’ve been able to parse from documentation, which I’ll admit is not a great amount. It’s clear to me that you understand what you’re doing and are very good at it, @stephanie.chamblee, but I think you might be overestimating me and going a bit too fast for me with your explanations.

So, on that note, I have some very detailed follow-up questions that I think will clarify things for me, and if you could answer them I would be so very grateful:

you use this exports.onExecutePostLogin. I see it all over the place in the auth documents as well. What actually is it all? what is “exports”? where does keyword “exports” come from? do I need to import some extra technology to get “exports” or does auth0 define exports somewhere?

In what context is this exports.onExecutePostLogin for? as in where does this code actually literally go? I’ve noticed that Rules for auth0 are code that I put onto the auth0 website, rather than somehwere in my Visual Studio Code. Does this code go somewhere like that on the auth0 website? or does it go into my own code. If it does go into my code then where exactly do I put it?

Related to that note of Rules, I’m actually trying to figure out what’s going on with Actions, which you suggested. On Write Your First Action page it says as a prerequisite to writing actions to “create an Incoming Webhook for a Slack Workspace” and then links me to Sending messages using Incoming Webhooks | Slack. What actually is this Slack API thing? It seems to be a proper noun, there doesn’t seem to be such a thing as a generic API type called slack. As far as I can tell it’s just some sort of Facebook-like thing. What does it have to do with Actions in Auth0?

Also, On that Slack API page I can get through step 3, but I cannot do step 4, where they ask me to use my Webhook URL to post a message. I’ve played around with their website a bit and I can put messages into a chat and have them show up, but I have not been able to send a message to it via POST request, as I THINK they are telling me to do. I tried to set that up by giving my Angular2 app a button to call a post request that looks like:

postTest() {
console.log(“you pressed PostTest button!”);
return this.http.post(this.postUrl, this.postData, httpOptions);
}

while this.postUrl is the URL they provided me, and this.postData is “Hello World”, and httpOptions is:

const httpOptions = {
headers: new HttpHeaders({
‘Content-Type’: ‘application/json’,
})
}

Have I misunderstood? are they telling me to do something completely different?

So I keep seeing cURL being referenced everywhere, both in auth0 documentation and on that Slack API site. I have no idea what cURL is. Should I go learn cURL? is cURL a necessary thing for me to know to work with Auth0?

Sometimes in the documentation they have me select from cURL, C#, Go, Java, Node.JS, Obj-C, for example here: (Create Roles). These are all different back ends, yeah? Since I’m using Angular2 does that mean by default I’m using the Node.JS version?

I also keep seeing Axios.js being used in that documentation. My understanding is that axios is for sending http requests. Is it actually necessary for me to use axios? I’m using Angular2, and I believe they have their own http module. Does that mean I can forego axios and use Angular2’s native http support?

OK, so once I get it to where I can use a post-login Action to also include app_metadata values on my ID token, this allows me to easily access them for client-side logic with user.user_metadata, correct? I will at that point only have a jwt ID token which is a COPY of the actual values right?

So that means to change the actual values of a user’s app_metadata I need to First: Get a jwt Management API Token via GET HTTP request, and Second: Call an update/PUT HTTP request while passing the Management API token as the Authorization value in the header. Correct?

How do I actually get that Management API token in the first place? On Get Access Tokens it says you “request one when authenticating a user” but how is that actually done? Is it possible for me to just make, for example, a GET HTTP request inside of a function that I call from a button on my app to get the Token? It says I can call the auth0 Authentication API directly. is that what that is? is that what Authentication API Explorer is?Or can I only access a Management API token through some sort of rule or action attached to when I have a user log in?

I also see “flow” being mentioned all over the place. In regard to gettting access tokens Get Access Tokens says “Authentication API: If you prefer to write your code independently, you can call the Auth0 Authentication API directly. First, you need to know which flow to use. Then follow the instructions to implement that flow.” So what exactly is a “flow”?

I keep running into this page (Auth0 Management API v2 ) as I hyperlink click through the auth0 documents. What is it? Why does it ask for API token values and why does it let me run code snippets through it? Is it just some kind of testing tool or is it an integral part of setting up auth0?

So I’m actually still confused about this {{ user[“https://my-app.example.com/groups”] }} thing. You didn’t object when I said the {{}} makes it look like text interpolation, so I’m assuming that’s what it is, text interpoloation, but I still don’t know this syntax:

user[“https://my-app.example.com/groups”]

I know the user.name or user.whatever syntax. I’m not familiar with “user” followed by square brackets and a string value. What does it mean and where does it come from? Also, for this syntax is it generalizable such that maybe it could say:

someDifferentVariable[“some other string value”]

or is it only allowed with the “user” variable?

So I hope this isn’t too much for me to ask in one place, and I hope this is the right place to ask it all. Like I said before, I’m in the process of learning to code and very much a beginner so there’s a lot I don’t know, and a lot I don’t know that I don’t know, and If you can help me out with such a huge request I would be very deeply grateful I’ll name my firstborn child after you.

Hi @fullmetalpinky,

I’ll try to answer all of these, although, no need to name anyone after me :laughing:.

1-2:
The first couple of questions are related to Actions. When advising on how to customize the login flow, we are now suggesting Actions instead of Rules, because it is likely that Rules will eventually be replaced by Actions.

To sum that up, Actions are the new, improved way to customize login flows.

There are a few different triggers for Actions. You can do stuff when someone signs up or changes their password, etc. The Login flow runs at the same point in the login flow that Rules run, so whatever you would write a rule for, you should use a post-login Action.

Actions are edited directly in your Auth0 dashboard:

  1. Go to Actions > Flows and select the
  2. Click Create Action on the right side to create a new Action
  3. Add code:

  1. Click “Add to flow” after you hit deploy. The link to add it to a flow will pop up on the top right:

When you create a new post-login Action, you will see the function called exports.onExecutePostLogin. Exports are used in Node.js to make code available throughout the app, but you don’t need to alter this at all from the default code. As you can see in the screenshot above, this is already written out for you. This is just how Auth0 will know about your custom code and at which point during login the run it.

3-4:
Slack is a messaging app that is often used in the workplace. It’s not related to Auth0, it’s just a common app and is used as an example for those who happen to already use Slack. This example is helpful because it shows you how you can make an API request in an Action and use Action features like storing secrets (sensitive info you want to keep hidden). A simpler example would be to go through the steps I added above and add a custom claim to the ID Token:

exports.onExecutePostLogin = async (event, api) => {
  const namespace = 'https://my-app.example.com';
  api.idToken.setCustomClaim(`${namespace}/groups`, "Some Group");
}

5. Nope, no need to learn cURL. cURL is a command-line/terminal tool to help you make HTTP requests. You can use postman or just make the requests in your code if you prefer.

6. Backend languages are provided because you can only use the Management API to assign roles from the backend. Alternatively, instead of using the Management API, you can assign roles from you dashboard UI:

Most things you’d use the Management API for you can do from the dashboard. If you need to do something programmatically/automated, then you’d want to use the Management API from your own backend or from within an Action. You’d have to create and authorize a machine-to-machine app to do that.

7. No need to use axios. You can use Angular2’s nattive HTTP module.

8. Yes, that’s correct! The ID Token is a JWT that contains profile info. You can extend the default claims and include your own custom claims from within a post-login Action.

9. There’s actually a shortcut for this in post-login Actions. You can update the user’s metadata from inside an action like this: api.user.setAppMetadata(name, value)

If you’d like to add app metadata and add it to the ID token, you do something like this in the Action:

exports.onExecutePostLogin = async (event, api) => {
  const groupName = "My Group"
  // Add the group to the user's metadata to permanently update the user
  api.user.setAppMetadata("group", groupName)

  // Add the group to the ID Token so that the app can see what group the user belongs to
  const namespace = 'https://my-app.example.com';
  api.idToken.setCustomClaim(`${namespace}/groups`, groupName);
}

OR You could also check to see if the user already has a group before updating it like this:

exports.onExecutePostLogin = async (event, api) => {
  const namespace = 'https://my-app.example.com';
  const userGroup = (event.user.app_metadata || {}).group;
  if (userGroup) {
    // Since the user already has a group, just add it to the ID Token
    api.idToken.setCustomClaim(`${namespace}/groups`, userGroup);
  } else {
    // Since the user does not have a group yet, we'll need to update the user's metadata AND add it to the ID Token
    const groupName = "My Group"
    // Add the group to the user's metadata to permanently update the user
    api.user.setAppMetadata("group", groupName)

    // Add the group to the ID Token so that the app can see what group the user belongs to
    api.idToken.setCustomClaim(`${namespace}/groups`, groupName);
  }
}

10. If you just need to update the user’s metadata, it will be much simpler to use a post-login Action. Otherwise, you will need to create a machine-to-machine app and authorize it to use the Management API and build a backend API for your Angular app. It is possible to do a few things with the Management API from the frontend (docs), but generally not recommended.

11. Flow here refers to what type of OAuth2 flow your app needs to use. For instance, a SPA uses Authorization Code flow with PKCE whereas a backend API might use Client Credentials flow. The docs here explain this in more detail: Which OAuth 2.0 Flow Should I Use?

12. The Management API is basically the API version of the Auth0 dashboard. For example, instead of creating a user by clicking the purple “+ Create User” button:

You can use the Management API’s POST/api/v2/users endpoint instead.

Pretty much everything you can do from the Auth0 dashboard, you can do programmatically with the Management API. As you can imagine, there are security concerns with such a powerful API, which is why you can only use this tool from the backend such as in an Action or in your own custom API.

13. In JavaScript, bracket notation (user["name"]) is another way to access a property in an object. This is used when you have special characters in the property key and cannot use dot notation user.name.

For example, user["name"] is the same as user.name. In this case, you could write it either way.
However, in the example, the property key is "https://my-app.example.com/groups”, and so I can’t use dot notation (user.https://my-app.example.com/groups is not valid), and so I can access the property like this instead (user["https://my-app.example.com/groups"]).

The full user object will look something like this:

{
  "iss": "http://my-domain.auth0.com",
  "sub": "auth0|123456",
  "aud": "my_client_id",
  "exp": 1311281970,
  "iat": 1311280970,
  "name": "Jane Doe",
  "given_name": "Jane",
  "family_name": "Doe",
  "gender": "female",
  "birthdate": "0000-10-31",
  "email": "janedoe@example.com",
  "picture": "http://example.com/janedoe/me.jpg",
  "https://my-app.example.com/groups": "My Group"
}
1 Like

I’m finally back. I was back the morning after my 13-parter, actually, to see a beautifully thought-out and thorough response from @stephanie.chamblee which is why I’m able to be here as soon as I am, actually. Incredible. In fact, this is the first time I’ve ever actually asked for help from the internet, rather than google searching to find what I needed, and I am just awestruck by how amazing and helpful people are, and honestly I feel foolish it didn’t occur to me sooner to try this approach. Thank you so much.

All right. So I feel like I’m about halfway there to understanding how these action things work. I’ll tell you a bit about what I’ve been doing with them and then follow up with some questions. I’ll also label these ones starting with a letter “B” and also subdividing question numbers into lowercase letters where appropriate:

B1)

I’ve set up an action that looks like:

exports.onExecutePostLogin = async (event, api) => {
const namespace = ‘https://my-test-namespace.com’;
api.user.setAppMetadata(testParam, ‘foo’);
api.idToken.setCustomClaim(${namespace}/testParam, event.user.app_metadata.testParam);
};
I’ve also learned to use that “Try it out ->” hyperlink on the “Getting Started” page on the dashboard that lets you know whether you logged in or not, and then displays the data on the idtoken that would have been generated.
First time I log in with this setup I have an https://my-test-namespace.com/testParam = null;
Second time I log in I get the value https://my-test-namespace.com/testParam = foo; like I intended.

I’ve repeated this a few times and keep getting the same result where the new value is null until I log in a second time.

I thought maybe seperating the setAppMetadata function in one action, followed by a second action which contained the setCusomClaim function might work, but when I did that the value wouldn’t show up on the idToken at all.

What sort of thing is happenning so that I can’t set a new value and attach it to an idToken at the same time? and how would I actually manage doing that?

B2)

I also tried seperating the two halves into the first exports.onExecutePostLogin function, and the exports.onContinuePostLogin function that is by default commented out. It didn’t work. So what actually is that second function generated on each action? what’s it for?

B3)

I followed that guide to setup a machine-to-machine application for use with action, and I THINK I had it working. Anyway, though, it says you only need the machine-machine application for changing values that aren’t metadata. That would be like usernames or passwords and things right?

B4)

I don’t think I actually need a machine-machine application for what I’m doing, because the only values I need to access and modify are in the user and app metadata. Correct?

B5)

How does that machine to machine application thing work anyway? Since I’m using Angular2, I have a single page application, and I notice that’s a completely seperate option than machine to machine. Can you use machine-to-machine connection from a single page application?

B6)

Also, what is the relationship between the APIs and Applications on the dashboard Applications->APIs, the dashboard Applications->Applications and the “application” that I’m building in my visual studio code? Like, is “application” on the auth0 website a placeholder for my application? or are they both considered different applications that are working together? Why is there an option to create and hook up multiple applications and API’s from my dashboard if I can also create new tenants?

B7)

I know I had to use the client ID & domain etc to set up my app in visual studio code. On the machine-to-machine api tutorial it used those same values linking up the API with the Application and the action it attached to. Why don’t I need to attach those key and secret values to get most actions working?

B8)

Can I store values on app_metadata as arrays with variable length?
B9a)

So, I need users on my app to be able to log in to an account, but then while logged in create and edit values in their metadata. Does that process look like requesting a token: Call Your API Using the Authorization Code Flow with PKCE
and then putting actions into the machine to machine authentication flow? or do i not need actions necessarily?

B9b)
and if a user has already logged in is there some kind of shortcut I can take rather than go through the full step on that link? Do I still have some kind of usable token from login I could use or is that token not appropriate/not

B9c)
Actually, is all this the place where I would need a Machine to Machine application/api? Would I have to do anything special to connect that with my single page application beyond what I’ve already done ?

B9d)
So I’ve sort of made sense of the authorization code flow with pkce from that link. Is it that the process I need to alter metadata values is to request this pkce token and then any machine to machine flows I’ve made automatically run? does requesting the token with pkce method count as a machine to machine action? or do I need to do some different thing entirely?

B9e)
I know I’m not wording these questions here super great, so I’ll tell you what I’m trying to accomplish. I need it so a user can login and potentially create a group which the user will by default be in charge of, who can then invite other people to their group who can participate in the group with them, but are not themselves in charge of. Basically like Facebook groups more or less. What this means is that i can’t simply modify values during the login flow, I need to edit values at spontaneous times or whenever the user initiates them. FURTHERMORE, I need to capture data which the user themselves define, such as a group name value, and then somehow pass that value into the app_metadata. (I asked earlier if I could store arrays, because I imagine user’s potentially creating multiple groups, in which case i would effectively just list them in one array value). So basically, I want to know, what does the process of getting user-defined data from a form they fill out on my app all the way to storing that data somewhere in their user.app_metadata look like? What are all the key steps?

B10)

I know within actions I can check for the existence of values and things on the user profiles with if statements and for example break if a value is found. That happened on How can I use the Management API in Actions? this page you linked me. At the end the final code they have within their action reads:

if (event.stats.logins_count !== 1) {
return;
}

When I had that action built up like the tutorial and tried to log in it gave me an error and didn’t let me log in at all. If i commented out that if statement there then it would log in just fine. my login count was already higher than 1. Is that what’s supposed to happen? that I can’t log in at all unless my login count ==1 with that setup? I would have assumed that that if statement just ends the action itself. Yes/No? Did I make an error in my setup or is my understanding correct and returning within an action just cancels the login?

B11a)
Why do namespaces on the app and user metadata have to be URI style? especially if auth0 undestands those values to be, for example, user.app_metadata.favColor, why do I write them in as https://some-name.com/favColor???

B11b)
Can I store different values with same end location name but different http? as in can both https://some-name.com/favColor and https://some-other-name.com/favColor be on the same user app_metadata?

B12)
How does console.log() work within actions? I tried having a console.log(event), followed by a console.log(“Hello World”); and only the first displayed within the dashboard’s Monitoring->logs->successful login->Action Details->myAction logs.

All right. That’s all I’ve got this time. I hope it’s okay to ask here. I keep worrying, because I know like stackOverflow sorts of forums are big about keeping very on topic with quesions, and formally my responses have been very removed from my initial post, but I believe my questions are aligned with the spirit of what I was initially asking and what I’m trying to understand, and putting them in this one place made sense to me, so i hope that’s okay with the community.

And once again, Thank You so much. The auth0 community is just so amazing how they’re able to help me out like they do.

Okay, actually, I think I’m slowly answering all these questions myself.
Also, I maybe overstayed my welcome a bit asking ever granular and expansive questions, I did not mean to take advantage of @stephanie.chamblee’s good graces, so i apologize for that. I’d also like to give out one last huge thank you to stephanie, for really explaining a ton and pointing me in the right direction. That help made all the difference.

No worries! Just keep gradually working through those and you should work it out :slight_smile: Let us know if you have any other questions down the further road

Perfect! Glad to hear that!

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