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)
})
}
}
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With