okta / okta-oidc-js

okta-oidc-js
https://github.com/okta/okta-oidc-js
Other
394 stars 232 forks source link

Confused? Is Okta auth in Android apps shareable with webviews? #877

Open bdruth opened 4 years ago

bdruth commented 4 years ago

I'm submitting this issue for the package(s):

I'm submitting a:

Current behavior

I'm new to Okta and trying to navigate the best way to incorporate Okta authentication in native apps (iOS/Android) to SSO with webviews opened by these apps to apps protected by the same Okta domain. I've been able to get this working in iOS using the custom-sign-in example in https://github.com/okta/samples-js-react-native and the information to setup usePersistentCookie. On Android, however, I'm not connecting the dots. Is this possible? I've seen an issue reference Chrome Custom Tabs and how cookies can't be shared to a webview from there, but it doesn't look like the custom-sign-in example uses that on Android. The browser-sign-in example is documented as returning a code to the app which is then exchanged for an access token and id token, so it doesn't appear that there's any cookie being set that a webview could inherit in this approach, either?

Expected behavior

Both Android and iOS platforms have an Okta SSO mechanism to embedded webviews from the native application.

Minimal reproduction of the problem with instructions

Run https://github.com/okta/samples-js-react-native apps on Android and use a webview (e.g. https://github.com/react-native-community/react-native-webview) to load an Okta web app protected in the same domain - should work w/o prompting for login again.

Extra information about the use case/user story you are trying to implement

Implement a hybrid native app with some native functionality and some webview functionality under the same Okta domain without requiring multiple logins on Android.

Environment

swiftone commented 4 years ago

@bdruth - Thanks for the question, I'll forward this to team members that can speak intelligently about react-native and see if we can get you an answer.

shuowu commented 4 years ago

@bdruth Webview is not the recommended way to implement OIDC flow in the native app. Quoted from https://www.linkedin.com/pulse/webviews-bad-use-appauth-mike-schwartz/

In a WebView any malicious code in the page has the same rights as your application, so you should make sure you only load trusted content. But there is another risk–a malicious app may also have access to browser content (like cookies) and may snoop passwords or intercept OAuth codes.

For the browser-sign-in, you may need to listen on signInSuccess event to get the tokens after redirect, like https://github.com/okta/samples-js-react-native/blob/82b302de87a1d8d0213bff4899604202aef1d312/browser-sign-in/App.js#L51-L54 If you want to implement customized UI, you can follow the custom-sign-in sample to build your native UI, then call signIn function from @okta/okta-react-native to acquire token.

signIn({ username, password })
      .then(token => {
        // do things with acquired token
      });
bdruth commented 4 years ago

OK - that jives with some of the things I've read. What I'm not clear on is what to do with the acquired token so that there's a seamless SSO? I'm not married to using webviews, and I've read that Chrome Custom Tabs on Android might be the better option, though it's bizarre that those are two completely different things in react-native, and the cross-platform behavior is completely different for Chrome Custom Tabs ... ideally there'd be a simple way to use webviews on iOS and Chrome Custom Tabs on Android in an effectively identical way from React Native, but I digress ...

shuowu commented 4 years ago

@bdruth The @okta/react-native has implemented the browser sign-in flow based on the android custom tab, you can use it by calling signIn function without providing any arguments.

bdruth commented 4 years ago

Right ... but I need custom sign-in. So, how do I use custom sign-in and pass what to a Chrome Custom Tab?

shuowu commented 4 years ago

For custom signin in react native you will need to build your own sign in UI, instead of using custom tabs to open the okta login page.

https://github.com/okta/okta-react-native#custom-sign-in

bdruth commented 4 years ago

Right - I'm following all that. I have the custom-sign-in working from the Okta react-native samples. All good with custom-sign-in, but ... how do I SSO that to something, anything that can load a web page ... I don't really care if it's Chrome Custom Tabs or webviews at this point.

shuowu commented 4 years ago

