Amplify-js: Identity providers authentication against User Pools WITHOUT hosted UI

Created on 26 Jul 2018  Â·  74Comments  Â·  Source: aws-amplify/amplify-js

Allow for custom UI (self-hosted, not the poorly customizable hosted UI) when using User Pools.

Currently, I have to redirect my customers to a page I have 10% control over how it looks. I can't change the font or the layout. I'm using react and have already built my desired flow. I only want to be able to get a JWT from Cognito for a person who decides to signup with Facebook or Google.

If I use Federated Identities I get a Facebook token. If there is a way to then "exchange" that for a JWT issued by Cognito I'll be happy as well.

Cognito feature-request

Most helpful comment

@martimarkov we find a solution for you to use the customized button to do that. You can construct a url like https://the-app-domain/oauth2/authorize?redirect_uri=callback_url&response_type=token&client_id=the_web_client_id&identity_provider=Facebook. This url will directly take you to the facebook login page and after login successfully, the browser will redirect to the callback_url. Then you can parse the callback url to get the access_token and id_token. We will also bring this into Amplify library in the future.

All 74 comments

Hi @martimarkov

Currently it is not possible, but the good news is that this is currently in the roadmap. Please stay tuned!

I did find I workaround by copying the code from the hosted us version and listing for the events using Hub.

Thanks,
Marti Markov

On 26 Jul 2018, at 20:01, Manuel Iglesias notifications@github.com wrote:

Hi @martimarkov

Currently it is not possible, but the good news is that this is currently in the roadmap. Please stay tuned!

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

@martimarkov we find a solution for you to use the customized button to do that. You can construct a url like https://the-app-domain/oauth2/authorize?redirect_uri=callback_url&response_type=token&client_id=the_web_client_id&identity_provider=Facebook. This url will directly take you to the facebook login page and after login successfully, the browser will redirect to the callback_url. Then you can parse the callback url to get the access_token and id_token. We will also bring this into Amplify library in the future.

@powerful23 does it work for other providers too ? (google, amazon )

@akash87 It works for google but not sure for Amazon because I didn't add it as one of the providers in my Cognito User Pool settings.

This is great, thanks!!

@powerful23 What do I do with the response from https://the-app-domain/oauth2/authorize?redirect_uri=callback_url&response_type=token&client_id=the_web_client_id&identity_provider=Facebook ?

Do I call federatedSignIn ? Do I pass the access_token or the id_token?

Hi! Thanks for the great work around it works well. @peetss yes you need call the federatedSignIn first and after navigate to the custom url and you will get the JWT tokens.

I've only one question. How do you refresh the token after you get that, because It does not contain any refresh token? @powerful23 Could you give me a hint please?

Thanks again!

@peetss for now it's not completely integrated into the library but you can use a work around like:

import { CognitoAuth } from 'amazon-cognito-auth-js';

const params = {
                    ClientId,
                    UserPoolId,
                    AppWebDomain,
                    TokenScopesArray,
                    RedirectUriSignIn,
                    RedirectUriSignOut,
                    ResponseType,
                    Storage
}
const cognitoAuthClient = new CognitoAuth(params);
cognitoAuthClient.userhandler = {
                // user signed in
                onSuccess: (result) => {
                    Auth.currentSession().then(async (session) => {
                         // ...
                    });
                },
                onFailure: (err) => {
                    logger.debug("Error in cognito hosted auth response", err);
                }
};

const curUrl = window.location.href;
cognitoAuthClient.parseCognitoWebResponse(curUrl);

The params are basically the same with https://aws-amplify.github.io/amplify-js/media/authentication_guide#configuring-the-hosted-ui
What it does is to parse the callback url when loaded, store those tokens in the storage and fire the callback. Then Auth.currentSession() will return the session info to you.

@bajzat if there is no refresh token, then you will need to log the user out when the token is expired.

Would someone (@bajzat @peetss) mind sharing out the snippet before the storage of the cognito token workaround? I'm unclear on how you're invoking the oauth url after the Auth.federatedSignIn call. I'm using the Facebook federated identity approach and it looks like from my react app I need to call the Cognito Oauth2 url, that then calls Facebook Oauth (https://your-cognito-domain/oauth2/idpresponse), which redirects back to Cognito and then my app.

@powerful23 hello again :)
I'm trying to integrate this call you posted but I get cors from facebook.
Do you know how can I solve it?

@powerful23 are there any updates on how to get a refresh token via this method?

@ardsouza if you mean how to get the refresh token via Cognito Hosted UI, you need to set the responseType to code: https://aws-amplify.github.io/docs/js/authentication#configuring-the-hosted-ui

@powerful23 so it's not possible to get refresh token with response type :token
1) will it work with custom IDP(SAML or OIDC) in cognito user pool registered identity?
2) with response type:code how to get access token to authorize some .net web api endpoints

