Search code examples
react-nativelyft-api

Redirecting Back to App After User Accepts Permissions Oauth2


Background

I am using the Lyft API to authenticate users with their 3 leg Oauth2 flow. I have added Deep Linking to my app by following this documentation here.

When my app opens it immediately loads the Lyft Authentication page in Safari. After they go through the process to accept the permissions I have requested Safari tries to redirect to the URL I have set inside of my developer account at Lyft Developer

The problem here is I need the user to come back to my app with the response that Lyft gives when a user grants my application permissions.

What I have Tried

Deep Link

lyftauth://

I can type that link into Safari and it opens my app when I am on the phone, and if the app is installed. I tried to add that link as the redirect URL on the Lyft Developers page but it does not accept that url format.

So I know for sure I have to give the developers account page a URL to redirect, I know it will try to redirect to that URL and I know I cant use the correct URL to get my app to open back up.

React Native Oauth Libraries

I tried using the library react-native-oauth. When using this library I found that it does not work as expected. Many issues are opened on githu.com and a lot of them do not even have a response. I tried to for the library and edit the code to work for me but no matter what I ended up with a method being called on an object that did not exist. Specifically a method called authorize.

Native Xcode Application

I built out a Xcode project using Swift and installing the Lyft SDK with cocoapods. I was able to work with the API using this SDK. I was unable to incorporate React Native to the existing Swift project due to constant missing dependencies.

Code Examples

Using the docs for Linking mentioned above I added this code to my App Delegate file,

AppDelegate.m

#import "AppDelegate.h"

#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTLinkingManager.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
  return [RCTLinkingManager application:application openURL:url options:options];
}

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity
 restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler
{
  return [RCTLinkingManager application:application
                   continueUserActivity:userActivity
                     restorationHandler:restorationHandler];
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSURL *jsCodeLocation;

  jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];

  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"Lyft"
                                               initialProperties:nil
                                                   launchOptions:launchOptions];
  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  return YES;
}

@end

Then I went into the info.plist and added a link to open the app by,

enter image description here

I am importing Linking to my React Native Component,

import React, { Component } from 'react';
import {
  Linking,
} from 'react-native';

I added the code from the React Native Documentation to the React Native Component,

const url = 'https://api.lyft.com/oauth/authorize?client_id=PbUe5NjrXqQP&scope=public%20profile%20rides.read%20rides.request%20offline&state=true&response_type=code'

class App extends Component {
      constructor(props) {
        super(props);
        this.state = {

        }
        this._handleOpenURL = this._handleOpenURL.bind(this);
      }

      componentDidMount() {
        Linking.openURL(url);
        Linking.addEventListener('url', this._handleOpenURL);
      }

      componentWillUnmount() {
        Linking.removeEventListener('url', this._handleOpenURL);
      }

      _handleOpenURL(event) {
        console.log(event.url);
        console.log('WE ARE TRYING TO CALL THIS FUNCTION AT THIS POINT');
      }

      render() {
        return (
          ...
        );
      }

}
export default App;

Question

What is the most common way to handle the Oauth2 redirect back to your application using React Native when the API you are using does not specifically handle it for you?


Solution

  • Since this was a very hard problem to overcome and this question has not received a lot of attention, I suspect someone else in the future would appreciate an example of how I overcame this.

    Problem

    Handling the redirect after a user accepts permissions using the Lyft API 3 leg Oauth flow.

    Solution

    Example Solution Repo Here

    In order to handle this I used Deep Linking which is supported by React Native. I also had to setup links in the IOS and Android applications. These links needed to be the same as the redirect URL in the Lyft Developers Page so the app could be opened back up when the link was fired off on the mobile device with the application on it. This can be done as followed,

    1. Setup Deep Linking. Instructions on how to add Linking to your app can be found here.

    2. The URLS are not explained at that React Native Link. Here are the resources for the Deep Links for each OS. Apple / Android

    3. You will have to add a redirect URL to your Lyft App in the Developers page. This URL will be finessed into the native app settings for each OS, (IOS & ANDROID). You will make that the redirect URL in the Lyft Developer App Page here.

    Code Example

    Android

    AndroidManifest.xml

    <intent-filter android:label="lyft-app-authorize">
      <action android:name="android.intent.action.VIEW" />
      <category android:name="android.intent.category.DEFAULT" />
      <category android:name="android.intent.category.BROWSABLE" />
      <data android:scheme="http"
            android:host="lyft-app"
            android:pathPrefix="/authorize" />
    </intent-filter>
    <intent-filter android:label="lyft-app-authorize">
      <action android:name="android.intent.action.VIEW" />
      <category android:name="android.intent.category.DEFAULT" />
      <category android:name="android.intent.category.BROWSABLE" />
      <data android:scheme="lyft-app"
            android:host="authorize" />
     </intent-filter>
    

    IOS

    info.plist

    <key>CFBundleURLTypes</key>
    <array>
      <dict>
        <key>CFBundleURLSchemes</key>
        <array>
          <string>lyft-app</string>
        </array>
      </dict>
    </array>
    

    AppDelegate.swift

    - (BOOL)application:(UIApplication *)application
                openURL:(NSURL *)url
                options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
    {
      return [RCTLinkingManager application:application openURL:url options:options];
    }
    
    - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity
     restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler
    {
      return [RCTLinkingManager application:application
                       continueUserActivity:userActivity
                         restorationHandler:restorationHandler];
    }
    

    Lyft Developer App Page

    App Management Page

    enter image description here

    React Native

    Load URL / Handle Redirect

      componentDidMount() {
            Linking.openURL(url);
            Linking.addEventListener('url', (responseUrl) => {
              console.log(responseUrl);
            });
          }