How to Auto Populate the Current User after Login
In this guide, we're going to walk through how to fix a subtle bug that you may or may not have noticed as you built out this authentication system.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

Let's talk through what the bug is. So right here, if you type in a valid email. So mine's "reactnative@devcamp.com".

large

If you type that out and you click Login, you can see everything worked theoretically. So we got redirected to the correct screen, everything on that authentication side worked. However, the "CurrentUser" is null. So if I hit Refresh, then you'll see that the "CurrentUser"'s populated. So why exactly is that? Let's first walk through what the core issue is and then we'll walk through the solution. So when a user logs in, let's see what happens.

So if you go to the "AuthScreen", and you go to handleLogin right here, you can see that we are requesting the JWT token and if the user's authenticated, then we get it and we store it and then from there, we redirect the user.

However, nothing in here actually populates our current user. That occurs in our "AuthLoadingScreen", so right here, when we call logged_in, that is what is populating that "CurrentUser" and so that means we kind of have one scenario where have the user, and where the app might not know about the user and that's a problem. It may not be a problem on the FeedScreen because in cases where the user navigates to the FeedScreen, we're not passing up any current user details and we're not showing anything there to the user but what if they clicked on say the AccountScreen right here?

small

We eventually might want to put the user's email in there and in this case, we would just be getting an error because we don't have access to that. So let's see how we can fix that. Really the easiest way is to create a function that essentially mimics what's happening here in the "AuthLoadingScreen" and then to call that from our "CurrentUserProvider".

Now, we aren't going to be able to share that function because we have different behavior here. The reason why we're calling "logged_in" in this case is not only to get the "CurrentUser" data but then also to route the user to different spots of the application. In this case, we simply want to get the "CurrentUser". So we're going to be able to copy most of this but only keep about half of it. So I'm going to start off with that so I'm going to open the "AuthLoadingScreen" here and I'm just going to copy everything from api to the get, the then and the catch.

I'm going to copy all of that and then let's open up the "CurrentUserProvider" here and I'm going to create a new function.

So this new function here is going to be const getUser. It's not going to take in any params and then I'm going to paste in the code we had in our "AuthLoadingScreen".

AuthLoadingScreen

  const getUser = async () => {
    const token = await SecureStore.getItemAsync("memipedia_secure_token");

    api
      .get("logged_in", {
        headers: {
          Authorization: `Bearer ${token}`,
        },
      })
      .then((response) => {
        console.log("response from getUser", response.data);

        if (response.data.memipedia_user) {
          setCurrentUser(response.data.memipedia_user);
        } else {
          setCurrentUser(null);
        }
      })
      .catch((error) => {
        setCurrentUser(null);
      });
  };

Now, we have a few errors and that's perfectly fine. We have to finish still finish building this out and so the very first error is we need to import our API. So I'm going to import the API from '../utils/api'

import api from "../utils/api"

That'll take care of the first error. Then we need to make sure that we're getting the token. So in this case, we always know, any time we call getUser, we know we're going to have a token so we don't need a conditional there because the only way that this will be called is if the user's already in the system, which means they passed this test. So we can be confident that we have the token so I'm just going to copy this whole line right here and then inside of getUser, I'm going to paste that and that means we have to do a few more imports so we first have to bring in

so I'm going to say import * as SecureStore from ''expo-secure-store

import * as SecureStore from 'expo-secure-store'

That's the first part. Then we need to make sure, because we're using await, we need to make sure that we add async right up here and that's all working.

CurrentUserProvider

small

So now it looks like our only errors are related to the "props.navigation" which we do not care about. We want to get rid of those anyway. So I'm going to delete all of those calls and I'm going to also add some more debugging so I'm going to say response from "getUser" and that's everything that we need there and the only reason why we're storing this

small

and or the only reason we're storing "null" here is if the user, for some reason at some point, maybe there's a corruption with the JWT or something like that and so that's why we're just adding the conditional here to make sure that we do receive that user