@BKB503

  1. Yes.
  2. When you get redirected back from the login page and Auth.configure() is called, Amplify library will try to parse the current url and get the tokens from Cognito. You need to use the Hub module in the Amplify to wait for the signIn event emitted: https://aws-amplify.github.io/docs/js/hub#listening-authentication-events
  3. Now you are logged in and you can call Auth.currentSession().then(session => console.log(session.getAccessToken()); to get the access token.

@powerful23 Thanks for your reply.

1) If I use custom hosted UI based on domain in the textbox either SAML or OIDC it will redirect to relevant IDP login page with resonsetype: code it will get AUTHORIZATION_CODE,to get accesstoken Auth.currentSession().then(session => console.log(session.getAccessToken()); and send to WEB API endpoints, to achieve this I can use this library or is there any angular version to use it ?
2) If not with Custom hosted UI what are options to implement with angular 6 and angular js including refresh token and custom IDP

Hello! i used this workaround for now:

@peetss for now it's not completely integrated into the library but you can use a work around like:

import { CognitoAuth } from 'amazon-cognito-auth-js';

const params = {
                    ClientId,
                    UserPoolId,
                    AppWebDomain,
                    TokenScopesArray,
                    RedirectUriSignIn,
                    RedirectUriSignOut,
                    ResponseType,
                    Storage
}
const cognitoAuthClient = new CognitoAuth(params);
cognitoAuthClient.userhandler = {
                // user signed in
                onSuccess: (result) => {
                    Auth.currentSession().then(async (session) => {
                         // ...
                    });
                },
                onFailure: (err) => {
                    logger.debug("Error in cognito hosted auth response", err);
                }
};

const curUrl = window.location.href;
cognitoAuthClient.parseCognitoWebResponse(curUrl);

The params are basically the same with https://aws-amplify.github.io/amplify-js/media/authentication_guide#configuring-the-hosted-ui
What it does is to parse the callback url when loaded, store those tokens in the storage and fire the callback. Then Auth.currentSession() will return the session info to you.

@bajzat if there is no refresh token, then you will need to log the user out when the token is expired.

and it worked! at least until the feature is live. you can get the expiration time decoding the JWT access token.
My problem here is the lack of features for Angular2+ projects since all the docs examples for social authentication in userpools (in my case using identity provider) goes for a react approach, while trying to adapt it to Angular is not that clean as i wish... so yeah.. again, this worked for now but i'm waiting for a feature

@ngrosso is it working with above approach for custom idp ? if yes can you please provide me full example and which library is it amplify angular or amazon-cognito-auth-js

@BKB503

  1. As I said you can use the library to get the access token if you follow the right process. Then it depends on you to use that access token.
  2. The Cognito Hosted UI is the only way to federated with the Cognito User Pool.

@powerful23 Ok thanks

When I call: https://DOMAIN.amazoncognito.com/oauth2/authorize?redirect_uri=https%3A%2F%2Flocalhost%3A8080%2F&response_type=token&client_id=CLIENTID&identity_provider=Facebook

I'm getting this response: https://localhost:8080/#error_description=unauthorized_client&error=invalid_request

Any ideas?

I'm able to bring up the hosted UI directly so I'm pretty sure my DOMAIN and CLIENTID are correct.

Wish this was easier!

To add to my previous comment... if I login with the hosted UI, then I'm redirected back to localhost, but then the URL contains the following:

https://localhost:8080/?error_description=attributes+required%3A+%5Bemail%5D&error=invalid_request#_=_

Feels like I'm getting close... but missing something.

@cliffordh can you check if you are mapping the required attributes correctly in your Cognito User Pool Console -> Federation -> Attribute mapping

Thank you, this was indeed the problem, missing the mappings. However, it revealed something new... if a social login user does not have an email address, or chooses not to share their email address then I get the error: error_description=attributes+required%3A+%5Bemail%5D&error=invalid_request in my response from the backend.

The front-end does not parse these errors from the backend. I would suggest a feature to implement error handling.

Can this be done, not for Facebook but for COGNITO itself? I mean, is there a way to use our own UI with Cognito's OAuth 2.0 Authorization Server without using Cognito's Hosted UI?

Lack of look & feel customization and inability to use CUSTOM Authentication Flows is what is driving us not to use the Hosted UI and try and develop our own.

@powerful23 - thanks for this solution - we are currently using it to provide a custom Login with Facebook button on our site. We are also using the aws-amplify CustomUI flow for regular Cognito Login. We are not using Federated Identity Pool, only the normal User Pool.

Is it correct that your solution above, which isn't documented anywhere except here that I can find, is still the only option? Or has something been added to amplify?