@bdruth If I understand your question correctly. You want to implement the SSO as the app redirects the user to a webpage (OKTA domain) to start the sign in process, then it redirects user back to the app after success sign in. If that's the case, it is the browser sign in flow that provides from the sdk. It is implemented by using custom tabs in android.

Or you want to customize any step in the browser sign in flow?

bdruth commented 4 years ago

No, that's not correct. We're looking at how we would have a native app, with native (custom sign-in flow), that will have native functionality in hybrid with loading web pages from existing / legacy secured systems. Both are setup in Okta using OIDC (there may be future need for SAML, too, but right now our test bed is using OIDC). Current behavior is:

desired behavior is:

This is working in iOS using the usePersistentCookie and sharedCookiesEnabled on the react-native-webview. So, the question is, how do we accomplish this in the Android ecosystem.

FeiChen-okta commented 4 years ago

Hi @bdruth,

The android native sdk needs to have webview support. I have an experimental branch with this webview but this is not something we are planning to support. If you like I can share the patch with you.

bdruth commented 4 years ago

@FeiChen-okta - feel free to share patch. So, if I'm understanding correctly, the only way to have SSO between native & web content on Android is to use the browser-sign-in flow? Custom sign-in (without your experimental, unsupported patch) is not possible, irrespective of Chrome Custom Tabs or Webview?

FeiChen-okta commented 4 years ago

Hi @bdruth We have a couple of different configurations

  1. custom-sign-in: To make SSO work for your legacy web pages you'll have to get the response headers from the custom signIn call and set those headers in react-native-webview

  2. browser-sign-in-chrome-tabs: This will not work for your legacy web pages if using react-native-webview since the sessions are stored in chrome and not webview. If you open your legacy web pages with chrome custom tabs then it will work but I'm not sure if react-native-webview supports it.

  3. browser-sign-in-webview: This will require the following patch. This will make oidc sdk use webview instead of chrome custom tabs. This way the session is already in webview so react-native-webview should work.

You can apply the following patch to the master branch in https://github.com/okta/okta-oidc-android Once you've compiled the aar you'll have to add it to the android bridge. 0001-Webview-support.patch.txt

bdruth commented 4 years ago

@FeiChen-okta thank you! So, I'm assuming I need to actually call signIn from @okta/okta-auth-js as is being done by the signIn call in okta-react-native, since the signIn exposed by okta-react-native is only returning the access token as far as I can tell? Or is there a way to make the call through okta-react-native? @okta/okta-react-native#index.js

export const signIn = async(options) => {
  // Custom sign in
  if (options && typeof options === 'object') {
    return authClient.signIn(options)
      .then((transaction) => {
        const { status, sessionToken } = transaction;
        if (status !== 'SUCCESS') {
          throw new Error('Transaction status other than "SUCCESS" has been return, please handle it properly by calling "authClient.tx.resume()"');
        } 
        return authenticate({ sessionToken });
      })
      .then(token => {
        if (!token) {
          throw new Error('Failed to get accessToken');
        }

        return token;
      })
      .catch(error => {
        throw new OktaAuthError('-1000', 'Sign in was not authorized', error);
      });
  }
FeiChen-okta commented 4 years ago

Hi @bdruth Currently the react-native SDK does not support a way to get the headers from the Android bridge. So you'll need a way to get the response header from HttpClientImpl.java to react-native layer and set the cookies in react-native-webview

Or the other way around where react-native set a boolean flag similar to iOS sharedCookiesEnabled. This is then propagated to HttpClientImpl and it will use CookieManager to set the cookies for WebView

After these changes signIn should set the correct cookies in WebView depending on which path you implement. Unfortunately both methods require some changes to the SDK. I believe the react-native to Android way is the simplest since that seems to be the way react-native-webview is enabling cookies in iOS.

bdruth commented 4 years ago

K, I'll take a look and see what I can come up with. Thanks much for your assistance thus far!

gabrielmirandat commented 3 years ago

@bdruth I am facing the same issue now. Have you workaround this? Tks