Conditional Example

if (response.data.memipedia_user) {
          setCurrentUser(response.data.memipedia_user);
 } else {
          setCurrentUser(null);
 }

but this is pretty much always going to be the case.

CurrentUserProvider-current

small

So now we have all of those items in place and we need to now call "getUser" and pass that to our set of" stateValues". So essentially what we're doing here is we're registering this function as a function that other components can call because we need that because we need this to be called from our login component. So I'm going to hit Save here, and let's open up our "AuthScreen" and I'm going to say that we are going to be setting the token but then we also want to get the user before we navigate so let's come up here. We already are bringing in the "CurrentUser", now we can just import "getUser" from the "CurrentUserContext" and then back down here, right after we set that token, we're going to say get the user. Now let's test this out. So I'm going to log out of here and you can see down here for our debugging, we do not have a "CurrentUser", so we're all good there and I'll say "reactnative@devcamp.com" and pass in the password, hit Login and there you go.

small

You can see that we now have this working and we now have access to that user. So I'm really liking how all of this is coming along. We have fixed a little bit of an error there and before we finish up, I just have one more change and that is for the "registration" component. So I'm going to log out one more time. I'm going to come over to "Register" and let's see what happens there. We need to populate all this entire workflow we just built out. We need to populate that inside of the registration process. So right here, you can see we're handling registration. Right now we are getting the current user or we're getting the user that got sent back from the server and then we're just navigating to the feed. Notice, we're missing a couple steps. We're not setting the token and we are also not setting that "CurrentUser". So what we can do here is we can actually get rid of this entire code. We can get rid of all of that there

remove props.navigation
.then((response) => {
        console.log("Res for creating user", response.data);
        if (response.data.memipedia_user) {
          handleLogin();
        } else {
          alert(
            `Error creating account: ${formatErrors(response.data.errors)}`

and this is a nice little trick, we're just going to be able to say "handleLogin", and so how were we able to do this? Well, let's go up top and let's see. So up top here, you can see that we have handleLogin.

const handleLogin = () => {
    const params = {
      auth: {
        email: email,
        password: password,
      },
    };

We have the same params, remember, we're sharing params. So we're calling "handleLogin". We know we have access to the email and password. That's going to call, it's going to retrieve the token if it's valid. Then it's going to get the user and then it's going to navigate the user. So all we have to do is inside of our "registration", this step now has been migrated from actually focusing, instead of having it to worry about populating the user and redirecting and setting the token and doing all of those things, we're able to simply say okay, if the user is created, just log them in. So I'm going to hit Save here and let's test this out. So click "Register" and I'm going to say "somenewuser" and one little I do that whenever you're dealing with testing and you're doing validations, let's say that you have an issue like we have here, where the email has to be unique, one little trick I do is I just use the day's date. So I could say 4620 and then 1 and then if I test again

small

I could say "4622", that kind of thing and that way I have a nice and easy way of ensuring that I am always working with the unique email and I don't have to worry about typing a bunch of gibberish up there. That's just a little something that I've learned through the years and helps me. Okay, so I'm going to click "Register" and it looks like yes, everything there is working.

small

So that email is working, all of that is looking great. If you refresh, you'll see that that user and their token is in the system. So I am liking that. Very nice job if you went through that. We now have the full login and registration functionality built in to our application.

Resources

CurrentUserProvider

const getUser = async () => {
    const token = await SecureStore.getItemAsync("memipedia_secure_token");

    api
      .get("logged_in", {
        headers: {
          Authorization: `Bearer ${token}`,
        },
      })
      .then((response) => {
        console.log("response from getUser", response.data);

        if (response.data.memipedia_user) {
          setCurrentUser(response.data.memipedia_user);
        } else {
          setCurrentUser(null);
        }
      })
      .catch((error) => {
        setCurrentUser(null);
      });
  };