Question:
I’m trying to understand how to sign in with a developer authenticated identity using AWS mobilehub’s iOS SDK as recently AWS changed this SDK and I cannot find any documentation on this.
The sample app fails to shed any light on this.
I have a REST API to retrieve the cognito ID and Token but I’m not
sure what to do with them once I have this.
AWS has the following different classes that futher complicate the issue:
AWSCredentialsProvider,
AWSCognitoCredentialsProvider (No longer available in new SDK),
AWSSignInProvider,
AWSAbstractCognitoIdentityProvider (no longer available in new SDK)
There’s now something called AWSAbstractCognitoIdentityProviderHelper..
These are handled by the AWSIdentityManager which is part of AWSMobileHubHelper.framework however AWSIdentityManager doesn’t let you
set the credentials provider so I don’t understand how I am meant to
interact with it in this regard.
Any tutorials, documentation, etc. would be much appreciated
Answer:
Your question exposes certain terminology problems:
- When you say developer I am not sure you mean the same thing that AWS
means.- AWS use the term “developer” (as in “developer identities”) to mean
externally maintained pools as opposed to privately maintained. AWS
has it’s own product (Cognito User Pools) which you can privately
maintain as an AWS service. AWS does not consider user pools to be a
developer identity provider (but in practice, it is simply a fully implement renamed version of a developer identity provider).
- AWS use the term “developer” (as in “developer identities”) to mean
- Regarding maintaining tokens
- The iOS SDK maintains the tokens you need for access to AWSServices.
You should use the Mobile Hub, (it has a much nicer object and
interface design than the SDK). But regardless of whether you use
mobile-hub-helper or the SDK directly, you never have to manage a
token (the SDK does it for you). The documentation (almost cruelly) obscures this fact (and yes, it is lagging behind the SDK).
- The iOS SDK maintains the tokens you need for access to AWSServices.
3 ways to use Cognito
You must understand that there are 3 different interface apis.
- COGNITO API and up to date API documentation (the RESTFUL interaction)
- iOS SDK and out of date SDK documentation (the SDK is not RESTFUL, it has ton’s of state).
- Mobile Hub Helper (MHH) SDK – MHH is documented (a little) by the hub, and pretty well in the .h files used to produce appledoc documentation.
With respect to Identity and SignIn/Authentication (the topics of this question) aws-mobile-hub-helper (hereafter MHH) has an elegant design and works well. I would recommend anyone using Cognito start with the Mobile Hub site (or at least with aws-mobile-hub-helper). The MHH is basically a wrapper to the SDK and helps clarify and separate the issues of persistent federated identity and credentials/authorization for AWS services from issues of Identity, authentication and attributes/claims for that Identity.
- Identity in MHH has only 1 class AWSIdentityManager.
- SignIn in MHH has one protocol, AWSSignInProvider, and two implementations of that protocol (plus one I made):
- AWSGoogleSignInProvider: OpenID-Connect/OAuth implementation of
AWSSignInProvider for Google+ - AWSFacebookSignInProvider: An OAuth/Proprietary implementation of AWSSignInProvider for Facebook
- AWSCUPIdPSignInProvider: OpenID-Connect/OAuth implementation of
AWSSignInProvider for Amazon AWS Cognito Your User Pools service (this is available on a forked repository)
- AWSGoogleSignInProvider: OpenID-Connect/OAuth implementation of
The mobile-hub-helper is documented only in the .h files. These can be processed into documentation by appledocs, and the comments there are pretty good if you had an overview of the class structure (which does not exist but I will attempt to provide).
SDK Authentication Flow
The authentication flow documented by AWS, is an oversimplification and does not aid in understanding how the authentication is accomplished using the SDK and Mobile Hub Helper. The following diagrams attempt to convey how identity authentication(login) and authorization(credentials) to use AWS Services (like S3, and DynamoDB) works.
Understanding Cognito
- Understanding Cognito is initially confusing for a variety of
reasons: Authentication, Authorization and Identity Management in a
distributed system is complex. There are many parties with different
roles and elaborate prescribed interactions with keys, tokens and
signatures. The end user, the relying party (RP) the Identity
Provider (IdP) the resource being used (RS) and the resource owner
(RO). This terminology is used by the OpenId Connect and OAuth2.0
standards documents. For reasons that will become clear, this
terminology is not consistently used by AWS. But they concepts and
the entities are all there when using Cognito.- There are some good OpenID connect overviews online (ex: http://nordicapis.com/api-security-oauth-openid-connect-depth/ ) and
a review of those will help.
- There are some good OpenID connect overviews online (ex: http://nordicapis.com/api-security-oauth-openid-connect-depth/ ) and
- Cognito allows non-OpenID Connect Identity Providers, this is
an advantage (ex: allowing the OAuth/Proprietary Facebook identity
API) but it also means that Cognito is playing a “federating”
role. This role is outside the scope of OpenID Connect standards
documents (Update 1: Lately I have begun to wonder whether the
Cognito Credentials Provider is really the RP (Relying Party) which
then issues credentials for AWS Services. But the main point here is
that OpenId Connect does not prescribe the way that Identities from
different IdP’s could be combined.), Amazon are essentially inventing
the role, and in doing so have had some naming challenges.
Cognito Naming
- Cognito is a single name created by AWS to cover many functionalities
and roles.- There is the RESTFUL web API to Cognito, but there is also the
Cognito SDK. The SDK calls and the API messages are not named the
same, and SDK calls make multiple and conditional API calls. - Cognito can federate identity providers. It can persist and
association betwene authenticated users from different identity
providers (So it can remember your google+ and your facebook
identities and associated them with a single Cognito identityId.) - Cognito can provide persistent identityId (which, if anonymous,
follow an iOS device using keychain data) for users as well as
authenticated users. These are stored in what is called an
Identity Pool (NOT to be confused with a User Pool). Your app
receives the same identityId for a user on different devices for
authenticated users. Unauthenticated (Guest) identityId’s follow a
single device. - Cognito can store (known as “Sync”) state data IdentityId’s (on
the AWS server), which works for authenticated and unauthenticated
users. - Cognito has a AWSCredentialsProvider (a source for AWS Credentials
for using AWS Services (Cognito but also S3, DynamoDB, etc) - Cognito can create an OpenID Connect server called a User Pool,
which can be used by Cognito Identity to Authenticate users. - Cognito is new, but AWS Federated Identities and AWS Identity
Management and AWS Credentials are not, so there are lots of classes
with overlapping responsibility. And the naming conventions are
confusing (consider the name
AWSCognitoIdentityCognitoIdentityProvider! ). The use of the “cognito”
brand name for userpools, really is a nightmare. An
AWSCognitoIdentity thing is Cognito Federated Identity CFI but an
AWSCognitoIdentityProvider thing is a thing like userpools an
authentication provider also called an identity provider.
- There is the RESTFUL web API to Cognito, but there is also the
- SDK class names are confusing. But with few exceptions, classes starting with AWSCognitoIdentity (but NOT AWSCognitoIdentityProvider) are about the credentialsProvider/IdentityProvider, classes starting with AWSCognitoIdentityProvider relate to Oauth/Open Id Connect providers and other distributed identity providers (facebook).
Glossary/Synonyms
These terms are used loosely throughout the AWS documentation and marketing material. This is an attempt to sort out the terminology by grouping terms that are used interchangeably by AWS.
- Identity provider, authentication provider, Login provider, federated
identity provider(s) - Amazon Cognito, Cognito credentials provider, cognito identity (all
seem to refer to the same class/process) - Cognito user pool, Cognito Your User Pools, user pool. CUP is an identity provider aka authentication provider
- Cognito identity pool, pool, cognito pool, identity pool. Occasionally called an identity provider (which seems incorrect) but it is never called an authentication provider
- Developer identity, developer authenticated identities, developer
provider, developer identity provider, all used to refer to private external Identity Providers. - Identity is a term often misused in Cognito documentation. It is important to understand there are two different kinds of identity that Cognito manages. identityId (which should be in lower case) is the persistent unique name that Cognito associates with credentials and uses to federate different Identity providers, and Identity (upper case) which is an authenticated identifier from an Identity Provider.
- identityId Identity ID, id (as in get-id), identity, identityId
- Identity
- Federation means multiple things.
- Web identity federation – an earlier way of federating identity at AWS
- Cognito federated identities
- BYOI (bring your own idenity) where a user may use google, facebook or another identity provider (perhaps a developer provided identity) usually through OpenId-Connect.
IdentityId Behaviors
- An identity id looks something like this:
us-east-1:982396fs-841e-3cdd-9r43-e7ac41bhbcb28 - The identityId is maintained on an iOS device in a keychain entry.
For an unauthenticated IdentityId it remains the same until you clear
the keychain (This can be done in simulator by Simulator -> Reset
Content and Settings…). At that point that IdentityId is abandoned.
It is not disabled, it is just never used again. - When the user authenticates, authenticating disables the unauthenticated identityId (the
identityId will be permanently marked with DISABLED in the Logins array in the
identityPool entry. You can see this in the Cognito console.) that is
currently on the device. There is one exception: If this is the first time the
authentication takes place for this Identity then the unauthenticated identityId is not abandoned but
is associated with the Identity and used as the authenticated
identityID going forward. - Merging multiple Identities (meaning usernames not IdentityId’s) from
different Identity providers abandons (disables) one of the identityId’s, and
associates both Identities with the other identityId. Disabled
Id’s get created whenever this happens. These abandoned identityId’s are marked with DISABLED in the Logins array in
the cognito identityPool. - In practice this process creates a reasonable use of unique identityIds with
disabled ones only getting created when a user authenticates on a new
device (It can be bothersome in testing as it creates a barrage of
disabled and unused identityId’s as the tester logs out and in
multiple times with multiple id’s). But in practice the common use case would not create this barrage of disabled identityIds. A user would: - Connect – get an unauthenticated id – authenticate – and use the same
ID. No abandoned id is created. - Connect on another device – here he/she would momentarily get a new
unauthenticated id – and when he/she authenticated and got the
identityId for his/her identity, that unauthenticated id would be
disabled and abandoned. - Each merging of identities from two identity providers would also
create a disabled and abandoned identityId.
AWSIdentityProviderManager
- AWSIdentityProviderManager is the protocol that manages federated AWSIdentityProviders
- In mobile-hub-helper AWSIdentityManager is the AWSIdentityProviderManager
- All it needs to do is return to credentials provider a logins
dictionary, with providers name and ID Token. AWSIdentityManager
only returns the providername and token for a single identity
provider. It simply gets the name and token from the
AWSSignInProvider and returns. (There is a fork with a modification
that adds the ability to return all of the current logged in
providers in the logins dictionary.) - As modified AWSIdentityManager maintains an NSDictionary
called cachedLogins. Each new login adds an login (an identity
provider name and id token) to the cache. Then logins always returns
the whole loginCache. This is what supports identity merging.
- All it needs to do is return to credentials provider a logins
- When the credentials provider calls it’s associated
AWSIdentityProviderManager logins method, and finds a list of
logins instead of just one it will merge the identityId’s for those logins
in it’s database and disable the identityId of one of them. How does
it know which ID goes with which login? The ID Token contains an
encoded decryptable (paste the token into https://jwt.io to see for
yourself) set of claims, one of which is the identity (ex: username) - Note: Even though you have an identityId that has multiple related logins, in Mobile Hub Helper you are only ever authenticated by one AWSSignInProvider. Credentials get associated with the merged identityId, but in mobile-hub-helper access to that identityId is always via the active AWSSignInProvider (authentication provider) even if you are logged with multiple identity providers. Your app can keep track of all of the AWSSignInProviders and access them independently of AWSIdentityManager, but from AWSIdentityManagers point of view you are logged in with one of them. In practice this has little impact (until you try to get “claims” like imageURL from different providers for instance).
About Merging Identities
- Currently the AWSIdentityManager does not support identity merging. I have a forked repository https://github.com/BruceBuckland/aws-mobilehub-helper-ios from the github repository that adds that capability, and adds a Cognito User Pools Identity Provider AWSSignInProvider (AWSCUPIdPSignInProvider.swift).
- You can probably think of all sorts of gotcha’s when merging
identities. - What if I try to merge two identities from the same provider
(wouldn’t the dictionary keys be the same?) - What if I try to merge two identities, each of which has a different
identity from the same provider associated with it (and again they
would create two entities with the same keys). - Cognito manages this beautifully and rejects attempts to merge
identites that cannot be merged. The rejection happens at login time
(when you would try get credentials, the credentials provider will
reject the logins dictionary that contains an un-mergeable
identityId)
Where Cognito buries its data
- Cognito stores a keychain on the device that contains the last identityId that was used. This is used by the credentialsProvider/identityProvider object upon a call to credentialsProvider.credentials (iOS SDK name) to re-use an existing identity (for example unauthenticated) and avoid creating unused identities unless the user truly is not going to log in or resume.
- Mobile-Hub-Helper’s AWSSignInProvider’s and AWSIdentityManager store an indication of an open session state in NSUserDefaults. These are used to re-start the session if the app is terminated and restarted.
- AWSSignInProvider’s store NSUserDefaults too, and sometimes in the iOS Keychain, for their own internal purposes (like retaining easy persistent access to a username or imageURL or token)