aws-amplify / aws-sdk-ios

AWS SDK for iOS. For more information, see our web site:
https://aws-amplify.github.io/docs
Other
1.68k stars 880 forks source link

getTokens takes forever to complete #2095

Closed hossamghareeb closed 4 years ago

hossamghareeb commented 4 years ago

Describe the bug I login with Google via hosted UI and all goes well. Then I call getTokens to get the access token to use it to hit our services and also all goes well. Then next time I open the app, when I call getTokens again, it never returns and keeps stuck inside. When I call initialize the state returned is corrected and it says signedIn. I don't see anything/errors in log

Setting up Cognito:

private let configuration: [String: Any] = [
    "IdentityManager": [
        "Default": [:]
    ],
    "CognitoUserPool": [
        "Default": [
            "PoolId": "eu-west-XXXXX",
            "AppClientId": "XXXXXX",
            "Region": "eu-west-1"
        ]
    ],
    "CredentialsProvider": [
        "CognitoIdentity": [
            "Default": [
                "PoolId": "eu-west-1_7FMzK7QQ6",
                "Region": "eu-west-1"
            ]
        ]
    ],
    "Auth": [
        "Default": [
            "OAuth": [
                "WebDomain": "accounts-staging.xxxx.com",
                "AppClientId": "xxxxxxx",
                "SignInRedirectURI": "ourApp://sign-in",
                "SignOutRedirectURI": "ourApp://",
                "Scopes": ["openid", "email"]
            ]
        ]
    ]
]

@objc final class CognitoClient: NSObject {

    @objc static let shared = CognitoClient()
    @objc let awsClient = AWSMobileClient(configuration: configuration)
}

Login with Google:

        let hostedUIOptions = HostedUIOptions(scopes: ["openid", "email"], identityProvider: "Google")

        // Present the Hosted UI sign in.
        CognitoClient.shared.awsClient.showSignIn(navigationController: navigationController, hostedUIOptions: hostedUIOptions) { (userState, error) in

        }

Getting Token:

    /// Get valid token
    private func getValidTokenStringSync() -> String? {
        let dispatchGroup = DispatchGroup()

        var tokenString: String?

        dispatchGroup.enter()
        awsClient.getTokens { (token, error) in
            tokenString = token?.accessToken?.tokenString
            dispatchGroup.leave()
        }
        dispatchGroup.wait()
        return tokenString
    }

To Reproduce Steps to reproduce the behavior:

  1. Login successfully with Google via hosted UI in iOS
  2. Kill the app and open it again
  3. Call getTokens and it will never return

Expected behavior completion block to be called and get the tokens again.

Environment(please complete the following information):

Device Information (please complete the following information):

kneekey23 commented 4 years ago

hi @hossamghareeb what version of the Google SDK are you trying to use? I believe the Google SDK needs an update to fix some off the issues [#1993] we have seen with iOS13 and just want to make sure this isn't related to that issue as well.

hossamghareeb commented 4 years ago

Hey @kneekey23, I'm using GoogleSignIn 4.4. I think the issue is in version 5.0 right? Also let me understand one thing, I'm using hosted UI option so what I understand is that the login will be done in SFSafariViewController and it has nothing to do with Google SDK

walterg2 commented 4 years ago

Seeing the same issue using AWS Cognito login as well.

walterg2 commented 4 years ago

I am able to reproduce this one without using either a Google Authentication option (simply using the standard AWS Cognito Login) and not a hosted UI.

spandanabatchu commented 4 years ago

I have a similar issue in my iOS app. AWSCognitoService.client.getTokens completion doesn't get called even though AWSCognitoService.client.isSignedIn is true. The AWSCognitoService.client referred above is AWSMobileClient provided by the SDK that initialised with my aws pool configuration.
static let client = AWSMobileClient(configuration: awsConfiguration) We have set the token expiry to one day and when I launch the iOS app after a day, the completion doesnt get called.

The SDK version that Im using is as below. The issue can be reproduced on both iOS12 and 13.

  - AWSAuthCore (2.12.1):
    - AWSCore (= 2.12.1)
  - AWSCognitoIdentityProvider (2.12.1):
    - AWSCognitoIdentityProviderASF (= 1.0.1)
    - AWSCore (= 2.12.1)
  - AWSCognitoIdentityProviderASF (1.0.1)
  - AWSCore (2.12.1)
  - AWSMobileClient (2.12.1):
    - AWSAuthCore (= 2.12.1)
    - AWSCognitoIdentityProvider (= 2.12.1)
karasahinemre commented 4 years ago

We're facing with this issue too.

hossamghareeb commented 4 years ago

Ok, I think I found what is the problem. I did some intensive debugging in what is happing inside awsClient.getTokens and I found that there is some logic is being sent to be executed in the main queue while I'm already blocking the main queue to get the new token synchronously. So when I removed the code of using DispatchGroup, the function returns as expected without locking. I don't remember what is this called, maybe deadlock :). This should be documented somewhere by the AWS iOS team as I spent too much time to figure out this issue.

spandana-batchu commented 4 years ago

@hossamghareeb I tried moving awsClient.getTokens to background thread and Im still not getting the call back. Do you know anything else that would help? Some details here

royjit commented 4 years ago

@hossamghareeb Could you please provide some code sample that fixed your issue.

Ideally getToken request should not happen in main thread. When the session expires, getToken will wait for the user to be signed in again to return with a result. You have to listen to signIn event by adding yourself as a listener :

AWSMobileClient.default().addUserStateListener(self) { (state, dict) in

  }

When you get the state as signedOut, call AWSMobileClient.default().signIn to authenticate the user again and the getToken will return.

palpatim commented 4 years ago

@hossamghareeb We use the main queue for certain activities using the Drop-in UI [edit: and Hosted UI], so if you're blocking the main thread on a request, that could cause issues. In general a best practice is to perform these kinds of activities asynchronously and invoke callbacks when you receive a response, so you don't block your working thread. That said, if you have encountered a use case where you must block the main thread (for example, the SDK requires you to invoke something on the main thread and blocks until complete) we'd love to know about that since we definitely don't want to build that potential deadlock into the SDK.

hossamghareeb commented 4 years ago

@palpatim my use case was that the project i'm working on is assuming that the token is loaded synchronously as it was not a JWT token. When I moved to Cognito, fetching the token is done async which required me to do some refactoring in many places so I decided to block the thread till I fetch the token, it worked at the beginning but not with hosted UI as you see. I used now URLProtocol and it helped me fix the problem without refactoring.

palpatim commented 4 years ago

@hossamghareeb Thanks for the clarification. It sounds like everything is working as designed--the asynchronous parts are asynchronous as designed, and invoking sign in UI methods on the main thread. I'm going to close this issue, but please feel free to open a new one if you encounter other problems.