User Name and Password Login Using Auth0 in an Expo.io + React Native Application

Part 5 of Implementing an Authentication Flow in React Native

Note: For some reason, Medium’s default font displays the number zero as “0” which prevents me from using the proper title of the Auth0 (Auth zero) Authentication as a Service provider. At least, it does so on updated Chrome and Edge. So throughout this article, please read Auth0 as “Auth zero”. Thanks. Also, if anybody knows a work-around for this problem, please post it in the comments section.

Introduction

This article is part 5 in a series on adding authentication to a react-native with redux application built in Expo.io that communicates to a server created using AWS API Gateway and Lambda functions.

In the previous article , I discussed how to cache social login information so that the user wouldn’t have have log in each time they ran the application.

This article will present the final feature addition for this project; user name and password authentication. User/pass auth is a bit more involved then the social logins implemented previously. Not only do you have to retrieve the username and password from the user, you have to provide the ability for the user to change their password, retrieve forgotten passwords and usernames, and maintain and update a profile. On top of all that, you also have to do so in a secure manner. To mitigate that heavy lifting, I will be using Auth0; an authentication as a service provider. It provides an easy to use API that provides access to those needed functions.

The code referred to in this article can be found at the GitHub repo here.

Note: for this article, I will be using the term username for the name that the user logs in with. This is often the user’s email address. In fact Auth0 defaults to using email addresses as the username. However, for consistency (and my own sanity) I’ll attempt to stick to username.

Design

On the client, we’ll need to receive the user name and password from the user, and at a minimum give them the ability to create an account, and log in to said account. The backend server will require the ability receive Auth0 token information from the client, and authenticate that token information with Auth0.

Also, since Auth0 is a service, we’ll have to sign up for it. It’s easy to do and can be used for free for some large (but not too large, there’s no infinite free chicken here) number of accounts. It’s definitely enough for this project.

Client Application Implementation

The big UI additions here are a couple of input boxes to receive the user name and password, and buttons to enable the user to create a new account, and sign in to an existing account, and retrieve a forgotten password. I won’t be implementing the retrieve password functionality in this article, because once you see how easy it is to work with Auth0, you won’t need me to.

Auth0 provides a JavaScript API, but its just as easy to work with the HTTP API documented here. Working with the HTTP API allows me to continue the “fetch then do redux-saga” pattern established in the previous posts. It also lets me customize the login experience so that I don’t have to use Auth0’s Lock log in screen.

Just like the social logins explained in my previous post, a successful Auth0 login returns an access token. Additionally, it returns an id token.

There are a couple of flows to implement using redux-saga. One flow supports actions after the user successfully signs in. The other exists to perform actions subsequent to account creation.

Auth0 doesn’t immediately provide profile information upon sign in; obtaining a user’s profile requires another call to the Auth0 API. So after receiving a SIGN_IN_AUTH0_USER_FULFILLED action upon successful sign in, I use redux-saga to dispatch a GET_AUTH0_USER_INFO action that performs another fetch to retrieve the user’s profile data .

Similarly, creating a new account does not automatically log the user in, so I created another saga. This saga logs the user in after sign up. And as stated in the previous paragraph, a successful sign in results in a call the retrieve the user’s profile.

Also, as mentioned in the previous posts, I normalized the user and profile data so that all the information I need such as access tokens, profile picture URLs, token expiration dates, can be accessed consistently irregardless of the login service. This data normalization is done in the reducer after each successful sign in with the Auth0 API. The token needed for authentication is the biggest difference when using Auth0 vice Facebook or Google logins. Auth0 authentication requires a JSON Web Token (JWT) which is provided to us in the id_token property of the sing in response. It is this id_token that we need to pass to our custom authorizer for authentication in order to perform authorization. I chose to save the ID token value as the accessToken in my normalized data. Keeping the same name keeps the rest of my code simpler at the expense of some readability. Another option would be to save both the access token and id token received from the login services, and use conditional statements to look for either accessToken or idToken when needed.

Similar code patterns can be used to enable the user to change their password .

Server Implementation

As previously state, an addition must be made to the custom authorizer Lambda that performs authentication & authorization. We can build off of the verification code previously implemented for the social logins. I added the code below to the custom authorizer to verify the token that it receives. The code is called using a case in the switch statement that selects the correct authorization scheme based on the login provider.

// built using combination of https://auth0.com/docs/tokens/access-token                                 
// and https://www.npmjs.com/package/jsonwebtoken
let verifyAuth0Token = (auth, callback, event) => { console.log("^^^^ Inside verifyAuth0Token Token ^^^^^^^^^^");
console.log(util.inspect(auth, { showHidden: true, depth: null }));
var decoded = jwt.verify(auth.accessToken, auth0ClientSecret, {
audience: auth0ClientID,
issuer:`https://${auth0Domain}/`,
subject: auth.id
}, function (err, decoded){
if (err) { // token is not authentic
console.log(util.inspect(err, { showHidden: true, depth: null }));
callback(null, generatePolicy('user', 'Deny', event.methodArn));
}
else {
console.log(util.inspect(decoded, { showHidden: true, depth: null }));
callback(null, generatePolicy('user', 'Allow', event.methodArn));
}
});
}

I used the jsonwebtoken library to parse and verify the token. The library has a very easy to use verification function that you pass various known values into. Since I know my Auth0 client ID for this application, along with issuer and the user’s ID, I was used jsonwebtoken to decode the passed JWT and compare the decoded to expected values. The token is considered valid if they match, and invalid if not. It is also considered invalid if it is expired. Once again, the generatePolicy function creates a policy that either authorizes or denies the user access to the Lambda functions that respond to HTTP requests.

Conclusion

Hopefully, this post shows how easy it is to add user/pass authentication and authorization to an existing application. The process is even easier if you use an authentication as a service provider like Auth .

Final Thoughts

This is the last post in this series of articles. I hope you found them useful, and would love to hear whether you did (or didn’t). I would especially be grateful to learn of any mistakes or omissions I made in the process of creating authentication for my application.

Reginald Johnson has maintained his passion for coding throughout his 20+ year career as an Officer in the United States Navy. He enjoys applying his training and experience in programming, Systems Engineering, and Operational Planning towards programming. Follow him Twitter @reginald3.