Validate AWS Cognito Token with Swift - swift

I have nearly completed the process for a developer authenticated sign in using AWS. I cannot seem to authenticate the back-end token that I receive and cannot seem to find any current implementations that are performing developer authentication via a third-party back-end. The error that I get is listed below.
As of right now my code looks like this:
Class containing Custom identity provider:
import Foundation
import AWSCognitoIdentityProvider
class CustomIdentityProvider: NSObject, AWSIdentityProviderManager {
var tokens: [NSString: NSString]?
init(tokens: [NSString: NSString]) {
self.tokens = tokens
}
#objc func logins() -> AWSTask<NSDictionary> {
return AWSTask(result: tokens! as NSDictionary)
}
}
AWS-APIManager.swift {snippet}
/* obtained cognito token from my back-end via getOpenIdTokenForDeveloperIdentity*/
/* From here I my app receives an IdentityId and Token */
let client_cognito_id = String(describing: valid_response)
let session_token = json.dictionaryValue["Token"]!
let login_with_amazon = NSString(string: "cognito-identity.amazonaws.com")
let token = NSString(string: String(describing: session_token))
let customProviderManager = CustomIdentityProvider(tokens: [login_with_amazon: token])
let credentialsProvider = AWSCognitoCredentialsProvider(
regionType: self.AWS_REGION,
identityPoolId: "us-east-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
identityProviderManager: customProviderManager
)
credentialsProvider.setIdentityProviderManagerOnce(customProviderManager)
credentialsProvider.getIdentityId().continue ({ (task: AWSTask!) -> AnyObject! in
if (task.error != nil) {
print("Error!!!: " + (task.error?.localizedDescription)!)
} else {
// the task result will contain the identity id
let cognitoId = task.result
print(cognitoId)
print("SUCCESS!!!")
}
return nil
})
}
For some odd reason odd reason I can cannot authenticate the token that I have received. I get an error "Invalid login token. Can't pass in a Cognito token.". I've tried to follow the documentation and piece together working code that I have found literally hundreds of sources and cannot seem to be able to move past this part of the authentication process. Any help would be greatly appreciated. Thanks!

I believe the issue here is that although you are supplying the token, you are not setting the identity id that you are getting from your backend. As such, it is calling GetId with a Cognito OpenIdConnectToken, which is not supported.
The simplest client implementation of Developer Authenticated Identities is to extend AWSCognitoCredentialsProviderHelper
Apologies for providing this in Objective C instead of Swift. In your implementation just override the token method.
- (AWSTask<NSString *> *)token {
//get the identity id and token from your server
//You can use AWSTaskCompletionSource if you don't have it and need to get it asynchronously.
//Once you have this information, simply set the identity id and return the token
self.identityId = identityId;
return [AWSTask taskWithResult:token];
}

Related

AWS Cognito: developer authenticated identities problem in log in and refresh token