Why this issue is closed? I don't see any clean implementation for this issue (only the workarounds).

I agree: super confusing when it should be straightforward.. My use-case: I'd like to have users in User Pool (as they have usernames and a few additional fields, + I use groups for role base access control), while being able to sign in / sign up with facebook.

I don't think there's anyway to do this with Cognito. Users who sign in with Facebook and Google are always treated as completely separate users. They can even have the same email address as an existing user and Cognito won't mind.

You may have to do what we're doing which is keep a separate dynamo DB that ties users to our back-end CRM ID. In this way even though the users are separate from Cognito's POV, we basically treat them the same. You could also use a Cognito custom field for something like this.

@uclaeagit would you mind sharing with us your approach?

Well our setup is unique in that we have 3 DBs - Cognito, Dynamo and our CRM system. So I'm not sure how well it would apply to different scenarios.

When a new user comes in, we look them up in our CRM system (where they usually already have an account) by verified email or phone number. If a single CRM ID is found, we store the mapping between Cognito ID (sub, not username) and our CRM ID in dynamo. So we still have an entry in dynamo for every Cognito user.

You could also just store the CRM ID in a Cognito custom field if desired. We're using dynamo because we expect to save more things later and the Cognito custom fields aren't very flexible.

The net result for us is that the user is treated basically the same no matter how they log in - because the bulk of their data is stored in the CRM system. It's possible in the future we might have some kind of site preferences. Then we'll have to figure out how to handle multiple cognito users.

Ok, I have some questions:

  1. As you are federating, you are giving control to the user to the DynamoDB (through the authenticated role), right?
  2. Are you able to create a post-authentication lambda for federations that can manage this?

I think that this doesn't apply to me, as I want to use it with AppSync (this is really bad for Amplify, as social logins are out for every non-hosted web pages).

Anyways, thanks @uclaeagit

Cognito User Pools have an integration with social login that can save user information in Cognito, and this pool is used for services like AppSync. It's really bad that Amplify supports federated social logins giving them a role but not federated user pools logins giving them an access token they can use for other services.

We're not federating - we're just using Cognito User Pool - no Federated Identities.

I messed around with the post-authentication trigger->lambda but I wasn't able to return the data I wanted to the client. So since I had to make a separate call anyway - to get the CRM info from the back end - I do all my post authentication logic in that call.

Ok, I understand. The problem is that other Login providers (like Auth0) save this federated information somewhere. It's crazy there isn't a clean way to do this with User Pools (federating this with a user role or enforcing using the poorly customizable login hosted page). There are only workarounds, that won't even work for mobile apps (as they don't have callback urls).
I hope amplify team takes into account that this was in roadmap for more than 6 months and we have more than 6 issues referencing this problem. So far, they made a really cool library that it isn't customizable (so it doesn't apply for 90% of the use cases nowadays)

@timgivois Thanks for the feedback, I think a little clarification is necessary. The Amplify team is not the Cognito team. While more customization of the Cognito Hosted UI may be on their roadmap, we are separate from them and can only provide the customer feedback. We don't have the ability to add features for them. While we'll continue to raise feedback such as yours to them I also suggest logging an issue on their forums: https://forums.aws.amazon.com/forum.jspa?forumID=173

That being said, I'd like to help make you successful in any way possible. It sounds like you're trying to do federation with a Social Provider, however there are multiple ways to do this with Cognito depending on your use case. You can federate with Cognito Identity and get AWS Credentials (which are needed to access services such as S3 for storing images, videos, etc.) or you can federate with Cogito User Pools via the Hosted UI which will return a JWT token. You can also federate your User Pool with your Identity Pool, which is a popular option to retrieve both a JWT token as well as AWS Credentials for an app interfacing with services like AppSync as well as S3.

There are a few more details about your use case that I'd like to know so that we can best assist you, and perhaps getting such detail will help us add more clarifying information to our docs as well. Since this issue is closed (as the original poster received assistance for their issue) could you either email me at richthr_AT_amazon.com (replace _AT_ with @) or alternatively open a new issue with all the details of your use case?

Regarding react-native, looks like someone here has found a workaround https://github.com/aws-amplify/amplify-js/issues/1143

