iosamazon-web-servicesaws-sdkamazon-cognitoaws-sdk-ios

AWS iOS SDK Cognito Developer Authentication (Swift)


I am having a hard time figuring out how to return developer credentials provided by my server (via AWS) to my Example identity provider.

It seems I need to do this synchronously within the refresh method on the ExampleIdentityProvider class. I'm using AFNetworking to make the request, but it is an async GET request. How can I do this synchronously for the refresh method on my IdentityProvider?

The following is in Swift:

class ExampleIdentityProvider: AWSAbstractIdentityProvider  {
    var newToken: String!

    override var token: String {
        get {
            return newToken
        }
        set {
            newToken = newValue
        }
    }

    override func getIdentityId() -> BFTask! {
        if self.identityId != nil {
            return BFTask(result: self.identityId)
        }else{
            return BFTask(result: nil).continueWithBlock({ (task) -> AnyObject! in
                if self.identityId == nil {
                    return self.refresh()
                }
                return BFTask(result: self.identityId)
            })
        }
    }

    override func refresh() -> BFTask! {
        return BFTask(result: nil).continueWithBlock({ (task) -> AnyObject! in
            let result =  AFNETWORKING REQUEST FOR CREDENTIALS TO MY SERVER
            self.identityId = result.identityId
            self.token = result.token

            return BFTask(result: self.identityId)
        })
    }
}

Solution

  • I believe I have figured it out. I needed to utilize BFTask which is built for handling background tasks with completion.

    For people struggling with a Swift implementation of developer authentication with Cognito who might have a similar setup to me, this is how I accomplished it:

    class ExampleAppIdentityProvider: AWSAbstractCognitoIdentityProvider {
        var _token: String!
        var _logins: [ NSObject : AnyObject ]!
    
        // Header stuff you may not need but I use for auth with my server
        let acceptHeader = "application/vnd.exampleapp-api+json;version=1;"
        let authHeader = "Token token="
        let userDefaults = NSUserDefaults.standardUserDefaults()
        let authToken = self.userDefaults.valueForKey("authentication_token") as String
    
        // End point that my server gives amazon identityId and tokens to authorized users
        let url = "https://api.myapp.com/api/amazon_id/"
    
        override var token: String {
            get {
                return _token
            }
        }
    
        override var logins: [ NSObject : AnyObject ]! {
            get {
                return _logins
            }
            set {
                _logins = newValue
            }
        }
    
        override func getIdentityId() -> BFTask! {
            if self.identityId != nil {
                return BFTask(result: self.identityId)
            }else{
                return BFTask(result: nil).continueWithBlock({ (task) -> AnyObject! in
                    if self.identityId == nil {
                        return self.refresh()
                    }
                    return BFTask(result: self.identityId)
                })
            }
        }
    
        override func refresh() -> BFTask! {
            let task = BFTaskCompletionSource()
            let request = AFHTTPRequestOperationManager()
            request.requestSerializer.setValue(self.acceptHeader, forHTTPHeaderField: "ACCEPT")
            request.requestSerializer.setValue(self.authHeader+authToken, forHTTPHeaderField: "AUTHORIZATION")
            request.GET(self.url, parameters: nil, success: { (request: AFHTTPRequestOperation!, response: AnyObject!) -> Void in
                // The following 3 lines are required as referenced here: http://stackoverflow.com/a/26741208/535363
                var tmp = NSMutableDictionary()
                tmp.setObject("temp", forKey: "ExampleApp")
                self.logins = tmp
    
                // Get the properties from my server response
                let properties: NSDictionary = response.objectForKey("properties") as NSDictionary
                let amazonId = properties.objectForKey("amazon_identity") as String
                let amazonToken = properties.objectForKey("token") as String
    
                // Set the identityId and token for the ExampleAppIdentityProvider
                self.identityId = amazonId
                self._token = amazonToken
    
                task.setResult(response)
            }, failure: { (request: AFHTTPRequestOperation!, error: NSError!) -> Void in
                task.setError(error)
            })
            return task.task
        }
    }
    

    And initialized the ExampleAppIdentityProvider by doing:

        let identityProvider = ExampleAppIdentityProvider()
        let credentialsProvider = AWSCognitoCredentialsProvider(regionType: AWSRegionType.USEast1, identityProvider: identityProvider, unauthRoleArn: GlobalVariables.cognitoUnauthRoleArn, authRoleArn: GlobalVariables.cognitoAuthRoleArn)
        let defaultServiceConfiguration = AWSServiceConfiguration(region: .USEast1, credentialsProvider: credentialsProvider)
        AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = defaultServiceConfiguration
    
        let transferManager = AWSS3TransferManager.defaultS3TransferManager()
    
        let uploadRequest = AWSS3TransferManagerUploadRequest()
        uploadRequest.bucket = GlobalVariables.awsBucket
        uploadRequest.key = "\(GlobalVariables.environment)/uploads/users/\(userId)/\(type)/\(timestamp)/original.jpg"
        uploadRequest.ACL = .AuthenticatedRead
        uploadRequest.body = tmpFileUrl
    
        // Upload file
        let task = transferManager.upload(uploadRequest)
    

    I created a struct named GlobalVariables with global environment variables that hold values for bucket, unAuthRoleArn, authRoleArn, etc. Of course you don't have to do that, but I'm mentioning it in case someone is confused.