React Native: Oauth

If you want to use any self-respecting API on the web, you are going to need Oauth authentication. If you need an explanation what oauth is, there are plenty better ones. These are the steps your app needs to go through:

  • Send user to special URL in the browser that lets them log in to the service
  • Handle callback URL from this service
  • Exchange keys with the service to make sure callback was legit
  • Store the token.

Since I used Slack API from the slack npm package, I had the “Exchanging the keys” parts ready.

0. Preparing the app

You thought we were going to start from 1, didn’t you? Well, you you must be new to this “programming” stuff. 😃  On the more serious note, you need to register your app in the API provider to get special keys for the step 1 and fill in URL for the step 2.

For TeamParrot, I set up following OAuth redirect URL:

Zrzut ekranu 2016-10-30 o 16.29.31.png

1.Send a user to the special URL

You will need to use “Linking” library. DO NOT confuse Library called “Linking” with the process of “Linking libraries”, which is tying native libraries to JS code. I just send my users to 'https://slack.com/oauth/authorize?client_id=' + client_id + '&scope=channels:read%20channels:history%20users:read%20channels:write' and that opens web browser with appropriate “back” button.

2. Handling callback URL

If all goes good and your users give you appropriate permissions, they will be sent to the URL you specified in step 0. You may notice that this step has a weird protocol (team-parrot://). It actually can have any protocol you want that’s not used by an other app. This is enabled by “Deep linking” and “Intents” On android.

iOS

First, you need to configure your app to handle this specific link you want to capture. That tells the operating system to route those links to your beautiful and shiny app. As per usual, you have to do that in info.plist :

<string>com.piszek.teamparrot</string>
<key>CFBundleURLSchemes</key>
<array>
<string>team-parrot</string>
</array>

For a reason I completely do not understand, you also have to mess with a bit of Native code. More info in the docs

So, in file ios/SlackBird/AppDelegate.m :

@implementation AppDelegate
+- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
+  return [RCTLinkingManager application:application openURL:url sourceApplication:sourceApplication annotation:annotation];
+}

Android

Deep linking is called “intents” in Android and much of the same concept applies. You have to tell the OS that you want to open certain protocol inside your app. So:

In android/app/src/main/AndroidManifest.xml

<activity android:name=".MainActivity"
+        android:launchMode="singleTask"
...
>
+        <intent-filter>
+          <action android:name="android.intent.action.VIEW" />
+          <category android:name="android.intent.category.DEFAULT" />
+          <category android:name="android.intent.category.BROWSABLE" />
+          <data android:scheme="team-parrot"/>
+      </intent-filter>

OAuth walkthrough

Now you are ready to become a Oauth Sith and step to the dark side. This is how you oauth:

Linking.openURL( 'https://slack.com/oauth/authorize?client_id=' + client_id + '&scope=channels:read%20channels:history%20users:read%20channels:write' );
Linking.addEventListener( 'url', handleUrl );

And now your function “handleUrl” will be responsible for confirming “CODE” and exchanging it for a token. Usually, that will be handled by your API library of choice. Here is how I did it for TeamParrot:

import { client_id, client_secret } from '../config/slack';
import exchangeCode from 'slack/methods/oauth.access';
import testAuth from 'slack/methods/auth.test';
import { Linking } from 'react-native';

export function login() {
    return new Promise( ( resolve, reject ) => {
        Linking.openURL( 'https://slack.com/oauth/authorize?client_id=' + client_id + '&scope=channels:read%20channels:history%20users:read%20channels:write' );
        Linking.addEventListener( 'url', handleUrl );
        function handleUrl( event ) {
            const error = event.url.toString().match( /error=([^&]+)/ );
            const code = event.url.toString().match( /code=([^&]+)/ );
            if ( error ) {
                reject( error[ 1 ] );
            } else if ( code ) {
                exchangeCode( { client_id, client_secret, code: code[ 1 ] }, ( err, data ) => err ? reject( err ) : resolve( data ) );
                Linking.removeEventListener( 'url', handleUrl );
            } else {
                reject( 'params' );
            }
        }
    } );
}

export function testLogin( account ) {
    return new Promise( ( resolve, reject ) => testAuth( { token: account.access_token }, ( err, data ) => {
        if ( err || data.user_id !== account.user_id ) {
            reject( err, data );
        } else {
            resolve( Object.assign( {}, account, data ) );
        }
    } ) );
}

So there you have it. Now, you should be authenticated over Oauth2 with the slack API and able to make calls.

React Native Isomorphic app over the weekend

This article is part of my “React Native Isomorphic app over the weekend” series. It shares the problems I encountered during development of TeamParrot and Headstart Journal.

  • Oauth
  • Vector Icons – FontAwesome
  • CodePush
  • Renaming an App
  • Web & Isomorphic code development in Chrome devTools
  • Whole story

2 thoughts on “React Native: Oauth

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s