AWS ExpiredTokenException after app relaunch

I’m building an iOS (Swift) app using AWS as the backend with Developer Authenticated Identities. Everything works fine until I close the app, leave it for a while and then relaunch. In this scenario I often, but not always, receive ExpiredTokenException errors when trying to retrieve data from AWS.

Here is my code:

  • AWS: How to properly authenticate a user against Cognito Pool and use it for Cognito Federated Identity?
  • iOS AWS SDK and CloudFront - accessing images
  • Creating a user authentication system for iOS (previously with Parse hopefully AWS)
  • AWS Cognito Swift credentials provider “logins is deprecated: Use AWSIdentityProviderManager”
  • Using the force_load linker flag with RestKit (iOS)
  • Amazon sns (push notification) does not send to IOS application
  • class DeveloperAuthenticatedIdentityProvider: AWSAbstractCognitoIdentityProvider {
        var _token: String!
        var _logins: [ NSObject : AnyObject ]!
    
        override var token: String {
            get {
                return _token
            }
        }
    
        override var logins: [ NSObject : AnyObject ]! {
            get {
                return _logins
            }
            set {
                _logins = newValue
            }
        }
    
        override func getIdentityId() -> AWSTask! {
            if self.identityId != nil {
                return AWSTask(result: self.identityId)
            } else {
                return AWSTask(result: nil).continueWithBlock({ (task) -> AnyObject! in
                    if self.identityId == nil {
                        return self.refresh()
                    }
                    return AWSTask(result: self.identityId)
                })
            }
        }
    
        override func refresh() -> AWSTask! {
            let apiUrl = "https://url-goes-here"    // call my server to retrieve an OpenIdToken
            request.GET(apiUrl, parameters: nil, progress: nil,
                success: {
                    (task: NSURLSessionDataTask, response: AnyObject?) -> Void in
    
                    let tmp = NSMutableDictionary()
                    tmp.setObject("temp", forKey: "ExampleApp")
                    self.logins = tmp as [ NSObject : AnyObject ]
    
                    let jsonDictionary = response as! NSDictionary
                    self.identityId = jsonDictionary["identityId"] as! String
                    self._token = jsonDictionary["token"] as! String
                    awstask.setResult(response)
                },
                failure: {
                    (task: NSURLSessionDataTask?, error: NSError) -> Void in
    
                    awstask.setError(error)
                }
            )
    
            return awstask.task
        }
    }
    

    And in the AppDelegate:

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        let identityProvider = DeveloperAuthenticatedIdentityProvider()
    
        // set default service configuration
        let credentialsProvider = AWSCognitoCredentialsProvider(regionType: cognitoRegion, identityProvider: identityProvider, unauthRoleArn: unauthRole, authRoleArn: authRole)
        let configuration = AWSServiceConfiguration(region: defaultServiceRegion, credentialsProvider: credentialsProvider)
        AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = configuration
    
        // set service configuration for S3 (my bucket is located in a different region to my Cognito and Lambda service)
        let credentialsProviderForS3 = AWSCognitoCredentialsProvider(regionType: cognitoRegion, identityProvider: identityProvider, unauthRoleArn: unauthRole, authRoleArn: unauthRole)
        let awsConfigurationForS3 = AWSServiceConfiguration(region: s3ServiceRegion, credentialsProvider: credentialsProviderForS3)
        AWSS3TransferUtility.registerS3TransferUtilityWithConfiguration(awsConfigurationForS3, forKey: "S3")
    
        return true
    }
    

    This post suggests that the Cognito token has expired and it is up to the developer to manually refresh. This seems overly complex as it would require setting a timer to refresh regularly, handling app closures and relaunches and handling AWS requests that occur while the refresh is taking place. Is there a simpler way? For example, is it possible to have the AWS SDK automatically call refresh whenever it attempts to query the server using an expired token?

    Any help would be appreciated. I’m using version 2.3.5 of the AWS SDK for iOS.

    2 Solutions Collect From Internet About “AWS ExpiredTokenException after app relaunch”

    The AWS Mobile SDK for iOS 2.4.x has a new protocol called AWSIdentityProviderManager. It has the following method:

    /**
     * Each entry in logins represents a single login with an identity provider.
     * The key is the domain of the login provider (e.g. 'graph.facebook.com') and the value is the
     * OAuth/OpenId Connect token that results from an authentication with that login provider.
     */
    - (AWSTask<NSDictionary<NSString *, NSString *> *> *)logins;
    

    The responsibility of an object conforming to this protocol is to return a valid logins dictionary whenever it is requested. Because this method is asynchronous, you can make networking calls in it if the cached token is expired. The implementation is up to you, but in many cases, AWSIdentityProviderManager manages multiple AWSIdentityProviders, aggregates them and return the logins dictionary.

    Unfortunately developers refreshing the token is the only way.

    I agree that it would be simpler for app developers if AWS SDK handled this but the way CrdentialsProvider is designed is supposed to be generic for all providers. For example, if someone wants to use Facebook as provider then AWS SDK will not be able to handle the refresh on its own and developer will have t handle that in his app. Keeping the refresh flow out of the SDK gives us the capability to keep the CredentialsProvider generic.