We have similar problem and we ended up having a separate user profile in DynamoDB, which will be populated in the post-confirm trigger. The main problem is having different accounts if user chooses also to sign in/sign up with Facebook/Google and currently there seems to be no proper way of doing the accounts merging automatically based on Cognito triggers. So we had to build an UI for the users to merge their accounts (which in my opinion, it's not user friendly). In addition to that, we also have to deal with different user account IDs, which are assigned to the products/posts posted by different user accounts (same emails). When user merges all of their accounts, they should see all the products under the same single User Pool entity.

@undefobj

You can also federate your User Pool with your Identity Pool, which is a popular option to retrieve both a JWT token as well as AWS Credentials for an app interfacing with services like AppSync as well as S3.

Do you have an example for this? I couldn't find a way to federate Cognito with Identity Pools to interface it with AppSync. It's not very clear in the docs (AWS and Amplify)

@timgivois Thanks for the feedback, I think a little clarification is necessary. The Amplify team is not the Cognito team. While more customization of the Cognito Hosted UI may be on their roadmap, we are separate from them and can only provide the customer feedback. We don't have the ability to add features for them. While we'll continue to raise feedback such as yours to them I also suggest logging an issue on their forums: https://forums.aws.amazon.com/forum.jspa?forumID=173

Hi @undefobj - thanks for the response. Is there any roadmap to support social login with Custom UI and User Pool (not Federated Identity Pool) - other than the workaround shown by @powerful23 above?

Regarding react-native, looks like someone here has found a workaround #1143

@ngocketit we have actually been working on React Native support for hosted UI and are very close. It's a bit tricky since The OAuth spec https://tools.ietf.org/html/rfc8252 requires specific View Controller implementations on iOS and custom tabs on Android, otherwise customers will get rejected from the AppStore for security reasons. On web you have more flexibility since the browser doesn't need any trusted domain approvals hence the redirect flow. If you want to take a look at what this will look like for React Native the docs PR is here: https://github.com/aws-amplify/docs/pull/432

Essentially you will be able to use the withOAuth() HOC as a wrapper in your React Native app to either launch the hosted UI directly, or receive the redirect links as props which you could then build your own UI and go to the social provider from a button click and bypass the hosted UI screen.

This is slightly different than the existing withOAuth() HOC for React (web) which you wrap a button, however we're looking at refactoring that to match this newer implementation.

@undefobj

You can also federate your User Pool with your Identity Pool, which is a popular option to retrieve both a JWT token as well as AWS Credentials for an app interfacing with services like AppSync as well as S3.

Do you have an example for this? I couldn't find a way to federate Cognito with Identity Pools to interface it with AppSync. It's not very clear in the docs (AWS and Amplify)

@timgivois Yes, an example is here: https://github.com/dabit3/appsync-auth-and-unauth

This is fair feedback and something we identified in recent weeks as well. We just put this example together in the past couple weeks to address the demand, however we're going to automate it in the CLI and make the process easier as there is too much heavy lifting for customers to wire up all the pieces. An RFC for the work we're going to tackle is here: https://github.com/aws-amplify/amplify-cli/issues/766
Please do have a read and add any thoughts/comments if we are missing any use cases. We're beginning the design work this week.

@timgivois Thanks for the feedback, I think a little clarification is necessary. The Amplify team is not the Cognito team. While more customization of the Cognito Hosted UI may be on their roadmap, we are separate from them and can only provide the customer feedback. We don't have the ability to add features for them. While we'll continue to raise feedback such as yours to them I also suggest logging an issue on their forums: https://forums.aws.amazon.com/forum.jspa?forumID=173

Hi @undefobj - thanks for the response. Is there any roadmap to support social login with Custom UI and User Pool (not Federated Identity Pool) - other than the workaround shown by @powerful23 above?

@uclaeagit By roadmap do you mean Cognito roadmap to make their hosted UI more customizable? If so that's not something I can comment on and as noted previously, it's better to ask that team on their support forum: https://forums.aws.amazon.com/forum.jspa?forumID=173

However that being said, the above code by @powerful23 is being referred to as a "workaround" however I'm not sure that's a completely accurate description. The hosted UI provides mechanisms to federate with social providers through redirects, which matches the OAuth spec: https://tools.ietf.org/html/rfc6749
For SPAs this means that if you don't want to show the default Cognito Hosted UI due to styling limitations, you can still use it as an "endpoint" to redirect to your social provider of choice as well as provide a user directory for your customers to register in your app if they don't want to login to that provider. At that point you can provide all the styling you like in your application. While we do provide this through the withOAuth HOC for React apps (and per my previous comment, we'll have it for React Native soon), if you don't use this HOC then yes you need to manage the redirect process yourself which is seen as a "workaround".

After mulling this over last night, my question to you is - should Amplify simply add a helper API so that you don't need to manage this redirect process yourself? We could do something like let you configure Auth.federatedLogin() to use either Cognito Identity Pools OR Cognito User Pools for federation. We'd probably add an argument or something to when calling that API you could automatically redirect to the social provider of your choice which you could then wire up to a button in your app. Is this something that would make the process easier for you?

@undefobj - I'm not sure if we're talking about the same thing, but a helper API sounds great.

FYI - I am talking about fully CustomUI. The Hosted UI (the Cognito-hosted version or the locally-hosted Amplify version) won't work for us.

Just to be clear what we're talking about, here's our stripped down sign in code for regular Cognito, implemented following the aws-amplify documentation:

<form onSubmit={this.handleSubmit}>
  <Input type="text" name="username" defaultValue={this.state.savedUsername}/>
  <Input type="password" name="password" />
import { Auth } from 'aws-amplify'

const handleSubmit = async (event) => {
  event.preventDefault();
  const data = new FormData(event.target);

  const signinData = {
    username: data.get('username'),
    password: data.get('password'),
    rememberMe: data.get('rememberMe'),
  };

  try {
    await Auth.signIn({ ...signinData });
    await doPostLogin();
  }
  catch (err) {
    component.setState({error: err.message});
  }
};



md5-4355619a62cda94d4afa6685fb991ae9



<SocialSigninButton oauthUrl={fbOauthUrl} linkText="Continue with Facebook"/>



md5-0ee8163afe996c17f0718d2fbf34bc39



import { CognitoAuth } from 'amazon-cognito-auth-js';

const socialParams = {
  ClientId: userPool.webClientId,
  UserPoolId: userPool.poolId,
  AppWebDomain: userPool.domain,
  TokenScopesArray: ['email', 'profile', 'openid', 'aws.cognito.signin.user.admin'],
  RedirectUriSignIn: redirectUrl + '/registration',
  RedirectUriSignOut: redirectUrl,
  ResponseType: 'token',
};

const fbOauthUrl = 'https://' + socialParams.AppWebDomain + '/oauth2/authorize' 
                     + '?redirect_uri=' + socialParams.RedirectUriSignIn 
                     + '&response_type=token' 
                     + '&client_id=' + socialParams.ClientId
                     + '&identity_provider=Facebook';

// after Oauth call - browser is redirected back to this page - could be changed to somewhere else
componentWillMount = async () =>{
  if (window.location.href.indexOf('/registration#access_token') > 0)
    await socialLogin();
}

// called when browser returns from oauth route after user logs in via Facebook or Google
const socialLogin = async () => {
  const cognitoAuthClient = new CognitoAuth(socialParams);

  cognitoAuthClient.userhandler = {
    onSuccess: async function(result) {
      await doPostLogin(result);
    },
    onFailure: async function(err) {
      await component.setState({error: err.message || "There was a problem with your social login."});
    }
  };

  await cognitoAuthClient.parseCognitoWebResponse(window.location.href);
};

As you can see in one case we're using the aws-amplify library, but for social login we have to use the amazon-cognito-auth-js library. Also we're using parseCognitoWebResponse - which I'm not sure is intended to be part of some public API.

I haven't found any other way though to do social login with custom UI and User Pool. The only examples I could find on the aws-amplify docs are for the Federated Identity Pool.

I hope this all makes sense, Please let me know if I'm missing something and doing things the hard way. I do love the way the withOauth HOC works. But our visual requirements and stuff like forgot password flow means we need a fully custom UI - not hosted or out of the box. So if there's some way we could use the withOauth or withAuth HOC's with custom UI that would be awesome.

Thanks for your help and taking the time.

@uclaeagit thanks for the details. We are actually talking about the same thing but unfortunately the architecture and industry nomenclature in this area can confuse some things. To be clear when you construct this URL:

const fbOauthUrl = 'https://' + socialParams.AppWebDomain + '/oauth2/authorize' 
                     + '?redirect_uri=' + socialParams.RedirectUriSignIn 
                     + '&response_type=token' 
                     + '&client_id=' + socialParams.ClientId
                     + '&identity_provider=Facebook';

You are using the Cognito User Pools Hosted UI. That's what I mean when I say it's not a workaround, you're just interfacing directly with the endpoint and entering the OAuth flow by directly going into the redirect to your social provider of choice (Facebook in this case). Then after that social provider gives you a grant token you are redirected with this information back to User Pools and using the window object the token is parsed so that the social user account is added to the User Pool (aka "Federation").

I hope that the architecture makes sense. Essentially the Hosted UI does all of this but you can still bypass it and achieve the same flow in your app which you are doing above. My proposal would be that we could bake all of that logic into an Amplify Auth category API for you.

@timgivois Thanks for the feedback, I think a little clarification is necessary. The Amplify team is not the Cognito team. While more customization of the Cognito Hosted UI may be on their roadmap, we are separate from them and can only provide the customer feedback. We don't have the ability to add features for them. While we'll continue to raise feedback such as yours to them I also suggest logging an issue on their forums: https://forums.aws.amazon.com/forum.jspa?forumID=173

Hi @undefobj - thanks for the response. Is there any roadmap to support social login with Custom UI and User Pool (not Federated Identity Pool) - other than the workaround shown by @powerful23 above?

@uclaeagit By roadmap do you mean Cognito roadmap to make their hosted UI more customizable? If so that's not something I can comment on and as noted previously, it's better to ask that team on their support forum: https://forums.aws.amazon.com/forum.jspa?forumID=173

However that being said, the above code by @powerful23 is being referred to as a "workaround" however I'm not sure that's a completely accurate description. The hosted UI provides mechanisms to federate with social providers through redirects, which matches the OAuth spec: https://tools.ietf.org/html/rfc6749
For SPAs this means that if you don't want to show the default Cognito Hosted UI due to styling limitations, you can still use it as an "endpoint" to redirect to your social provider of choice as well as provide a user directory for your customers to register in your app if they don't want to login to that provider. At that point you can provide all the styling you like in your application. While we do provide this through the withOAuth HOC for React apps (and per my previous comment, we'll have it for React Native soon), if you don't use this HOC then yes you need to manage the redirect process yourself which is seen as a "workaround".

After mulling this over last night, my question to you is - should Amplify simply add a helper API so that you don't need to manage this redirect process yourself? We could do something like let you configure Auth.federatedLogin() to use either Cognito Identity Pools OR Cognito User Pools for federation. We'd probably add an argument or something to when calling that API you could automatically redirect to the social provider of your choice which you could then wire up to a button in your app. Is this something that would make the process easier for you?

This seems fair, and its another workaround. The only problem is that it doesn't integrate with auth directives in the schema. I believe it's one of the coolest things that AppSync has, and also it's the reason we've chosen it instead of other Graphql servers like Apollo server. Plus, we'll need to add additional logic to add the user info to a Dynamo Table/User Pool.

For the moment, we'll handle authentication on our side: we'll switch to Api Keys for AppSync and create users directly in a Dynamo Table adding its info from facebook/google token we receive in the request. The authorization we'll be made by a Lambda that will check that the user has a valid token. We already have some automation tools that add transpiles a schema, adds a Lambda for authorization. (I wanted to use the aws implementation though)

I'm looking forward to see this feature (federatedLogin) implemented to consider the tool for future projects. thanks a lot for your reply, AWS team seemed really supportive and I really appreciate that.

@undefobj - thanks - I think I understand now. We're basically hacking the Cognito Hosted UI. Which is always a little scary, because who knows when they're going to change something, and all our social logins stop working.

So yeah - any other means which we can use to login to social providers would be very welcome here. Your proposal sounds great.

This seems fair, and its another workaround. The only problem is that it doesn't integrate with auth directives in the schema. I believe it's one of the coolest things that AppSync has, and also it's the reason we've chosen it instead of other Graphql servers like Apollo server. Plus, we'll need to add additional logic to add the user info to a Dynamo Table/User Pool.

For the moment, we'll handle authentication on our side: we'll switch to Api Keys for AppSync and create users directly in a Dynamo Table adding its info from facebook/google token we receive in the request. The authorization we'll be made by a Lambda that will check that the user has a valid token. We already have some automation tools that add transpiles a schema, adds a Lambda for authorization. (I wanted to use the aws implementation though)

I'm looking forward to see this feature (federatedLogin) implemented to consider the tool for future projects. thanks a lot for your reply, AWS team seemed really supportive and I really appreciate that.

@timgivois This should actually be able to use the @auth directive in the GraphQL Transformer. That directive uses the OIDC JWT token from User Pools to perform conditional logic in the AppSync resolvers (I was on the design team for this feature). You do need to define the authorization metadata somewhere which represents what users/groups have rights to a specific resource, and the way we do this with the Transformer is storing it on the DynamoDB record rather than hardcoding it in the resolver itself [1]. Essentially the AppSync resolver looks at $context.identity.username or $context.identity.claims.get("cognito:groups") and compares the current user context from the JWT token to what access has been granted on that GraphQL field.

[1] This is actually a fundamental requirement of any access control system as defined by Butler Lampson: https://en.wikipedia.org/wiki/Access_Control_Matrix
More info specific to AppSync which is what the GraphQL Transform does for you: https://docs.aws.amazon.com/appsync/latest/devguide/security-authorization-use-cases.html

I am trying the following work-around, but unsure yet if it will work eventually:

  • Adding a Facebook login button in front-end on sign up form (onboarding)
  • Reading facebook id and passing it through validationData to Auth.signUp call
  • Saving facebookId in pre-signup lambda trigger to DynamoDB (along with some other onboarding data)
  • Reading facebookId in post-confirmation lambda trigger from DynamoDB
  • Attempting to link Cognito user with facebookId in post-confirmation lambda trigger using AWS.CognitoIdentityServiceProvider adminLinkProviderForUser with following settings:
    ```
    DestinationUser: {
    ProviderAttributeValue: userName,
    ProviderName: 'Cognito'
    },
    SourceUser: {
    ProviderAttributeName: 'id',
    ProviderAttributeValue: facebookId,
    ProviderName: 'Facebook'
    },
    UserPoolId
    ````

So far I'm getting Invalid SourceAttributeName and SourceProviderName combination: Linking on arbitrary attributes for social providers is not allowed

Is this the right way to go?
Will I be able to sign in Auth.federatedSignIn and get Cognito token in the future?

@undefobj - thanks - I think I understand now. We're basically hacking the Cognito Hosted UI. Which is always a little scary, because who knows when they're going to change something, and all our social logins stop working.

So yeah - any other means which we can use to login to social providers would be very welcome here. Your proposal sounds great.

@uclaeagit We communicate with Cognito team so we're not going to do anything here to get you in trouble in the future, and in general AWS has a policy to not make breaking changes. It's not really hacking just more baking in the OAuth flows into an easier to use API. In general this AuthN/AuthZ space is a little tricky in the industry so I can see why it might look like a workaround. Let me work on an RFC proposal for this.

Hi @mkrn

Could you open a separate issue and add some more details for your specific scenario?

This seems fair, and its another workaround. The only problem is that it doesn't integrate with auth directives in the schema. I believe it's one of the coolest things that AppSync has, and also it's the reason we've chosen it instead of other Graphql servers like Apollo server. Plus, we'll need to add additional logic to add the user info to a Dynamo Table/User Pool.
For the moment, we'll handle authentication on our side: we'll switch to Api Keys for AppSync and create users directly in a Dynamo Table adding its info from facebook/google token we receive in the request. The authorization we'll be made by a Lambda that will check that the user has a valid token. We already have some automation tools that add transpiles a schema, adds a Lambda for authorization. (I wanted to use the aws implementation though)
I'm looking forward to see this feature (federatedLogin) implemented to consider the tool for future projects. thanks a lot for your reply, AWS team seemed really supportive and I really appreciate that.

@timgivois This should actually be able to use the @auth directive in the GraphQL Transformer. That directive uses the OIDC JWT token from User Pools to perform conditional logic in the AppSync resolvers (I was on the design team for this feature). You do need to define the authorization metadata somewhere which represents what users/groups have rights to a specific resource, and the way we do this with the Transformer is storing it on the DynamoDB record rather than hardcoding it in the resolver itself [1]. Essentially the AppSync resolver looks at $context.identity.username or $context.identity.claims.get("cognito:groups") and compares the current user context from the JWT token to what access has been granted on that GraphQL field.

[1] This is actually a fundamental requirement of any access control system as defined by Butler Lampson: https://en.wikipedia.org/wiki/Access_Control_Matrix
More info specific to AppSync which is what the GraphQL Transform does for you: https://docs.aws.amazon.com/appsync/latest/devguide/security-authorization-use-cases.html

Well, this is a cleaner way to user pools with AppSync. I'm not sure if we can find this in the docs.

Hello everyone, we have created an RFC for feature work that should make the challenges found in this issue easier in the future. If you have a moment please read through the details and add any comments: https://github.com/aws-amplify/amplify-js/issues/2716

Your feedback in the RFC will help us ensure that we are delivering the best experience possible. Thank you.

@uclaeagit would you mind sharing what's involved in your postLogin() ?
I have the following
``` const hashParams = querystring.parse(location.hash.substring(1));
const { access_token } = hashParams;
async function socialLogin() {
console.log('Configure Settings');
const cognitoAuthClient = new CognitoAuth(socialParams);

cognitoAuthClient.userhandler = {
  onSuccess: async function(result) {
    console.log(result);
// if I set {isAuthenticated} to true here, it navigates me to my authenticated route but it does this too quickly - it hasn't authenticated the user yet.
  },
  onFailure: async function(err) {
    console.log(err);
  },
};
await cognitoAuthClient.parseCognitoWebResponse(window.location.href);

}

useEffect(() => {
console.log('useEffect()');
console.log('access_token', access_token);
if (access_token) {
socialLogin();
}
}, []);
```

See comment above. When I navigate to the authenticated app(and call getCurrentAuthenticatedUser()) it seems as though my logged in user is gone again, any help on this would be greatly appreciated.

Sure, here's doPostLogin with some comments:

import { Auth } from 'aws-amplify'

export const doPostLogin = async () => {
  const userInfo = await getUser(Auth); // this goes to our back end
  const user = await Auth.currentAuthenticatedUser(); // make sure we have authenticated user

  if (userInfo.Success) component.props.history.push({pathname: '/dashboard'});
  else  component.props.history.push({pathname: '/promopage'});
};

We do some other stuff with user that won't matter to you. But I think awaiting the Auth.currentAuthenticatedUser() call is the key for you.

I just tested with our our getUser() call and it still works, so it's not because that is delaying it or anything.

@uclaeagit Thank you for your response.
I tried what you have working in a similar manner -

export const doPostLogin = async () => {
  // const userInfo = await getUser(Auth);
  const user = await Auth.currentAuthenticatedUser();

  if (!user) {
    console.log('no user');
  } else {
    console.log('Here it comes');
    console.log(user);
    ///NAVIGATE TO SPACES
    ///setIsAuthenticated(true); here ends up pushing to /dashboard too early
  }
};

The console.log(user) does log a cognito user though which is great so thank you.
Are you managing your user sign in sessions yourself or relying on amplify?

Using amplify for everything so far.

I used the instructions provided here (https://serverless-stack.com/chapters/facebook-login-with-cognito-using-aws-amplify.html) to integrate federated login with aws-amplify.

Then I use amazon-cognito-js to save user-specific information for users logged in with federated identities.

@peetss those instructions are valid for federated identities with an identity pool not federated login with user pools.

I believe there is a lot of confusion between identity pool and federated login with user pools. I don't think this is a problem with Amplify, it's Cognito's documentation.

@0x6C38 Ahhh yes it is.

What is the use case for using federated identities with user pools?

AFAIK you will save a lot if you leverage identity pool vs saving all federated identities to user pool.

@peetss using a cognito user pool with groups allows for easy, granular authorization that is a lot more difficult with identity pools. Other than that I have no use for an identity pool if I can do federated login through the user pool.

@0x6C38 Ah, what do you find to be more difficult in using identity pools for authorization?

@peetss my use-case: I store users in user pool. Each user belongs to a group, depending on which he has access to different appsync urls. userId (sub) is also part of rows in dynamodb, where it is clear which data belongs to user. Let's say I have a row like this in Dynamo:
user-subid | entityBelongingtoUser-uuid | entity info.
user-subid | entityBelongingtoUser2-uuid | entity2 info.

How can achieve similar logic using identity pool?
Do you propose to store all users only in identity pool? Is there any good tutorial on this approach?
Thank you.

I've tried all the possibilites but no chance to save facebook/google users into user pools. I'm using Auth.federatedSignIn, can display email,name but still don't save in user pools. What should I do after Auth.federatedSignIn?

Thank you!

I'm dealing with the same issue as @AdrianRealDevs. I've got the federatedSignIn working fine with Auth.federatedSignIn('facebook', { token: accessToken, expires_at }, user), but it does not create a user for them in the user pool.

I was previously using the Hosted UI (and it did create users in the user pool), but I found that the user experience is more customize-able by doing it manually.

It would be nice if there was some option in federated sign in to store the user in the user pool as well. For now, I'll probably trigger a Lambda that uses admin rights to create the social user during the sign up process.

@kylegwalsh It seems you have to redirect to Hosted-UI to let create the user in the user pool, they suggest to use withOauth HOC from your custom UI, so you can still use your custom UI just using the hosted-UI endpoints to sign in social identity. https://github.com/aws-amplify/amplify-js/issues/3875#issuecomment-523114135, but I still have to verify this.

Furthermore, I also was thinking to use a lambda but I didn't find any lambda triggered by Auth.federatedSignIn, did you do?

@michelem09

I ended up reverting to Hosted-UI due to the issue I cited above. When I was doing it manually, I did like the fact that I could trigger a popup window rather than temporarily redirecting the user away from my site, but I determined it was not worth the headache of trying to manually manage everything.

In addition I configured Hosted-UI to use a subdomain of my site (auth.mysite.com) so that I didn't have to rely on the random URL they generated.

@manueliglesias is this supported now ?
even with all the customization, when we click any sso button to login (google/facebook/etc), the pop up won't be able to say exactly "use google/facebook/... to login", the nicest thing we can do is configured Hosted-UI to use a subdomain , so it will say "use some-subdomain-of-us to login", this is a sketchy experience to end user tbh

I used the instructions provided here (https://serverless-stack.com/chapters/facebook-login-with-cognito-using-aws-amplify.html) to integrate federated login with aws-amplify.

Then I use amazon-cognito-js to save user-specific information for users logged in with federated identities.

@peetss
What did you mean by using amazon-cognito-js to save user-specific information for users logged in with federated identities?

Did you use it to create a User Pool user for FB / Google / other IdP logged in users?

@Jun711

Mainly I used this var client = new AWS.CognitoSyncManager(); for saving user-specific data.

Any updates?

As of the implementation of https://github.com/aws-amplify/amplify-js/issues/2716 you can call Auth.federatedSignIn({ provider: 'Facebook' }) and it will sign in using User Pool Federation provided you have Amplify configured that way.

Was this page helpful?
0 / 5 - 0 ratings