Search code examples
iosobjective-ciphoneamazon-web-servicesaws-mobilehub

iOS - AWS MobileHub sign in with developer authenticated provider


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


Solution

  • 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).
    • 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).

    3 ways to use Cognito

    You must understand that there are 3 different interface apis.

    1. COGNITO API and up to date API documentation (the RESTFUL interaction)
    2. iOS SDK and out of date SDK documentation (the SDK is not RESTFUL, it has ton's of state).
    3. 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):
      1. AWSGoogleSignInProvider: OpenID-Connect/OAuth implementation of AWSSignInProvider for Google+
      2. AWSFacebookSignInProvider: An OAuth/Proprietary implementation of AWSSignInProvider for Facebook
      3. AWSCUPIdPSignInProvider: OpenID-Connect/OAuth implementation of AWSSignInProvider for Amazon AWS Cognito Your User Pools service (this is available on a forked repository)

    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.

    Cognito SDK Authentication Flow (Single Identity Provider) Cognito SDK Authentication Flow (Multiple Identity Provider)

    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.
    • 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.

      1. 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.
      2. 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.)
      3. 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.
      4. Cognito can store (known as “Sync”) state data IdentityId’s (on the AWS server), which works for authenticated and unauthenticated users.
      5. Cognito has a AWSCredentialsProvider (a source for AWS Credentials for using AWS Services (Cognito but also S3, DynamoDB, etc)
      6. Cognito can create an OpenID Connect server called a User Pool, which can be used by Cognito Identity to Authenticate users.
      7. 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.
    • 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.

    • 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)