RFC: Auth Enhancements - Easier Federation with Cognito User Pools and Hosted UI
See original GitHub issueOverview: Based on feedback there is a fair amount of confusion when using User Pools for Social Provider Federation, especially when building a custom UI and bypassing the default Cognito Hosted UI. While Amplify documentation gives an overview of using redirects within the OAuth flow, this is cumbersome, error prone, and seen as difficult by customers who are unfamiliar with these flows. Therefore we wish to build this into Amplify’s Auth category via a simple, declarative API. There are several phases and potential future options with different trade-offs which are outlined below.
Please reply with a +1 or detailed comment on a feature if you have specific thoughts around how it should work.
Related Issues: https://github.com/aws-amplify/amplify-js/issues/1316 https://github.com/aws-amplify/amplify-js/issues/2644 https://github.com/aws-amplify/amplify-js/issues/2543 https://github.com/aws-amplify/amplify-js/issues/2525 https://github.com/aws-amplify/amplify-js/issues/2518 https://github.com/aws-amplify/amplify-js/issues/2512 https://github.com/aws-amplify/amplify-js/issues/2503 https://github.com/aws-amplify/amplify-js/issues/2456 https://github.com/aws-amplify/amplify-js/issues/2423 https://github.com/aws-amplify/amplify-js/issues/2283 https://github.com/aws-amplify/amplify-js/issues/2168 https://github.com/aws-amplify/amplify-js/issues/2156 https://github.com/aws-amplify/amplify-js/issues/2115 https://github.com/aws-amplify/amplify-js/issues/1585 https://github.com/aws-amplify/amplify-js/issues/1521 https://github.com/aws-amplify/amplify-js/issues/1392 https://github.com/aws-amplify/amplify-js/issues/1386 https://github.com/aws-amplify/amplify-js/issues/1281
Phase 1: Add User Pool Federation to federatedSignIn()
Amplify has an Auth.federatedSignIn()
method today which allows customers to pass tokens to Cognito Identity and retrieve AWS credentials, which are then used to sign requests (with Signature Version 4) to AWS services. Request signing happens automatically when using other Amplify categories.
UPDATE: A new value of federationTarget
will not be needed to the config. Instead in the design we are inferring the behavior from the existing configuration values.
Depending on the presence of Cognito User Pool or Identity Pool value passed to Amplify.configure()
, the call to federatedSignIn()
will use User Pools or Identity Pools as appropriate passing along the social provider token. In the case where both are present in your configuration, the social provider federation will go to User Pools, and the returned JWT token will be sent to Identity Pools thus federating the User Pool with that Identity Pool and providing AWS credentials to the caller automatically as well.
The existing behavior when only using an Identity Pool remains unchanged, and the only required arguments are the provider and token.
Auth.federatedSignIn(
'provider', //Facebook, Google, etc.
'token', //OAuth token from provider
'user', //Optional user attributes {username, phone}
'expiresIn', //Optional time to invoke refresh of provider OAuth token
'IdentityID' //Optional
);
UPDATED: When using User Pools, there are no arguments required and provider is optional. If a provider is not given the User Pool Hosted UI will be displayed. If a provider is given the page will redirect to the Hosted UI, but we’ll add a query string to signal an immediate redirect to the social provider’s login page (Facebook, G+, etc.). This allows you to build your own UI but still leverage the User Pool federation. In either case after the user signs in with that provider an account will be created in User Pools and a JWT token returned.
The user object contains information for you to use when calling Auth.currentSession()
and returns a CognitoUserSession object. If you do not pass this object we will attempt to automatically populate the name, email, and phone_number attributes from the social provider based on known mechanisms (for example - Facebook requires a call to ‘/me’ to retrieve details after authenticating) but if we are unable to retrieve the attributes we’ll populate them as UNDEF.
The expiresIn value controls when Amplify will attempt to refresh the token from the social provider, and when the time comes do so automatically behind the scenes. This value was previously required and is now optional as we will default by default attempt to retrieve the attribute from the social token, and if it does not exist we will set it to 1 hour. If you provide a value then Amplify will honor your provided time.
The IdentityID value is strictly for Cognito Identity Pools and allows you to set an Identity ID to retrieve credentials for a specific Cognito Identity ID: https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityCredentials.html#identityId-property
Implementation
Identity Pool federation will stay as-is today, with the only changes being automatic generation of expiresIn logic. For User Pools Amplify.configure()
will require domain, redirectSignIn, redirectSignOut, and responseType from https://aws-amplify.github.io/docs/js/authentication#configuring-the-hosted-ui. The scope will be optional (for discussion - should this move into the options object?). Note that in the future the Amplify CLI will set all this up see https://github.com/aws-amplify/amplify-cli/issues/766. Under the covers, we will manage the OAuth redirects on behalf of the developer such as the code below, which must be manually called today:
const {
domain,
redirectSignIn,
redirectSignOut,
responseType } = config.oauth;
const clientId = config.userPoolWebClientId;
// The url of the Cognito Hosted UI
const url = 'https://' + domain + '/login?redirect_uri=' + redirectSignIn + '&response_type=' + responseType + '&client_id=' + clientId;
// If you only want to log your users in with Google or Facebook, you can construct the url like:
const url_to_google = 'https://' + domain + '/oauth2/authorize?redirect_uri=' + redirectSignIn + '&response_type=' + responseType + '&client_id=' + clientId + '&identity_provider=Google';
const url_to_facebook = 'https://' + domain + '/oauth2/authorize?redirect_uri=' + redirectSignIn + '&response_type=' + responseType + '&client_id=' + clientId + '&identity_provider=Facebook';
// Launch hosted UI
window.location.assign(url);
// Launch Google/Facebook login page
window.location.assign(url_to_google);
window.location.assign(url_to_facebook);
We will provide a config option (or another mechanism) for customers to override what “opening a url” means in the different platforms (e.g. using Expo’s WebBrowser in React Native).
Potential future Option: Add Federation to signIn() and deprecate federatedSignIn() We are not doing this as part of this feature work but are including in the RFC to get early opinions for the future
One future option is to take all of the implementation and functionality as above, however we would move this into Auth.signIn()
instead of Auth.federatedSignIn()
.
PROS: Less APIs to learn, potentially simpler code that is standardized for many situations. Auth codebase could also have a “cascading rule set” depending on config leading to less logic branches.
CONS: This will take a larger effort as it introduces more code complexity into the existing signIn() method. There is also the fundamental question of whether or not the action of “federation” is actually a “sign-in” action (more below in Option 3). We would also need to deprecate the federatedSignIn() method requiring customers to update their code in the future if they upgrade versions. Perhaps the biggest issue though is that there is “no free lunch” and that while the code may become simpler from a customer perspective, you must push the logic somewhere and naturally that would go into Amplify.configure()
to know what actions to take. This ultimately means that finding the appropriate API setting will be less intuitive.
Potential future Option: Introduce a new API for Federation We are not doing this as part of this feature work but are including in the RFC to get early opinions for the future
All of the implementation and functionality as above, however we introduce a new API in Auth - Auth.XXX.
The reason for this discussion is should federation be an action tied to Login? The Wikipedia definition of federation (https://en.wikipedia.org/wiki/Federation_(information_technology)) has no terms around authentication but rather a “joining” or “association” of separate systems. In the Amplify case that can be either the joining of separate Identity Providers or it can be joining them to a single “logical identity”, ultimately all in order to provide authorization to resources (via OIDC tokens or AWS Credentials). This begs the question is there a more descriptive API such as simply Auth.federate() and, if one exists, is the name so much better that it makes performing these actions that much easier for customers?
Issue Analytics
- State:
- Created 5 years ago
- Reactions:72
- Comments:35 (20 by maintainers)
Top GitHub Comments
Cannot +100 this enough 🙏
Yes yes yes! This sounds awesome and will be really helpful. 😍😍😍