AWS iOS SDK Cognito Developer Authentication (Swift)

▼魔方 西西 提交于 2019-12-02 21:23:31

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.

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
}

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;
        }
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!