AmazonCognito

This information was last reviewed in July, 2018, by Scott Cantor.

Change Log:

7/30: Initial version.

This is not a replacement for the actual documentation and you cannot cut and paste your way to a working system. The examples are not usable without taking into consideration your local needs and requirements.

Amazon's Cognito service is a newish offering that's distinct from the "main" support Amazon Web Services offers for SAML integration. The configuration for that is totally distinct.

Cognito is their "application-level" IAM solution that allows local user pools to be defined, and supports federated login to user accounts in those pools. It's effectively a federation proxy from SAML or OIDC to an internal OAuth service that issues tokens to applications registered with the user pools.

The SAML-related documentation is mostly under https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-identity-federation.html but testing this really requires a bit of work with their OAuth support to at least mock up test cases.

Note that unlike AWS-proper, each Cognito User Pool is its own SP and is handled distinctly.

Identity Provider Metadata

Cognito is best described as ADFS-like. It supports https-based access to remote metadata and will apparenty poll that for changes and apply them. It does support multiple signing keys in the metadata, and seems to ignore at least basic extension content, so it's suitable for loading most Shibboleth IdP metadata. It does not verify the signature on a metadata file, and it doesn't have any logic for dealing with the validUntil attribute beyond perhaps checking it (and I didn't test that).

The only alternative to is to manually load metadata into it as a one-time step. So deployers have to judge whether they're comfortable with its trust model and who should be responsible for managing the metadata its using.

When you create an IdP, you also have to give it a "name" that is fairly important since it gets used as part of the usernames generated in the user pool, and using the entityID directly in most cases won't work. The scope of the name's usage is limited to that user pool but if you were supporting multiple IdPs at once, you would need to think about the naming convention to use.

Service Provider Metadata

They do not provide any SP metadata; it has to be manually pieced together from their documentation and that's somewhat scattered across different pages and you have to know what you're looking for.

Pulling bits out of the documentation, we find:

  • For some SAML identity providers, you also need to provide the SP Audience URI / SP Entity ID, in the form:

    urn:amazon:cognito:sp:<yourUserPoolID>
    

    You can find your user pool ID on the App client settings tab in the Amazon Cognito console.

  • You also need to provide an assertion consumer endpoint to your SAML identity provider. Configure this endpoint for SAML 2.0 POST binding in your SAML identity provider:

    https://<yourDomainPrefix>.auth.<region>.amazoncognito.com/saml2/idpresponse
    
  • Configure this endpoint for consuming logout responses from your IdP. This endpoint uses post binding.

    https://<yourDomainPrefix>.auth.<region>.amazoncognito.com/saml2/logout
    
  • If this option is selected and your SAML identity provider expects a signed logout request, you will also need to configure the signing certificate provided by Amazon Cognito with your SAML IdP.

So you can get everything required with some digging in the GUI, and you end up with the information to create the metadata.

The most confusing part is that "domain prefix" it mentions, which has to be defined ahead of time to control where some of the built-in pages Cognito serves up will live. It's possible to define a fully custom domain in your own DNS for this, but the normal way appears to be to supply a value that ends up in their DNS and that you can basically grab first-come, first-serve in the regions.

Profile Requirements

The SAML SSO profile behavior is fairly standard and relies on signed responses and no encryption. The former is the Shibboleth default and the latter would require setting the Idp.encryption.optional property or disabling encryption for the service.

Logout has not been tested.

Example Shibboleth Configuration

Refer to the RelyingPartyConfiguration topic and be cognizant that creating overrides for every service is generally an inefficient use of the software. Consider identifying common requirements across services and create overrides tied to multiple services that share those requirements, or that reference profile configuration beans containing common settings.

Example relying-party.xml override
	<!-- Container for any overrides you want to add. -->

	<util:list id="shibboleth.RelyingPartyOverrides">

		<!-- other overrides... -->

		<bean p:id="example.DisableEncryption" parent="RelyingPartyByName">
			<constructor-arg name="relyingPartyIds">
				<list>
					<value>urn:amazon:cognito:sp:us-east-2_poolid</value>
				</list>
			</constructor-arg>
			<property name="profileConfigurations">
				<list>
					<bean parent="SAML2.SSO" p:encryptAssertions="false" />
					<bean parent="SAML2.Logout" />
				</list>
			</property>
		</bean>

	</util:list>

Account Provisioning

The Cognito model largely assumes just-in-time provisioning during sign-in and I didn't observe any option to disable that behavior. Since licensing costs depend on active user count, that might require some IdP-based authorization in some cases.

More details below, but the system relies on both the <NameID> in the subject and on custom attribute mapping rules created in Cognito to control how user entries are created.

NameID Requirements

As with most vendor documentation, they are inaccurate regarding this piece. They do require a NameID and it's the basis of an auto-generated username value in the user pool. It does not have to be any particular Format, and they mis-speak by suggesting you should use "persistent". You certainly can, but they'll be visible in the pool and of course it means correlation between pools would not be possible. That may be good or bad, it just depends on your needs. If you want to use a more globally consistent value, you can, just pick a different Format.

Whatever you use, it will construct the eventual username key based on combining the IdP "name" with the NameID value with an underscore in between them. So it essentially auto-scopes the value with a prefix.

Having settled on a Format, make sure you add that to the Cognito metadata in a <md:NameIDFormat> element to trigger its use.

Attribute Requirements

Cognito essentially acts like a simplistic kind of user directory, and it supports a number of pre-defined attributes and allows custom fields to be created. A subset of them can be marked as "required" when the pool is created, but not changed afterward.

In turn, you can create mappings under the IdP definition that tell it which SAML Attributes to consume and map to each field in the user pool, and you have to create mappings for any required fields.