I implemented a social network app that uses Cognito for refreshing token. But still, I can't get my new tokens. When I first log-in to my server and get my first ID and token from it, The token expires after a while and I can't get any token. This is my implementation:
class DeveloperAuthenticatedIdentityProvider : AWSCognitoCredentialsProviderHelper {
override func token() -> AWSTask<NSString> {
self.identityId = ProfileDAL.shared.getId()
return AWSTask(result: NSString(string: ProfileDAL.shared.getToken()))
}
override func logins() -> AWSTask<NSDictionary> {
return super.logins()
}
}
I put this lines in my viewDidLoad right after getting first token from my server
let devAuth = DeveloperAuthenticatedIdentityProvider(regionType: MY_REGION, identityPoolId: MY_IDENTITY_POOL_ID, useEnhancedFlow: true, identityProviderManager:nil)
let credentialsProvider = AWSCognitoCredentialsProvider(regionType: MY_REGION, identityProvider:devAuth)
let configuration = AWSServiceConfiguration(region: MY_REGION, credentialsProvider:credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
and right after them, I use bellow lines to get my tokens:
AWSMobileClient.default().getTokens { (tokens, error) in
if let error = error {
print("Error getting token \(error.localizedDescription)")
} else if let tokens = tokens {
print(tokens.accessToken!.tokenString!)
}
}
finally I can't get my new tokens and it gives me this error:
AWSMobileClientError
▿ notSignedIn : 1 element
- message : "User is not signed in, please sign in to use this API."

AWSCognito Facebook Login Swift

I'm trying to use AWSCognito to sign in with a Facebook Access Token.
My identity pool is set up & I have the Facebook Access Token.
Unfortunately, the documentation is very limited for this. Has anyone successfully got a Facebook user logged in with AWSCognito?
So far I have;
let provider: Dictionary = [AWSIdentityProviderFacebook : accessToken.authenticationToken]
let credentialProvider = AWSCognitoCredentialsProvider(regionType: .EUWest2, identityPoolId: POOL_IDENTITY_STRING)
I'm assuming I need to pass-in the provider somewhere.
I tried to do this;
credentialProvider.identityProvider.logins() = provider
but I'm getting: "Cannot assign to value: function call returns immutable value"
Yes, I did something just like this recently.
You will want to init your credential provider using an identity provider manager.
credentialsProvider = AWSCognitoCredentialsProvider(
regionType: xxx,
identityPoolId: yyy,
identityProviderManager: idpm)
However, you can't just pass in the dictionary ["graph.facebook.com" : <access token>] as the third parameter.
You need something that conforms to the AWSIdentityProviderManager protocol, which basically just returns a task returning a dictionary.
I ended up making a simple wrapper class for this, see below.
import AWSCognito
class myIdentityProvider: NSObject, AWSIdentityProviderManager {
var keytokens: [String:String]?
init (logins: [String:String]) {
keytokens = logins
}
func logins () -> AWSTask<NSDictionary> {
let task = AWSTask(result: keytokens as AnyObject?)
return task as! AWSTask<NSDictionary>
}
}
So, then your code would be rewritten:
let provider: Dictionary = [AWSIdentityProviderFacebook : accessToken.authenticationToken]
let idpm = myIdentityProvider(provider)
let credentialProvider = AWSCognitoCredentialsProvider(
regionType: .EUWest2,
identityPoolId: POOL_IDENTITY_STRING,
identityProviderManager: idpm)

Microsoft Azure mobile SDK custom provider login IOS

I have followed this tutorial to implement a login with custom provider on an Azure mobile app. The backend works perfectly but when I try to login to my new custom controller I'm not able to do it. It's possible to implement it with Xamarin and also with java Android but is no way to do it with Objective C or Swift.
The object MSClient on Microsoft Azure mobile SDK only has two login implementations.
I have tried both but without luck, the callback always returns an empty client.
I also have tried to store the token created by own API use it for login call but without luck again.
Here is my Swift code:
let client = MSClient(applicationURLString: "https://myApp.azurewebsites.net")
client.login(withProvider: "custom", urlScheme: "myApp", parameters: ["username": "pau", "password": "123456"], controller: self, animated: true) {user, error in
print("USER", user)
print("ERROR", error)
}
We found a solution, it's really easy but the msclient documentation is not clear enough. You just need to pass whatever you need (i.e: username,password) as dictionary in token parameter.
Client.msClient.login(withProvider: "auth", token: params, completion: {user, error in
print("USER", user)
print("ERROR", error)
print("USER ID", user?.userId)
print("TOKEN", user?.mobileServiceAuthenticationToken)
if let user: MSUser = user {
guard let username: String = user.userId else { return }
guard let token: String = user.mobileServiceAuthenticationToken else { return }
Client.username = username
Client.msClient.currentUser = user
completion(true)
}else {
completion(false)
}
})

How do I get my AWS Cognito access token for updating user attributes?

I have the structure set up for updating user attributes, in this case the preferred username to use as an alias for signing in.
var attributes = [AWSCognitoIdentityUserAttributeType]()
let prefUsername = AWSCognitoIdentityUserAttributeType();
prefUsername?.name = "preferred_username";
prefUsername?.value = usernameField.text!;
attributes.append(prefUsername!);
let attributesRequest = AWSCognitoIdentityProviderUpdateUserAttributesRequest();
attributesRequest.userAttributes = attributes;
idProvider?.updateUserAttributes(attributesRequest)
Only thing I have no idea how to do is get the access token. I've looked in as much documentation as I could think of but I had no luck finding place to get access token.
You can use the api to initiate auth and get an AccessToken from the AuthenticationResult.
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_InitiateAuth.html
/// Function to retreive the current token for the logged in user.
///
/// - Parameter completion: A completion block with an error or the token. Called back on the main thread.
public func getJWT(completion: #escaping((_ error: Error?, _ token: AWSCognitoIdentityUserSessionToken?) -> Void)) {
guard let user = self.pool.currentUser() else {
let nsError = NSError(domain: "JWT Error", code: 500, userInfo: ["message": "No Logged in user"])
completion(nsError, nil)
return
}
user.getSession().continueWith { (task) -> Any? in
DispatchQueue.main.async {
if let error = task.error {
completion(error, nil)
}else if let token = task.result?.idToken {
completion(nil, token)
}else {
completion(nil, nil)
}
}
}
}
Where self.pool is the AWSCognitoIdentityUserPool you hopefully set up correctly.
You would have to authenticate first to establish a session with Cognito User Pools. That session would contain an access token which you can then pass to every subsequent request. I see you are using the low level SDK methods. Here is a sample in swift for SignIn:
https://github.com/awslabs/aws-sdk-ios-samples/tree/master/CognitoYourUserPools-Sample/Swift

How to link Cognito Identity ID with User Attributes?

I am trying to create a user login system for an iOS application written Swift 3. With the code below I get the unique Cognito Identity ID for the user in my User Pool but I am not sure what to do with this ID. Can I link it to the user and get the attributes associated with that user?
Code:
#IBAction func loginPressed(_ sender: Any) {
user = self.pool!.getUser(usernameTextField.text!)
user?.getSession(usernameTextField.text!, password: passwordTextField.text!, validationData: nil).continue({ task in
if let err = task.error { // some sort of error
print("LOGIN FAILED")
print(err)
//print(err.userInfo["message"] as! String)
}
else { //Successful login!
// this gets our token from the User Pool
let ret = task.result! as AWSCognitoIdentityUserSession
let myToken = ret.idToken?.tokenString;
print("Token: ", myToken);
let customcedentialProvider = AWSCustomIdentityProvider(tokens: [AWSCustomIdentityProvider.CognitoTokenKey : myToken!])
let credentialsProvider = AWSCognitoCredentialsProvider(regionType: CognitoConstants.COGNITO_REGIONTYPE, identityPoolId: CognitoConstants.COGNITO_IDENTITY_POOL_ID, identityProviderManager: customcedentialProvider)
let configuration = AWSServiceConfiguration(region: CognitoConstants.COGNITO_REGIONTYPE, credentialsProvider:credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
// wipe cached creds
credentialsProvider.clearKeychain()
credentialsProvider.clearCredentials()
// hit it
credentialsProvider.getIdentityId().continue({ (task: AWSTask!) -> AnyObject! in
if (task.error != nil) {
print("Error: ")
} else {
// the task result will contain the identity id
let UserIdentityID = task.result as String? // Im saving user identity id in constant variable called "kUserIdentityID"
print(UserIdentityID)// my identityID
print(credentialsProvider.identityId)// my identityID
}
return nil
})
}
return nil
})
}
Output (Identity ID):
Optional("us-east-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
Thanks!
Cognito UserPool is an "identity provider" like Facebook, Google etc. Whereas Cognito Federated Identity helps to keep the mappings between different logins to one unique id, which is often called "Cognito Identity ID". Typically you store and retrieve data inside datasets under Cognito Federated identity. And sync those for multiple logins. If I understand your question, you may want to store the userpool "username" in the above mentioned dataset so that you can retrieve it and query the userpool
The self.pool refers to your Cognito User Pool. User pools have at least a username and password, but usually also other attributes, which you can fetch using user.getDetails. A user pool is an IdP (an Identity Provider).
The identityId is a Cognito Identity concept which primarily has the purpose of providing a unique id for one or more identities from IdP's.
Don't worry about being confused. Cognito is very confusing, I found it so confusing that I wrote up a little powerpoint presentation from my notes. Here is a link to a diagram that should help you.Diagram of Cognito
Also, I would like to suggest that you use the AWS Mobile Hub Helper as a starting point. It will download a swift code sample app. And the sample code uses the aws-mobilehub-helper-ios wrapper which simplifies a lot of the SDK and makes it more rational. The downloaded code is Swift2 but it is not too hard to get running in swift 3.