Add a Custom User ID with an Auth0 Rule

Ryan Chenkie: [0:00] The endpoints that give us data for things like the inventory area, are going to give us data based on the user's ID. You'll probably recall, if we take a look in our server JS file, so into server JS. Let's go to the endpoint that gives us back inventory data.

[0:16] Up here we've got our get endpoint going to /inventory. We're looking for inventory items that belong to a particular user. Up until now, that user has been coming in on this subclaim from the user object.

[0:28] The issue we've got right now is that the subclaim as it currently stands is going to be coming from Auth0 and not from our own database. We take a look here at this user object in the Auth debugger, we've got a subclaim. This is pointing now to a new ID that we haven't seen before. This ID is coming from Auth0, and this is the ID belonging to our user in Auth0 as they store it in their database.

[0:52] Since our inventory items have a user ID that is from our own database, these aren't going to map together, so we're going to fix that up in one of two ways. First though, we should change things over in our applications, so that we aren't able to get to this inventory route if we're not an admin. Let's undo what we previously did, where we put in authenticated route here. Let's put in instead admin route and we'll apply it on the other side.

[1:21] Let's also do a check for whether the user is an admin, right here in the admin route component. Up until now, we haven't been enforcing that admin check, we've just been looking for an authenticated user.

[1:33] To do that we can get the user from use Auth0, so we'll say give us the user. Like we've done elsewhere, we can give ourselves a constant of roles and that's going to be the user at the JWT namespace, which comes in on process.env.reactapp JWT namespace, and then, we're looking for our roles.

[1:54] Then, here we can give ourselves this constant called isAdmin. That's going to be set to true based whether the user is an admin or not. For that we can say if the roles array at index0=admin, then that will be true, otherwise it will be false.

[2:10] We could wrap this up into some function in our own AuthContext, but since we're just using it here for now, I think this is OK. This route is going to render the children if the user is authenticated and if the user is an admin, so let's save that. Now, when we try to go to inventory, we get kicked back over to the home screen.

[2:29] Let's go back to Auth0 and we'll change up our role. Over in Auth0, we can come to our profile and we can assign a role. Let's assign a role of admin, we'll assign that, and then, let's delete the user role. Let's go back to the application now, and we will refresh. Now, let's go to the dashboard. We've now got all of our sidebar items back and we can get over to inventory.

[2:55] Now, we are getting this 400 coming from inventory when we try to make request to it. That's because right now the endpoint that we're getting inventory data from, is taking in the user's subclaim, so let's go back and we'll look at that subclaim here. Right now, that is this ID here.

[3:10] It happens to be a Mongo ID on this part of it, but then it's preceded by Auth0, which is an internal identifier for Auth0 to manage user IDs. The reason they have this is that other ID providers like maybe Google or Facebook might also be used to precede user IDs.

[3:27] This error that we're getting is a cast error. It's saying that in the query that we're making in our inventory endpoints, so we'll go back to server and check it out here, that value cannot be cast to be an object ID. Our Mongos model knows that when we go to search for an inventory item based on a user, since this user is set up to be an object ID, it's not able to run this query.

[3:49] At this point, we've got two options as to how this might work. The first option is that instead of storing our data with the user IDs that we currently have, we can instead store them with this Auth0 ID.

[4:02] If you're building your application from scratch, this is probably a good way to go. You would have users sign up with an Auth0 account. You would then be able to get their IDs back and you can use these values in your own database to track user's data.

[4:16] Another option that you might go with, especially if you're migrating to Auth0, is you might want to store the existing user IDs for your user on their new profiles in their app metadata. If we come to this user profile and we go to details, remember that we have two spots for data here. We've got user metadata and app metadata.

[4:37] This section here, app metadata, that's a good place for us to put the existing user ID. Then, what we can do is we can include that user ID in the user's access token as a custom claim. We can be in our rules section here, if we go to that rule that we were previously working on where we augment a user, we can work in here to add that user ID.

[4:58] Let's start by adding the user ID to our user's profile in their app metadata. You'll need to go get the user ID from your database. I've logged in to my MongoDB account, and I went looking for my user account object and I copied it over. We can come in here, and you can put this on any key you like, maybe we can just call it orbits ID.

[5:19] Once that's in place we can save it. Let's come now to the Auth pipeline into rules again, and into our augment user rule. We're modifying our ID token, so that we can get our roles array and the user profile. Now, what we can do is say context.accesstoken, add our namespace plus sub is going to equal user.orbitID.

[5:45] When we work within rules, anything that we've got on our app metadata gets placed here on this user object. Once again, we're using a custom namespace so that we avoid any collisions with JWT claims. Let's save this now, and let's see if we can get this new custom subclaim coming out on our back-end.

[6:04] Back over to server JS, let's start by including that namespace over here in our environment file. Right below Auth0 issuer, let's say Auth0 JWT namespace, that's going to equal https app.orbit, let's save that.

[6:21] Let's come now to server JS. Let's start by seeing if we can get that user ID. Let's give ourselves a new constant of user ID and that's going to be equal to request.user at our process.ENV.Auth0JWTnamespace, then it will say /sub. For now, let's log that out, console.log user ID. We can save this now and we can go back to our application to refresh.

[6:48] Now, we can open up the console here. What we've got is our orbit ID. To get rid of this 400 errors, we can just do this. Let's stick in user=userID. If we refresh again, we've got a good result coming from inventory and we've got our inventory items.

[7:05] We'll need to apply this in any of the endpoints we've got that are looking for user ID, so when we wrap this up into a function. Maybe let's go up here, just above all of our endpoints, we can get rid of require admins since we're not using that anymore. Let's give ourselves a new function, we'll say it's called getuserID, that's going to take a user objects and we'll just get back the user ID.

[7:27] For that we just return user at our JWT namespace, process.ENV.Auth0JWTnamespace/sub. Let's replace our inventory call with that function, and we'll see if it works. We'll get rid of this user here and user ID is now going to be equal to get user ID, passing in request.user. Let's make a request from our front-end and here we go with our data.

[7:58] Let's quickly apply this to all the endpoints that need it. We can copy this line and come down here to post an inventory item, so user ID is going to be equal to that function call. Then, down here when we delete, similar thing we can get a user ID and we'll pass in user ID here. Then, down below when we get users, we are looking for all users, so that's fine like is.

[8:22] Our user bio we can do the same thing, we'll replace sub here with user ID. When we patch a user, same thing here, we'll replace sub with user ID. It looks like we're good to go for the rest of them.

[8:35] With this method, we get to see how we're able to work in an Auth0 rule to use some existing data. That existing data is the user ID that currently is existing in our own database.

[8:46] If we want to continue to use that in our application, then we can just put it down here in the user's profile, and because Auth0 rules give us a dynamic way to get information from our user profile and then augment the authentication pipeline, we're able to use this easily within our app.