Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support OAuth #11

Open
Crizzooo opened this issue Aug 8, 2021 · 16 comments
Open

Support OAuth #11

Crizzooo opened this issue Aug 8, 2021 · 16 comments
Labels
enhancement New feature or request

Comments

@Crizzooo
Copy link

Crizzooo commented Aug 8, 2021

Hello,

Not reporting a specific issue as much as asking a question.

Is the current webView implementation intended to work with OAuth flows? Such as Platypus in Sandbox, or Chase in production.

I believe we need to add code to be able to relaunch PlaidLink with a receivedRedirectUri.

What do you think the current expected behavior is for OAuth flows?

@JBaczuk
Copy link
Contributor

JBaczuk commented Aug 24, 2021

@Crizzooo I'm not sure I understand your question. Plaid has a very specific flow, when would a different OAuth flow occur?

@Crizzooo
Copy link
Author

@JBaczuk Does this currently work with OAuth that redirects to Chase app, and then back to the RN app?

@valentinoConti
Copy link

@JBaczuk
I think it does not support OAuth, running the example of the repo, you can pay with normal payments, but if you select ACH and select the "5th Bank of Central Wisconsin" that uses OAuth, you end up with a infinite spinner with no redirect

@JBaczuk JBaczuk added the enhancement New feature or request label Nov 7, 2021
@JBaczuk
Copy link
Contributor

JBaczuk commented Nov 7, 2021

I'm not even sure this is possible, but I'll tag it as a feature request.

@isaachinman
Copy link

@JBaczuk Thanks very much for putting together this package – saved me a lot of headache today. I do believe OAuth could hypothetically be implemented via receivedRedirectUri, right?

Are you aware of any examples using receivedRedirectUri? I have searched extensively but haven't found anything.

@valentinoConti
Copy link

valentinoConti commented Dec 12, 2021

@JBaczuk Thanks very much for putting together this package – saved me a lot of headache today. I do believe OAuth could hypothetically be implemented via receivedRedirectUri, right?

Are you aware of any examples using receivedRedirectUri? I have searched extensively but haven't found anything.

Our app uses Expo and on internet I only found information to do it with react-native-cli and its pods. I was able to do this after a lot of struggle with Expo too!

First I had to configure the client_id and public_key and add a Redirect URI on the Plaid dashboard (dashboard.plaid.com). The redirect URI has to end on ".html" and that link has to be a static HTML file that redirects you back to the app. My HTML file looks like this:

<html>
<head>
  <style>.text { font-size: 64px; }</style>
</head>
<body style="text-align: center;">
  <p class="text">Please wait while we redirect you to MYAPP again...</p>
  <p class="text"><a href="javascript:redirectToApp()">Open MYAPP</a></p>
  <script>
    var redirectToApp = function() {
      var openURL = window.location.search + window.location.hash;
      window.location.replace(`yourapp://--/oauth${openURL}`);
    }
    window.onload = redirectToApp;
  </script>
</body>
</html>

Of course you have to add "youapp" to your scheme on your app.json file, so your app recognizes the redirect with the link that looks like yourapp://whatever, for example:

{
  "expo": {
    "name": "asd",
    "slug": "asd",
    "version": "0.1.0",
    "scheme": "yourapp",
    ...

and then handle the redirect with something like Linking.addEventListener('url', this.openDeepLink); somewhere on the root of your app (search "deep linking with expo" if you don't know about this)


Then on the react-native code, I added the following methods:

import { WebView } from 'react-native-webview';
...

const injectedJavaScript = `(function() {
  window.postMessage = function(data) {
    window.ReactNativeWebView.postMessage(data);
  };
})()`;

const webViewRef = useRef<WebView>();

const navigationRedirect = event => {
    if (event.url && event.url.startsWith('plaidlink://')) {
      const eventParams = queryString.parse(event.url.replace(/.*\?/, ''))
      const { link_session_id, institution_id, institution_name } = eventParams;

      if (event.url.startsWith('plaidlink://exit')) {
        // handle plaid exit here, for example go back on navigation.
         console.log('plaid exit!');
      } else if (event.url.startsWith('plaidlink://connected')) {
        // handle connect successfully here
        const { public_token } = eventParams;
        const accounts = JSON.parse(eventParams.accounts);
        console.log('plaid connect!');
        console.log('account_id', accounts[0]["_id"]);
        console.log('institution_id', institution_id);
        console.log('institution_name', institution_name);
        console.log('link_session_id', link_session_id);
        console.log('public_token', public_token);
      }
      return false
    }
    return true
}

and finally I used the component like this, where token its the link token that you get via plaid API sending as a minimum the redirectURI + your client id + your client name (docs here), and env its 'sandbox' or 'production' depending on the running environment.

<WebView
      ref={webViewRef}
      source={`https://cdn.plaid.com/link/v2/stable/link.html?token=${token}&env=${env}&isWebview=true`}
      useWebKit
      injectedJavaScript={injectedJavaScript}
      originWhitelist={['http://*', 'https://*', 'plaidlink://*']}
      onShouldStartLoadWithRequest={navigationRedirect}
      onNavigationStateChange={navigationRedirect}
      setSupportMultipleWindows={false}
      onError={() => {
        webViewRef.current.goBack();
      }}
/>

(I already had the token because on my app I get it on the previous step/screen, you can also render the WebView conditionally after getting the link token on the same screen).

Hope it helps!

@apiel51
Copy link

apiel51 commented Dec 28, 2021

^^similar to the above comment, I was able to get OAuth working as well!

One snag that held me up for a little while was that I was using encodeURI to encode the receivedRedirectUri parameter when instead I should have been using encodeURIComponent. This resulted in me getting a 400 when I would open the plaid link URL when passing that parameter since the oauth_state_id couldn't be parsed correctly.

@BFMarks
Copy link

BFMarks commented Jul 24, 2022

Can Expo-Plaid provide more support here? Without best practices for Oauth for Expo-Plaid, the library is unusable.

@JBaczuk
Copy link
Contributor

JBaczuk commented Jul 24, 2022

Happy to take PRs

@BFMarks
Copy link

BFMarks commented Jul 27, 2022

So it's fairly easy to get the link token and the oauth_state_id but can you paste an example of your WebView component with the functions?

I have this but it's failing when triggered:

const uri_string = 'https://cdn.plaid.com/link/v2/stable/link.html?Webview=true&token={token}&receivedRedirectUri=https://alpha.cloudcastles.io/oauth-page?oauth_state_id={oauth_state_id}'
    console.log(uri_string)
    return (
      <View style={{ flex: 1, marginTop: StatusBar.currentHeight + 20 }}>
        <WebView
          // ref={webViewRef}
          ref={(ref) => (webviewRef = ref)}
          originWhitelist={['https://*', 'plaidlink://*']}
          source={{ uri: uri_string }}
          // useWebKit
          // injectedJavaScript={injectedJavaScript}
          // originWhitelist={['http://*', 'https://*', 'plaidlink://*']}
          // onShouldStartLoadWithRequest={navigationRedirect}
          // onNavigationStateChange={navigationRedirect}
          // setSupportMultipleWindows={false}
          // onError={() => {
          //   webViewRef.current.goBack();
          // }}
      />
      </View>

@apiel51
Copy link

apiel51 commented Jul 28, 2022

@BFMarks I believe you need to encode the redirect URI as I mentioned above. Also, this library is certainly not unusable - we use it in production 🙂

^^similar to the above comment, I was able to get OAuth working as well!

One snag that held me up for a little while was that I was using encodeURI to encode the receivedRedirectUri parameter when instead I should have been using encodeURIComponent. This resulted in me getting a 400 when I would open the plaid link URL when passing that parameter since the oauth_state_id couldn't be parsed correctly.

@BFMarks
Copy link

BFMarks commented Jul 28, 2022

Fair enough @apiel51 - I have tried encodeURI with the same outcome, Plaid support said not to encode it which is why I posted this. Mind posting your reinitialization Webview and corresponding functions?

To my previous comment, Plaid support specifically recommended against using the Webview in this manner (but ejecting Expo to basic RN doesn't sound like a fun alternative option either). Any support would be appreciated 🙏

@apiel51
Copy link

apiel51 commented Jul 28, 2022

@BFMarks I think Plaid support was mistaken as certain characters must be encoded or the URI is invalid. I believe you should use encodeURIComponent as I mentioned since you are passing the redirect URI as a query parameter, not navigating to the URI itself.

Here's a snippet of our code which might be helpful:

  const webviewRef = useRef<WebView>(null);
  const plaidUri = `https://cdn.plaid.com/link/v2/stable/link.html?isWebview=true&token=${linkToken}`;
  const PLAID_OAUTH_PATH = 'open/plaid_oauth_redirect';

  useEffect(() => {
    // Listens for when our app is deeplinked to
    const handler: Linking.URLListener = ({ url }) => {
      const { path } = Linking.parse(url);

      if (path === PLAID_OAUTH_PATH) {
        webviewRef.current?.injectJavaScript?.(
          `window.open('${plaidUri}&receivedRedirectUri=${encodeURIComponent(
            url,
          )}')`,
        );
      }
    };

    Linking.addEventListener('url', handler);

    return () => {
      Linking.removeEventListener('url', handler);
    };
  }, [plaidUri]);

Yeah I assume Plaid would recommend against it but oh well 🤷

@macsinte
Copy link

macsinte commented Mar 12, 2023

So I managed to redirect back to my app, however I'm curious how y'all are handling the re-authentication when coming back? My experience goes from adding an account with Plaid, to a list of Bank Accounts, when a user is authenticated, to then re-directing to the app and authenticating the user.

Are y'all passing in a session token ? Or you're re-authenticating the user ?

@macsinte
Copy link

Also, when you re-direct back to the app, how to you pass the account information obtained in the webview? I'm confused on that

@macsinte
Copy link

Nevermind! thanks everyone for your help, figured everything out based on the comments above. It took a little bit to understand everything that's needed, but it totally makes sense now. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

7 participants