How exactly does user_id work and what is the difference between the top level user_id and the ones in identities?

I’m integrating Auth0 into my application and I’ll use Todos as a simple example.

Say they look like this:

Todo {
    Title string
    UserID string

What field from Auth0 do I use as the user ID (user that owns this Todo) to connect a user to their content (Todos in this simple case)?

After authenticating a token on my server (Go) I get the sub field in the claims that looks like this:


And noticed that is a combination of provider|user_id of the first login identity:

    "created_at": "2020-02-13T20:30:47.141Z",
    "email": "<user_id>",
    "email_verified": true,
    "family_name": "Holland",
    "given_name": "Zac",
    "identities": [
            "provider": "google-oauth2",
            "user_id": "<omit>",
            "connection": "google-oauth2",
            "isSocial": true
    <omitted irrelevant fields>

What is that googl-oath2 prefix in the account level ?
What if the user adds a new identity? Will this change the prefix of user_id?
Is the account level user_id set as the provider | user_id of their first login? Meaning it never changes even if the user adds more identities?

Ultimately: Is it safe to use the sub field as the user id with the prefex, or do I strip that prefix?

Hi @zacharyholland,

Welcome to the Community!

You have it right: The prefix on the root user_id is the provider which provided the details used to construct the original user profile. The component after the | is the user’s unique identifier within that provider. In this case google-oauth2 means a profile that was constructed from a Google Social login user.

The identities array allow you to link other identities / logins to the same user profile.

Hey @markd , thanks! Glad to be here.

So when storing user ids in my database, is it best practice to strip that prefix or keep it there?

To be honest, and this is just a personal preference, I would create my own uuid. If I was going to use the user_id, I think I would keep the prefix. The chance of a collision if you remove the prefix is probably zero, but I would be more comfortable leaving the prefix… just in case!

I would love to generate an id for the users… but this would add mean I need to make another api call when authenticating a user right? I’m using cloud functions so would like to reduce the amount of time spent authenticating as much as possible.

If I understand correctly, I would get the JWT, authenticate with Auth0, then also have to make a request to get the user profile to get the metadata correct?

Also, is that a function that I need to run or that’s something I can have auth0 run every time a user signs up?

The code I linked to can be run in Auth0 as a Rule. Rules are javascript snippets that run (only!) when a user logs in. Alternatively you could use a hook to run the same code during user registration.

You can query the /userinfo endpoint for user information, but you can also just include arbitrary data from the user’s profile in your ID and / or access tokens by writing a rule that handles that. For example, the following rule adds the uuid, created by the previous rule, to the ID token:

This diagram may be helpful as well:


Wow this is amazing thank you so much! I’m going to test this out now.

By the way, do you know what the pricing situation is with rules? Just want to be sure I don’t accidentally rack up a bill but it looks to me like there’s no extra charge. I guess this is because it only runs once?

Billing is based primarily on “monthly active users”, plus additional charges for some special features. Rules don’t cost anything extra as far as I know.

Hmm I’m not seeing the user’s app_data in the access token. Should it be in the claims? This is how I’m currently looking for the info:

type customClaims struct {
	UserID string `json:"https://mattribution/claims/uuid"`

func (h *Handler) newJwtMiddleware() func(http.Handler) http.Handler {
	return jwtmiddleware.New(jwtmiddleware.Options{
		authHeaderParts := strings.Split(r.Header.Get("Authorization"), " ")
		tokenString := authHeaderParts[1]

		token, _ := jwt.ParseWithClaims(tokenString, &customClaims{}, func(token *jwt.Token) (interface{}, error) {
			cert, err := getPemCert(token, h.auth0Domain)
			if err != nil {
				return nil, err
			result, _ := jwt.ParseRSAPublicKeyFromPEM([]byte(cert))
			return result, nil

		log.Printf("%+v", token.Claims)

But get nothing for UserID

Claims: &{UserID: StandardClaims:{Audience: ExpiresAt:1582426590 Id: IssuedAt:1582340190 Issuer: NotBefore:0 Subject:google-oauth2|100159157093560652991}}

Is the issue that I’m looking at the Access Token here, when I should be looking at the ID Token?

Sorry for the basic questions, I’m learning and really appreciate the help.

I’ve been following this tutorial

If you are using the example code above for a Rule, I am adding the profile attribute user.user_metadata.uuid*** to the ID token only:

  context.idToken[namespace + "uuid"] = user.user_metadata.uuid;

You can also add additional profile attributes (“custom claims”) to your access token. Whether you add data to your ID token and / or access token depends on your use case. The ID token is for your app (where the user is logging in) while the access token is for the API.

***this is actually an error in my code. It should reference app_metadata, not user_metadata.

Edit: Fixed.

haha I noticed that, forgot to mention it but wasn’t too much of a problem!

So I need to send the claims from my web client (who is authenticated with auth0) to the api in the access token as a claim?

Would this fall under changing the scope when the web client authenticates?
Right now this is how I get and send the access token… I can’t find any docs on adding fields from the profile…
const { accessToken } = await tokenCache.getAccessToken();

I see a way to add scopes but that doesn’t help does it?

Edit: I can confirm my web client has access to the user id in the id_token

    https://<>/uuid: "<uuid>"
    given_name: "Zac"

I just added this to my rule to this and it works! But is this the right way to do it?

context.accessToken[namespace + "uuid"] = user.app_metadata.uuid;

Scopes are data you can request when you go through authentication / authorization. Some scopes are defined by the underlying standards, others are custom that you create. Anything you add to a token via a Rule should be available to you no matter which scopes you request.

This is the basic model you need to set up:

Depends on what you mean by “right way to do it”. Auth0 gives you a lot of flexibility … which is great, but also potentially challenging. You can structure the contents of your user_metadata and app_metadata in whatever way suits your purposes. Certainly that specific line of code is a very typical example of adding a custom claims. You can also choose to structure your namespace however you like. It just needs to follow the URL format and be unique (and not use any of Auth0’s domains). I like having /claims on the end, but that is not required.

I guess I should say is this a good practice which it sounds like it is! I just want to make sure I’m not supposed to be sending it from the client rather than having Auth0 rules handle it.

I chose to use my API identifier as a my namespace. Does that sound good or could that create problems in the future?

100%, let Auth0 do the work where it can. Rules** can do a lot more than just injecting data into your tokens, but injecting data into your tokens is a very common user case.

I’m not sure if this matters, or if it is a best practice. I would use a single unique namespace for all your custom claims. E.g., If you own, I would use something like:

But it may be that using your API identifier is fine too. I like having one namespace, but in my case I have a lot of developers creating custom claims, so a single namespace helps keep it all straight.

**Rules are powerful, as they allow you to inject arbitrary javascript into the authn flow. “Anything you can do in js, you can do in a Rule.” Not exactly true, but close enough.

1 Like

Awesome, great advice.

Thank you so much for all the help!!

1 Like

Happy to help, and good luck!

1 Like

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