2017-01-19

This post was written by the team at Rangle.io. You can leverage their expertise with custom React and JavaScript training for you or your team. Learn more about creating React Native applications with some further reading on Rangle’s blog, which covers topics like common tooling and workflow, writing your own libraries, and more.

Welcome back to Part Three of our four-part series on building a React Native chat app, chock-full of interactive features like chat history and user online/offline detection. However, so far, our app is open to everyone, running on a public channel. We’ll change that with this tutorial.

In this tutorial, we’ll add a login feature that includes user permissions, securing the app and allowing chat admins and app owners to set and change user’s permissions within the app.

We recommend starting with Part One (creation and configuration of a basic React native app), then moving onto Part Two (adding chat functionality and features), before starting this tutorial. However, if you’re interested in OAuth and user access management, this is the post for you!

Setting up Access Manager with OAuth and GitHub

To add the login feature, we’ll use PubNub Access Manager and GitHub’s implementation of OAuth. We will use the popular Node.js middleware passport to set up our OAuth authentication flow.

Git Repository

To code along with this post, you can start from rev1 (where Part Two left off):

git checkout rev1

To work with the finished code, go to rev2:

git checkout rev2

Configuration

First, we’ll update our root config.js with keys we need for our server. Ideally, we would pull these from a configuration file of some sort, or perhaps discover them from a network service – but in the interest of brevity, we will hardcode these values in our config.js file.

In a real production application, it would make sense to put all of these properties in a configuration file of some sort.

The iOS and Android simulators use different IP addresses to refer to the host machine, so we have to provide a separate host and set of GitHub OAuth keys for each platform. We will need to specify the platform we’re simulating when we run our server.

There is also an authKey provided by us (meaning it could be any string), and a secretKey provided by our PubNub account. We’ve provided our own PubNub and OAuth keys here for convenience, but if you would like to generate your own set of keys check out the instructions on the GitHub README.

We will also create a constants/config.js. This is for configuration specific to our React Native code:

Again, our host is dependent on the platform we’re simulating, so we check the platform and specify the right host using React Native’s Platform module. We reference our root configuration file for values.

Setting Up the Server

Alright, let’s setup our server. By the end of this section we should have a NodeJS application that handles the following:

GitHub OAuth authentication

Creating sessions for logged in users

Redirection upon successful/failed authentication

Requests for the user’s GitHub info

PAM permissions at both application level and channel level

In our root directory, we’ll create a server/ folder with an index.js. At the very top of the file we’ll require babel-register so that our file can leverage ES6 constructs:

Next, we import our server-side dependencies and configuration. Then we initialize node-persist and express with passport:

Just to briefly describe each dependency:

express is a popular NodeJS framework that lets us set up middleware and routes without headaches.

passport by itself is a generic middleware for handling authentication. Our server will use it specifically to handle GitHub OAuth and user sessions.

passport-github is the module we plug into passport to handle GitHub OAuth.

The PubNub SDK here is the same one we’re importing in our React Native application, but we use it differently on the server.

node-persist is essentially localStorage on the server.

Since our simulator host is platform specific, we’ll allow the user to specify the platform through the command line, which will then be used to assign the right configuration values:

You can try testing the above from your command line:

npm run serve android (silent…)

npm run serve ios (silent…)

npm run serve (this one should throw the error)

Next, we’ll initialize PubNub then grant the instance admin permissions using PAM:

We initialize PubNub with the subscribeKey, publishKey, authKey, and secretKey from our configuration, then store the instance in pubnubHandle. Since we’ve provided a secretKey, our instance is able to grant and revoke permissions to our app — all messages to the PubNub network will be signed with the secretKey. The authKey is used on all requests when Access Manager is enabled.

Since PubNub’s grant function is callback-based by default, we’ve wrapped it in a helper function called executeGrant for the convenience of resolving grant in a promise.

We then call executeGrant with grantOptions to ask Access Manager to give our PubNub instance application-level privileges, meaning permissions that apply to all channels associated with our subscribeKey. By setting the manage, read, and write arguments to true, we’ve granted our PubNub instance permission to add and remove channels in any channel group as well as publish to them. Setting ttl to 0 applies the grant indefinitely.

To request channel-level privileges instead of application-level privileges, you would provide a list of channel names to a channels parameter in your options, and then PAM would grant permissions only to those channels. We didn’t provide a channels parameter in grantOptions, so PAM defaults to applying the grant to all channels associated with our subscribeKey. We’ll see an example of channel-level privileges later on. You can find more information about the grant function in the API reference.

Now let’s configure our passport session with our GitHub OAuth strategy. The following code facilitates the process of authenticating the user through GitHub OAuth, and then serializing the user to the passport session:

In case you didn’t know, when you authorize an application on GitHub to use OAuth, you have to provide a callback URL to redirect the user to upon authentication.

We instantiate the passport middleware GithubStrategy with said callbackURL and the right clientID and clientSecret. As indicated in our setup so far, those values will vary depending on the platform we’re simulating for. If the values are correct, the middleware will give our server permission to authenticate users through GitHub OAuth for our registered application.

We also provide a callback function to handle user data upon successful authentication. The callback just assigns the accessToken to the user object which is then passed on. Later on, we will use the accessToken as an Access Manager auth key as well as a query parameter for fetching the user’s data.

