AWS iOS SDK Cognito Developer Authentication (Swift)
Asked Answered
S

3

5

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)
        })
    }
}
Saari answered 14/2, 2015 at 1:1 Comment(0)
S
12

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: https://mcmap.net/q/1922710/-aws-cognito-ios-developer-authenticated-identities
            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.

Saari answered 14/2, 2015 at 10:35 Comment(7)
can you show how you initialized the provider / configuration for the AWS stuff to use this?Squeak
@Squeak see the updated code above. Hope that is helpful.Saari
Could you not use AWSTask instead so you don't have to import the Bolts framework separately?Meditation
@Meditation this was written before Amazon included AWSTask in this framework and were instead using BFTask. You can certainly use AWSTask now.Saari
When you set the login map value is "temp" arbitrary? I can't seem to get an authenticated identity even though I'm getting a token successfully and setting the login map. (docs.aws.amazon.com/cognito/devguide/identity/…)Meditation
@Meditation it was required for me in order to get it to work. Though the article I referenced (#26290756) in my code example has since been updated by the Amazon Developer who posted it.Saari
I think in getIdentityId method in else block we can simply use return self.refresh() instead of creation of new BFTask and all that stuff... That should make code clearer.Astarte
B
2

you can generate you custom class for cognito authentication

import AWSS3
import AWSCore
import Alamofire

//This variable is store aws credential token
var cachedLogin : NSDictionary?
final class AmazonIdentityProvider : AWSCognitoCredentialsProviderHelper{

    // Handles getting the login
    override func logins() -> AWSTask<NSDictionary> {
        guard let cachedLoginObj = cachedLogin else {
            return getCredentials().continueWith(block: { (credentialTask) -> AWSTask<NSDictionary> in
                guard let credential = credentialTask.result else {
                    return AWSTask(result: nil)
                }

                self.setCognitoTokenKey(credential: credential)

                return AWSTask(result: cachedLogin)
            }) as! AWSTask<NSDictionary>
        }
        return AWSTask(result: cachedLoginObj)
    }

    // Handles getting a token from the server
    override func token() -> AWSTask<NSString> {
        return getCredentials().continueWith(block: { (credentialTask) -> AWSTask<NSString> in
            guard let credential = credentialTask.result else {
                return AWSTask(result: nil)
            }

            self.setCognitoTokenKey(credential: credential)

            return AWSTask(result: credential.token as NSString)
        }) as! AWSTask<NSString>
    }

    // Handles getting the identity id
    override func getIdentityId() -> AWSTask<NSString> {

        return getCredentials().continueWith(block: { (credentialTask) -> AWSTask<NSString> in
            guard let credential = credentialTask.result else {
                return AWSTask(result: nil)
            }

            self.setCognitoTokenKey(credential: credential)

            return AWSTask(result: credential.identityId as NSString)
        }) as! AWSTask<NSString>
    }

    //This method is used to AWS Token set
    func setCognitoTokenKey(credential : AmazonCognitoCredential){
        let login: NSDictionary = ["cognito-identity.amazonaws.com": credential.token]
        cachedLogin = login
        self.identityId = credential.identityId
    }

    // Gets credentials from server
    func getCredentials() -> AWSTask<AmazonCognitoCredential> {
        let tokenRequest = AWSTaskCompletionSource<AmazonCognitoCredential>()
        getAwsToken { (isSuccess, error, credentials) in
            if isSuccess
            {
                tokenRequest.set(result: credentials)
            }
            else
            {
                tokenRequest.set(error: error!)
            }
        }
        return tokenRequest.task
    }
    typealias CompletionBlock = (_ success:Bool,_ errorMassage:Error?,_ responce:AmazonCognitoCredential?) -> Void
    func getAwsToken(complitionBlock : @escaping CompletionBlock)  {
//Your server token code
}
/// AmazonCognito credential custom class
final class AmazonCognitoCredential {
    let token: String
    let identityId: String

    init(token: String, identityId: String) {
        self.token = token
        self.identityId = identityId
    }
}

and you can use in app delegate

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

    AWSDDLog.sharedInstance.logLevel = .all
    let identityProvider = AmazonIdentityProvider()
    let credentialsProvider = AWSCognitoCredentialsProvider(regionType: .USEast1, unauthRoleArn: CognitoRoleUnauth, authRoleArn: CognitoRoleAuth, identityProvider: identityProvider)
    let configuration = AWSServiceConfiguration(region: .USWest2, credentialsProvider: credentialsProvider)
    AWSServiceManager.default().defaultServiceConfiguration = configuration

    let task = identityProvider.getIdentityId()
    task.continueWith { (task:AWSTask) -> Any? in
        if (task.error != nil ) {
            print("\(String(describing: task.error))")
        } else {
            print("Task result: \(String(describing: task.result))")
        }
        return nil
    }
    return true
}
Blanco answered 12/12, 2017 at 4:29 Comment(0)
T
0

First of all for uploading file, image,video to AWS S3 privet bucket, you need to do AWS authentication for the that you need ('CognitoId, CognitoAccesstoken) which you get from backend-server.You should have 'CognitoPoolID', 'S3 bucket name' and 'Region' which you can save in Constant file inside swift code. After that you have to write separate class for AWS Authentication

import UIKit
import AWSCore
// this custom class is dedicated for getting getting aws dev auth identity credentials
class DeveloperAuthenticatedIdentityProvider: AWSCognitoCredentialsProviderHelper {
    override init(regionType: AWSRegionType, identityPoolId: String, useEnhancedFlow: Bool, identityProviderManager: AWSIdentityProviderManager?) {
        super.init(regionType: regionType, identityPoolId: identityPoolId, useEnhancedFlow: useEnhancedFlow, identityProviderManager: identityProviderManager)
    }
    override func token() -> AWSTask<NSString> {
        self.identityId = “ADD_COGNITO_IDENTITY_ID”
        let token = “ADD_COGNITO_ACCESS_TOKEN”
        return AWSTask(result: token )
    }
    override func logins () -> AWSTask<NSDictionary> {
        return super.logins()
    }
    /*
     * Use the refresh method to communicate with your backend to get an
     * identityId and token.
     */
    func refresh() -> AWSTask<NSString> {
        self.identityId = “ADD_COGNITO_IDENTITY_ID”
        return AWSTask(result: identityID)
    }
}     

//Write below code from class from where you are uploading file

let devAuth = DeveloperAuthenticatedIdentityProvider.init(
    regionType: ADD_REGION,
                identityPoolId:”ADD_COGNITO_POOL_ID”,
                            useEnhancedFlow: true,
                                        identityProviderManager: nil)
let credentialsProvider = AWSCognitoCredentialsProvider.init(regionType:”ADD_REGION”, identityProvider: devAuth)
let configuration = AWSServiceConfiguration.init(region:”ADD_REGION”, credentialsProvider: credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration


@IBAction func uplaodVideo(){
    uploadFile(with: "FILE_NAME", type: "mov")
}
func uploadFile(with resource: String, type: String) {
        let key = "\(resource).\(type)"
        let localImagePath = Bundle.main.path(forResource: resource, ofType: type)
        let localImageUrl = URL(fileURLWithPath: localImagePath!)
        let transferManager1 = AWSS3TransferUtility.default()
        let expression = AWSS3TransferUtilityUploadExpression()
        self.uploadCompletionHandler = { (task, error) -> Void in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.2, execute: {
                if ((error) != nil){
                    print("Failed with error")
                    print("Error: \(error!)");
                }
                else{
                    print("Sucess")
                }
            })
        }
        let transferUtility = AWSS3TransferUtility.default()
        transferUtility.uploadFile(localImageUrl, bucket: "", key: key, contentType: "video/mov", expression: expression, completionHandler: uploadCompletionHandler).continueWith { (task) -> AnyObject? in
            if let error = task.error {
                print("Error: \(error.localizedDescription)")
            }
            if let _ = task.result {
                print("Upload Starting!")
            }
            return nil;
        }
    }
Tribasic answered 10/8, 2019 at 17:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.