okta / okta-react-native

OIDC enablement for React Native applications
https://github.com/okta/okta-react-native
Other
59 stars 39 forks source link

createConfig never returns a result on in some instances #391

Open yurykorzun opened 11 months ago

yurykorzun commented 11 months ago

Describe the bug?

We are seeing an issue with Okta React Native SDK, we are trying to reproduce it, but we are seeing this issue logged in production logs.

What is happening is that createConfig call never returns a result and the app gets stuck on the initialization screen. We don't see any errors logged, it just gets stuck. We have call createConfig when we initialize the app.

await createConfig

After createConfig returns a value, we check it and set a flag that indicates that auth is ready. In our case, we never see logs showing that the result is empty or any errors being thrown. We only see logs showing that createConfig never returned a result, which never set the flag and the app gets stuck.

I would appreciate any suggestions or ideas about how to better troubleshoot it, it causes a problem that affects our users. If we manage to reproduce it, I will add more information.

What is expected to happen?

createConfig call returns config and the app continues initialization.

What is the actual behavior?

createConfig never returns a result, which means that the native implementation never resolves a result.

Reproduction Steps?

We are still working on trying to reproducing this issue

Additional Information?

No response

SDK Version

2.9.1

Build Information

No response

rajdeepnanua-okta commented 11 months ago

Hi @yurykorzun, thanks for reporting this issue. For better context, could you also provide details on how you are calling createConfig? Please omit sensitive details such as issuer, clientId, redirectUri, endSessionRedirectUri, and discoveryUri. Also, which platform is this issue happening with, android or iOS?

If possible, I would also like to understand how you are logging details on whether createConfig returns or not, and in which thread the call to createConfig is being made.

yurykorzun commented 11 months ago

Here is a description of how we are using Okta library in our project.

First, we have AuthProvider React context, that manages authentication process in the app and provides access to Okta to all screens. We have a property called authIsReady, which indicates that Okta config was successfully created:

await  createConfig({
          clientId: nativeClientId,
          redirectUri: ..
          endSessionRedirectUri …
          discoveryUri: issuer,
          scopes: ['openid', 'profile', 'offline_access'],
          requireHardwareBackedKeyStore: false,
        });
await refreshTokens();

setAuthIsReady(true);

We rely of the value in authISReady to determine when we can navigate to the landing screen. If for some reason it never becomes true, users can get stuck on an empty screen. We will add a loading indicator that screen, but it’s a separate UX issue.

  // Wait until Okta is initialized and auth is ready
  // before navigating away from this screen
  useEffect(() => {
    if (!authIsReady) {
      return;
    }

    navigateAway();
  }, [authIsReady, navigateAway]);

We are seeing this issue mostly on iOS because most of our users are on iOS, but it happens on Android as well.

yurykorzun commented 10 months ago

We simplified our code to make it more straightforward, we are doing all our initialization on the splash screen. We still seeing that some of our users get stuck on the splash screen, because createConfig and refershTokens never complete. There are no errors, some users never get these two actions complete succesfully.

jpf commented 8 months ago

@rajdeepnanua-okta I've taken a look at this today. Given the elusive nature of this issue, I wonder if it would make sense to add more in-depth debugging capabilities to the SDK?

The area that would benefit the most from mode detailed debugging would be the createConfig function

@objc
    func createConfig(_ clientId: String,
                      redirectUrl: String,
                      endSessionRedirectUri: String,
                      discoveryUri: String,
                      scopes: String,
                      userAgentTemplate: String,
                      requestTimeout: Int,
                      promiseResolver: RCTPromiseResolveBlock,
                      promiseRejecter: RCTPromiseRejectBlock) {
        do {
            let uaVersion = OktaUserAgent.userAgentVersion()
            let userAgent = userAgentTemplate.replacingOccurrences(of: "$UPSTREAM_SDK", with: "okta-oidc-ios/\(uaVersion)")
            OktaOidcConfig.setUserAgent(value: userAgent)
            let config = try OktaOidcConfig(with: [
                "issuer": discoveryUri,
                "clientId": clientId,
                "redirectUri": redirectUrl,
                "logoutRedirectUri": endSessionRedirectUri,
                "scopes": scopes
            ])

            config.requestCustomizationDelegate = self

            oktaOidc = try OktaOidc(configuration: config)
            self.requestTimeout = requestTimeout

            promiseResolver(true)
        } catch let error {
            promiseRejecter(OktaReactNativeError.oktaOidcError.errorCode, error.localizedDescription, error)
        }
    }

One idea would be to modify the code above to use dispatch_after (or a DispatchQueue) to call promiseRejecter with an error if OktaOidc doesn't return within the expected p99 response time.

This might allow code in React Native to better handle the rare case where neither promiseResolver nor promiseRejecter is called?