Well, after a slight hiatus, I spent a little time understanding how to use AWS Cognito in an application. I've now got a more or less running Cognito-as-STS-token-generator for the Apple Watch. Features:
- Wired to any or all of Amazon, Google, Twitter or Facebook identity providers
- Cognito processing occurs on the iPhone (hands STS tokens to Watch as Watch can't yet run the AWS SDK)
- Leverage Cognito's ability to 'merge' identities producing a single CognitoID from multiple identity providers
- Automatic refresh of identity access tokens
Here's the iPhone display showing all the identity providers wired:
Ok, the good stuff. Here what the access key flow now looks like:
There are a lot of actors in this play. They key actor for this article is the IdP. Here, as a slight generalization across all the IdPs, we have a token exchange system. The iPhone maintains the long lived IdP session key from the user's last login. Then, the iPhone performs has the IdP exchange the session key for a short-lived access key to present to Cognito. For IdP like Amazon and Google, the access key is only good for an hour and must be refreshed...
Let me say that again; today, we need to manually refresh this token for Cognito before asking Cognito for an updated STS token! Cognito can't do this! FYA read Amazon's description here:
"Refreshing Credentials from Identity Service"
Especially in our case, where our credentials provider (Cognito) is merely referenced by the other AWS resources, we need to intercept the Cognito call to make sure that on the other side of Cognito, the 'logins' are up to date.
So, I replumbed the code to do just this (the 'opt' section in the above diagram). Now, a user can log in once on the iPhone application and then each time the Watch needs a token, the whole flow tests whether or not an accessKey needs to be regenerated.
For reference, here's the known lifetimes of the various tokens and keys:
- The Watch knows its Cognito generated STS Token is good for an hour
- Amazon accessTokens are good for an hour (implied expire time)
- Google accessToken is good until an expire time (google actually returns a time!)
- Twitter doesn't have expire so its accessKey is unlimited
- Facebook's token is good for a long time (actually the timeout is 60 days)
- TODO: do any of the IdPs enforce idle timeouts? (e.g. a sessionKey has to be exchanged within a certain time or it is invalidated...)
So, with all these constants, and a little lead time, the Watch->iPhone->DynamoDB flow looks pretty robust. The current implementation is still limited to having the Watch ask the iPhone for the STS since I haven't figured out how to get the various SDKs working in the Watch. I don't want to rewrite all the IdP fetch codes, along with manual calls to Cognito.
Plus, I'm likely to move the AWS writes back to the iPhone as the Watch is pretty slow.
The code for this release is
here. The operating code is also in TestFlight (let me know if you want to try)
Known bugs:
- Google Signin may not work when the app is launched from the Watch (app crashes)
- Facebook login/logout doesn't update the iPhone status section
- The getSTS in Watch is meant to be pure async -- I've turned this off until its logic is a bit more covering of various edge cases.
- The webapp should also support all 4 IdP (only Amazon at the moment)