iOS Swift, Aws Cognito User Pool, Unable to Refresh Access Token

AWS Cognito integration swift3 Refresh provides ResourceNotFoundException

Few things that made it work I think.

I made the correct move by making my own identityprovidermanager and I think the main thing that was blocking me from executing a lambda method was actually the fact that I was using AWSLambda instead of AWSLambdaInvoker. After I switched it started making errors that made sense.

Cognito User Pool: How to refresh Access Token Android

When you call getSession(...) - to get tokens - and if the cached tokens have expired, the SDK will automatically refresh tokens (as long as the refresh token has not expired). If the refresh token too has expired, then getAuthenticationDetails(...) is invoked because now the user credentials (username, password, etc) are required to get new set of tokens. It should not matter how you get the user object, i.e. through getCurrentUser() or getUser(...) methods, as long as there are valid cached tokens or if the tokens can be refreshed, you will get valid tokens with getSession(...).

Retry with the latest SDK (ver 2.3.1).

How to get an Apple refresh token from Cognito?

AWS Support said "If you are using Authorization Code grant then refresh token will be generated once the flow is completed. The Authorization code grant flow initiates a code grant flow, which provides an authorization code as the response. This code can be exchanged for access tokens with the TOKEN Endpoint. Because the tokens are never exposed directly to an end user, they are less likely to become compromised." and referenced me here:

https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-user-pool-oauth-2-0-grants/

AWS Cognito User Pools in iOS (Swift) app

Update 6: (and really final this time )

It is worth mentioning that (finally) AWS has made the AWS Mobile Hub build a very nice demo app that INCLUDES User Pools as a SignInProvider (With Google and Facebook too). The architecture is (in my opinion) excellent (they have separated Identity Management and getting credentials from Authentication)
Check it out

Update 5: (and final)

There is a fairly complete example implementation, and some documentation of how it works in this other answer.

iOS - AWS MobileHub sign in with developer authenticated provider

Update 4:

If you want to get access to AWS services, there are more steps needed

It turns out that this does not get you authenticated with Cognito Federated Identities (the "logins" count on the identity browser remains at 0). To fix this you need to establish a credentialsProvider and do "credentialsProvider.getIdentityId". After that logins will show positive, and you can get services from AWS based upon your authenticated role.

If you are trying to do both Authenticated and UnAuthenticated access for your mobile app, then you need to create an AWSAnonymousCredentialsProvider (in a separate service configuration). Then you self.credentialsProvider?.invalidateCachedTemporaryCredentials() and self.credentialsProvider?.clearCredentials() when logging out and do the getidentityid again with the anonymous service configuration and you will get an anonymous id. (Note: I found it seemed like if you clearkeychain on the credentialsProvider it starts with a new id each time a user logs out, which could burn up your free 50,000 ids pretty quick. )

Update 3:

Uploaded a github sample app for AWS User Pools for IOS in Swift.

https://github.com/BruceBuckland/signin

Update 2:

I finally got AWS User Pools to work correctly in Swift

My problem was that each time the authentication start happened it was caused by an authentication failure in a different viewcontroller (my error). I ended up with a bunch of them running waiting for completion returns which never came and the API was "silent" (showed no errors). The API does not notice that it is being initiated multiple times ( by a different viewController each time) so it silently lets log in over and over. There is not enough of your code in the original post to see if you are having that same issue.

You have to be careful, the AWS sample code (in Objective-C) has two navigation controllers, and the code re-uses them. I don't like the way the sample app flashes the logged in view controller before the authentication delegate gets going and I was trying to improve that in the swift version and that caused my problem.

AWS User Pools API is set up to work with a storyboard or app structure that works like this:

1) Your app ASSUMES it is logged in, and then triggers the delegate which triggers authentication and the login screens if it is not.

2) In original logged in view controller pool.currentUser() is NOT enough to get the authentication going, API will only trigger the delegate when you do more (in my case user.getDetails()).

3) The authentication is completed through the didCompletePasswordAuthenticationStepWithError. This delegate method is called if you get an authentication (or other) error AND if you SUCCESSFULLY authenticate. In the case of successful authentication the NSError is nil, so it should be declared as NSError? in the delegate (this causes a warning). The API is beta, they will probably fix this.

4) One other little “gotcha”, it may be obvious to you, it caught me, when you define your User Pool in the console you specify allowed apps, and each of these apps HAS DIFFERENT STRINGS for Client ID strings. ( I was just plugging the same thing into the example) which works badly (but does not report errors). The API needs some work in the reporting department. It is very Verbose when it is working, but says nothing if you pass it the wrong Client Strings. Also it seems to say nothing if you (like I did) call the API from different viewcontrollers. It was just taking each new authentication request from a different viewcontroller and saying nothing.

Anyway, it works now. I hope this helps resolve your issue.

Update:

I finally got getPasswordAuthenticationDetails to execute.

It turns out it does not get executed until user.getDetails for the current user (even if there is no current user).

So

let user = appDelegate.pool!.currentUser()
let details = user!.getDetails()

will result in the getPasswordAuthenticationDetails callback getting executed on the second line.

It seems the AWS UserPool concept is that we write an app that assumes we have a logged in user. We get details from that user (for instance in the initial view controller) and the delegate gets kicked off if we don't have a user.

The AWS documentation for User Pools on IOS is missing some important concept pages. Those pages ARE included in the (otherwise parallel) Android documentation. I admit that I am still struggling (days now) with getting User Pools to work in swift, but reading the "Main Classes" and "Key Concepts" Parts of the Android documentation clarified a lot for me. I can't see why it was omitted from the IOS doc.

Unable to mutate user attributes using iOS Cognito UserPool SDK after signup

So, my approach will not work, as it requires me to also create an identity pool as well. If you already have an identity pool, then the above approach might work but its a bit dirty/unsecure-ish as you are forcing your way as an admin to do something very simple.

I found a better solution thanks to this issue and this resource.

Here is the same answer in swift 4 syntax.

let attr = AWSCognitoIdentityUserAttributeType.init(name: "given_name", value: "TEST")

user?.update([attr]).continueOnSuccessWith(block: { (response) -> Any? in
// Was successful, do any changes, UI changes must be done on the main thread.
return nil
})

You can also update custom attributes this way as well. Just make sure that when you add a custom attribute via the aws panel, you go into your AWS Cognito UserPool. Go to General Settings -> App Clients -> Click on "Show Details" -> Click on "Set attribute read and write permissions" and specify appropriate read and write permissions for your newly created custom attributes.

When updating custom attributes, you only need to include the custom tag before the attribute name. So, for example, an attribute called school would be created as follows.

let attr = AWSCognitoIdentityUserAttributeType.init(name: "custom:school", value: "Junior High")


Related Topics



Leave a reply



Submit