Our passport serialization is setup to cache the authenticated user’s data in the node-persist store we initialized earlier. The user’s ID is serialized to the session, and the id is used to retrieve the user’s data from the store upon deserialization.

With the passport and GitHub OAuth logic set up, we’re ready to actually use them in our routes:

Once we update our React Native app to use our server, the /login route will be responsible for directing the user to the GitHub login page on the device’s browser. Once authenticated, the user is granted channel-level privileges from PAM to our default ReactChat channel. The user’s accessToken can now be used as an authorization key for reading and writing to the channel. We will use it to initialize PubNub when we update the React Native app.

Our /callback route handles what happens after GitHub redirects the user to the registered callback url upon an authentication attempt. If the attempt was unsuccessful, the user is redirected back to the login page. If the attempt is successful, the user is redirected to our React Native app with the access token. For convenience, we’ve already gone through the trouble of registering ‘reactchat://’ as the url for our application. If you’re curious about how this is done, check out React Native’s documentation on Linking.

As a side note, when we hit the /login route with an existing passport session, we’re immediately prompted to be redirected back to the app without having to login again. You’ll be able to see this for yourself once the feature is built.

Next, we will need to provide a route for fetching the user’s GitHub data:

We use the user’s access token to find the user’s data in our node-persist store and return just the information we need for the React Native app using a helper function called formatUser. We will call this chunk of information a user object throughout the remainder of this post. The user object contains the following:

login

the GitHub display name of the user

id

the user’s GitHub unique ID. We will use this as the uuid when initializing PubNub on the client.

avatarUrl

a link to the user’s GitHub profile image. This is essentially replacing the RoboHash icons we had in the previous blog post.

Finally, we need the server to listen to our port:

Let’s add an npm script to our package.json for starting the server:

Try running the server from the command line. Make sure to specify a platform:

npm run serve ios

Updating the React Native Application Itself

Now we can build out a login user flow with the help of our server. By the end of this section we’ll have the following:







1. Home screen with button.

2. GitHub login page.

3. Prompt to return to app upon successful login.

4. Our chat display from before but with our GitHub avatar and username

We need two new services. The first is services/authentication.js. We’ll use it to strip the user’s access token from the redirect URL we’ve set up earlier in our server’s /callback route:

We also need an API service to fetch the user data from our /user route. React Native already provides an implementation of fetch so we don’t have to import isomorphic-fetch as we did for the server:

Next, we’ll have to make some slight adjustments to services/pubnub.js to accommodate our server’s PAM setup. Let’s look at the connect function first.

Red means deleted, and green means added (like a GitHub diff):

We no longer generate the PubNub uuid on the fly. Instead, we pass in the user’s GitHub ID when we call the connect function. We also need to pass in the user’s access token as an authenticationToken so that the user’s PubNub instance has permission to read and write to the ReactChat channel. That’s the permission we set up in our /login route earlier.

And a slight change to the publishTypingState function:

Since we’ll be using the user’s GitHub info returned by our /user route, we’ll no longer be relying on RoboHash to provide our user images. So, we have to send the entire user object when we publish messages so that other users can see the messaging user’s avatar and username.

We have to update some of our Redux too. Our reducers/conversation.js is now:

This is also to accommodate the transition from user id to user object.

We also have to update actions/connection.js to use our api service and the updated pubnub service:

Our connect function now takes in a user access token and uses it to fetch the user’s GitHub information before calling our pubnub service’s connect function. As we’ve seen earlier, we’re not generating a random uuid in connect anymore so we pass in the user’s GitHub id from the response, and our dispatch of CONNECTED takes a user object instead of an id.

We have a few updates to make on the UI side of things too, but first let’s setup the login flow. Create a login screen called components/ChatLogin.js:

This is essentially a centered button on a white background that takes up the entire screen. We give an onPress handler to TouchableOpacity.

We’ll import this component into our root directory’s container.js and have it render when our ConnectionState is idle. We also need to import React Native’s Linking module:

This will display the login screen when we first load the app. We have passed as props an onLogin function that essentially replaces what we had as componentDidMount:

So now, instead of immediately connecting to PubNub when we load the app, we assign a function to a login button that handles the following when invoked:

Redirects the user to the GitHub login page for OAuth authentication. This is a jump from the React Native application to a web address in the mobile device’s browser.

Adds an an event listener that waits for the redirect from our server’s /callback route back to our React Native app upon successful authentication. The listener strips the user’s access token from the redirect url using our new AuthenticationService and then calls our updated connect action to fetch the user’s GitHub data and initialize PubNub.

Now we just have to update a few components to complete the transition from user id to user object.

components/User.js takes an avatar url instead of an id:

In components/ChatInput.js we have to send the whole user object upon message submission (as we’ve seen earlier in the publishTypingState function of our updated pubnub service):

When we render messages in components/ChatHistory.js, data.Who is now a user object:

components/ChatUsersTyping.js which indicates which users are typing, also needs to make the switch:

And consequently, components/Conversation.js needs to pass in the entire user object when firing startTyping and stopTyping:

Once all these changes are in place, your React App should have the complete login flow (fingers crossed). Try running the app by executing these two commands on separate terminal tabs:

npm run serve ios

react-native run-ios

In the next post, and final tutorial of the series, we will build upon the existing server to create direct conversation channels between the user and the user’s GitHub friends.

The post OAuth and Access Management for React Native Apps (Part III) appeared first on PubNub.

Show more