New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Comments: Login with AWS Cognito #38

Closed
jayair opened this Issue Apr 10, 2017 · 39 comments

Comments

Projects
None yet
@jayair
Copy link
Member

jayair commented Apr 10, 2017

@jayair jayair added the Discussion label Apr 10, 2017

@auz1111

This comment has been minimized.

Copy link

auz1111 commented Jun 1, 2017

I would love it if you added a chapter on logging in with facebook account. Do you guys know of any good tuts that would help walk us through this?

@jayair

This comment has been minimized.

Copy link
Member

jayair commented Jun 3, 2017

@auz1111 Yeah it has been on our list.

For the tutorial, I'd have to look for one. You need to use Cognito Federated Identities instead of the User Pool to manage your user accounts.

@rogueturnip

This comment has been minimized.

Copy link

rogueturnip commented Jun 8, 2017

I'm starting to look into this also. It seems that there is a mapping ability to map the different federated identities together but I'm not exactly sure yet how to do this in a way that makes sense.

@jayair

This comment has been minimized.

Copy link
Member

jayair commented Jun 8, 2017

@rogueturnip Yeah it is quite confusing to wrap your head around the set up. But that is how it is supposed to work. Federated identities pulls all your different identity sources together to give you a consolidated view of all your users, regardless of where they are coming from.

@rogueturnip

This comment has been minimized.

Copy link

rogueturnip commented Jun 8, 2017

If I'm understanding it correctly it seems you login the first time and that caches the credentials. Then when a login is done using something like facebook you would do a credentials.get to see if there are existing credentials and then when you do the Login Map you would send in both. Am I following the flow right or completely off base? I can't find an example so I'm piecing together things from the AWS docs.

@jayair

This comment has been minimized.

Copy link
Member

jayair commented Jun 9, 2017

Hmm I'm not completely sure if I follow the flow you are talking about. It is a bit complicated and not super well documented.

@rogueturnip

This comment has been minimized.

Copy link

rogueturnip commented Jun 9, 2017

It's possible I"m messing up what I think the process is. No where is this documented very well.

@ffmike

This comment has been minimized.

Copy link
Contributor

ffmike commented Jul 11, 2017

Getting "Error: Network Failure" out of the AWS xhr.js at this point. Have verified the correct username & password, they work from the console with aws cognito-idep admin-initiate-auth. Have verified the right user pool & client IDs in config.js. Not sure what to look at next...

@ffmike

This comment has been minimized.

Copy link
Contributor

ffmike commented Jul 11, 2017

Figured it out. NoScript stepped in to block amazonaws.com. That'd do it.

@exbarboss

This comment has been minimized.

Copy link

exbarboss commented Aug 22, 2017

+1 for Federated Identities chapter.

@triunm

This comment has been minimized.

Copy link

triunm commented Oct 17, 2017

Great tutorial! I've been through all the content and now building my app by customising the code produced in this tutorial. I just have question on how we can dynamically configure the app to use specific config.js file for different stages. I'm using the created config.js in this tutorial for production environment. Config.js should be different on dev or test environment like the user pool & app client id. It should be somewhere in build time, right?

Thanks in advance!

@jayair

This comment has been minimized.

Copy link
Member

jayair commented Oct 19, 2017

@triunm I was planning on writing on this but a simple way to handle this is as follows.

In your config.js use something like this:

const dev = {
  apiGateway: {
    REGION: 'us-east-1',
    URL: 'https://12345678.execute-api.us-east-1.amazonaws.com/staging',
  },
  cognito: {
    REGION: 'us-east-1',
    USER_POOL_ID : 'us-east-1_123456678',
    APP_CLIENT_ID : '4xxxxxxxxxxxxxxxxxxxx',
    IDENTITY_POOL_ID: 'us-east-1:12345678-1234-1233-9387-1234567890123',
  }
};

const prod = {
  apiGateway: {
    REGION: 'us-east-1',
    URL: 'https://567890.execute-api.us-east-1.amazonaws.com/prod',
  },
  cognito: {
    REGION: 'us-east-1',
    USER_POOL_ID : 'us-east-1_4444444',
    APP_CLIENT_ID : '5xxxxxxxxxxxxxxxxxx',
    IDENTITY_POOL_ID: 'us-east-1:456789-1234-1234-4567-123456789',
  }
};

const config = process.env.REACT_APP_STAGE === 'production'
  ? prod
  : dev;

export default {
  ...config
};

And in your package.json set your scripts as:

  "scripts": {
    "start": "REACT_APP_STAGE=dev react-scripts start",
    "build": "REACT_APP_STAGE=production react-scripts build"
  }

You can extend this idea to have other environments. This is based on the idea of custom environment variables in Create React App.

@leejh3224

This comment has been minimized.

Copy link

leejh3224 commented Nov 28, 2017

I'm leaving this comment just because I had a hard time finding out what was a problem when I faced 502 error(cors problem).

In that case, there are three possible options.
Firstly, you can look at '/libs/response-lib.js' and check whether the function contains 'Access-Control-Allow-Origin' header or not.
Secondly, you may have miss indented serverless.yml.
Or lastly, you may have forgotten deploying your functions as I did ...

I could see my notes after deploying the functions.
If you have same issue like me, you can re-deploy your functions and it might help you get out of trouble.

@jayair

This comment has been minimized.

Copy link
Member

jayair commented Nov 28, 2017

@leejh3224 Thanks for leaving your comment here.

@ohenneken

This comment has been minimized.

Copy link

ohenneken commented Dec 14, 2017

Hi,

I have reached a point in the splendid tutorial where a first time app-client user should login in for the first time, having his email already verified.

The login function from the Login.js is executed and comes back with a new challenge 'NEW_PASSWORD_REQUIRED'.

The tutorial says nothing about this and assumes the login succeeds?

So I added a function newPasswordRequired: function(userAttributes, requiredAttributes)

-according to http://docs.aws.amazon.com/cognito/latest/developerguide/using-amazon-cognito-identity-user-pools-javascript-example-authenticating-admin-created-user.html-

but i can not get that to work. How should i proceed?

Is the tutorial somewhat outdated regarding this feature? The Login.js in the tutorial does not have the newPasswordRequired method nor are there any problems mentioned at the end of the chapter.

Thnx,

Oscar Henneken

@jayair

This comment has been minimized.

Copy link
Member

jayair commented Dec 15, 2017

@ohenneken Hmm you shouldn't need to do this. If you've followed this chapter - https://serverless-stack.com/chapters/create-a-cognito-test-user.html

Then you user should be able to login without having to set a new password.

@ohenneken

This comment has been minimized.

Copy link

ohenneken commented Dec 15, 2017

@mdhendri

This comment has been minimized.

Copy link

mdhendri commented Dec 23, 2017

With cognito how would you setup up the ability to reset ones password if the user forgot the password or even within the app?

-Michael

@ohenneken

This comment has been minimized.

Copy link

ohenneken commented Dec 24, 2017

@jayair

This comment has been minimized.

Copy link
Member

jayair commented Dec 27, 2017

@mdhendri It is a few extra steps but it is fairly straightforward. Cognito will send a code to the user and they can reset their password with the code and their new password. I might write a chapter on this at some point, but here is the sample code from the Cogntio JS SDK docs - https://github.com/aws/amazon-cognito-identity-js

cognitoUser.forgotPassword({
    onSuccess: function (data) {
        // successfully initiated reset password request
        console.log('CodeDeliveryData from forgotPassword: ' + data);
    },
    onFailure: function(err) {
        alert(err);
    },
    //Optional automatic callback
    inputVerificationCode: function(data) {
        console.log('Code sent to: ' + data);
        var verificationCode = prompt('Please input verification code ' ,'');
        var newPassword = prompt('Enter new password ' ,'');
        cognitoUser.confirmPassword(verificationCode, newPassword, {
            onSuccess() {
                console.log('Password confirmed!');
            },
            onFailure(err) {
                console.log('Password not confirmed!');
            }
        });
    }
});
@jayair

This comment has been minimized.

Copy link
Member

jayair commented Dec 27, 2017

@ohenneken I'm not entirely sure based on what is happening here but a good place to start would be by checking the logs for API Gateway and Lambda. We have a chapter on this here - https://serverless-stack.com/chapters/api-gateway-and-lambda-logs.html

@ohenneken

This comment has been minimized.

Copy link

ohenneken commented Dec 27, 2017

@jayair

This comment has been minimized.

Copy link
Member

jayair commented Dec 27, 2017

@ohenneken You should be able to inspect the Lambda logs to see why they are failing. What do the logs for the failed ones say?

@ohenneken

This comment has been minimized.

Copy link

ohenneken commented Dec 28, 2017

@jayair

This comment has been minimized.

Copy link
Member

jayair commented Dec 30, 2017

@ohenneken The permissions for DynamoDB are set in the serverless.yml here https://github.com/AnomalyInnovations/serverless-stack-demo-api/blob/master/serverless.yml#L21

I'm not sure what your set up is and how it relates to the tutorial though.

@ohenneken

This comment has been minimized.

Copy link

ohenneken commented Dec 30, 2017

@walshe

This comment has been minimized.

Copy link

walshe commented Jan 21, 2018

hey guys, looking for some advice if you got any..

So this cognito stuff is really great, especially for managing users but also for using their authentication to get temporary user credentials in order to access secured api gateways.

I have a slightly different situation but wondering if I could somehow stay close to the same pattern/principles you guys are using.

So here is my situation.. (by the way, BigCommerce does not support OpenID Connect OIDC)

  • A user logs into their BigCommerce store and installs my app (my app is the react app with serverless backend).
  • when user clicks my app from inside BigCommerce, then bigcommerce forwards the user to my app via Auth Callback URL .. which is a serverless function I created. e.g. https://xxx.execute-api.us-east-1.amazonaws.com/prod/bc/oauth
  • In here I use a bigcommerce module to authorize the data sent to it from BigCommerce
    import {
    success,
    failure
    } from "./libs/response-lib";

    const BigCommerce = require('node-bigcommerce');

    const bigCommerce = new BigCommerce({
    logLevel: 'info',
    clientId: 'xxxx',
    secret: 'yyyy',
    callback: 'https://xxxxx.execute-api.us-east-1.amazonaws.com/prod/bc/oauth',
    responseType: 'json',
    apiVersion: 'v3' // Default is v2
    });

    export async function oauth(event, context, callback) {

    bigCommerce.authorize(JSON.stringify(event.queryStringParameters))
        .then(data => {

            console.log('Authorized! ' + JSON.stringify(data));
           
           // TODO what should I do here
         
            callback(null, {
                statusCode: 302,
                headers: {
                    Location: 'https://myreactapp.com',
                },
            })


        }).catch(error => {
            callback(null, failure(error));

        });

}

So above you can see the point at which I know the bigcommerce user is valid. The data I receive from bigcommerce is something like this: {"context":"stores/2345","code":"gpkof3rb4vo1ck0picwqjkeeeeett","scope":"store_v2_default"}.

So here is where I am at a crossroads and wondering what to do. Basically I ideally want to do something that allows me to hook into the same pattern as the tutorial and use cognito if possible.

For instance at the 'TODO' section above should I try to create (first time somehow) or retrieve a cognito user (every subsequent time) and get a token for that user using currentUser.getSession(..).getIdToken().getJwtToken() ? and then forward that token to some route in my react app e.g. myreactapp.com/bc/loginSuccess so that I can continue development just like the vein of the tutorial . When I create the cognito user I would perhaps give it some attribute that associates it with the BigCommerce user account e.g. the store id above

One other nice reason to use the cognito pool pattern is this:
While users will usually log in via the BigCommerce site and get forwarded to my app (at least once anyway for the first time), I will also want to give my users a direct login to my app. This access can only be given after they have first authenticated via BigCommerce at least once (obviously since otherwise we would have no bigcommerce data to connect them to). So if I could use the cognito pattern that I described above then this could all work really nicely.. i.e. at the time I create the cognito user (inside bigCommerce.authorize()..) above, cognito would sent the conformation email to the user with something like this: 'You logged into the app via BigCommerce but we have also created a username/password for direct access to the app, click here to confirm etc'

so anyway, curious if theres a better way or if such an approach is totally crackers..

@jayair

This comment has been minimized.

Copy link
Member

jayair commented Jan 23, 2018

@walshe Yeah this isn't straightforward but here is how I would do it. I haven't tried it but theoretically it should work.

  1. Create/Auth the user using the Admin Cognito APIs (https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminCreateUser.html). This needs to be done in your serverless backend and not in the client since you don't want to expose the Admin APIs publicly. These calls need to return the JWT token.

  2. Store the JWT token and handle sessions in the React app yourself instead of relying on the Cognito JS SDK.

So effectively, you are going to do the login and signup portions on your own in the serverless backend.

@walshe

This comment has been minimized.

Copy link

walshe commented Jan 23, 2018

thanks @jayair , actually I was thinking more since and thing I could further simplify:

so when BigCommerce forwards to my /bc/oauth and when bigCommerce.authorize(JSON.stringify(event.queryStringParameters)) succeeds, I do the following:

  1. I check if cognito user exists, if not , I create the new cognito user there with an attribute connecting user to the bigcommerce store (in the meantime an email gets sent to the new app user with login/password etc).
  2. Next I just forward user to the login form of the app (with a message saying check your email for password etc)

if on the other hand this is a second/subsequent time the user has authenticated with bigcommerce, then in this case (in my oauth method) the cognito user will be found and here I just forward them to the react app (without the message). If there is already a session still valid in the app then the user would go straight to the home page of app etc. If not then they are presented with the login form of the app

So in this approach the react app works exactly like it does in the tutorial, still uses the cognito js sdk and iam authorizer etc, saving me lots of work

thoughts ?

@jayair

This comment has been minimized.

Copy link
Member

jayair commented Jan 23, 2018

@walshe Yeah this would be a lot simpler than handling the sessions yourself.

@walshe

This comment has been minimized.

Copy link

walshe commented Jan 23, 2018

@jayair one other question.

There will be an admin (maybe more than one) user for the react app.

I have a requirement that this admin once logged in to be able to 'log in as' another user. I guess I could pass the id or something of the 'other' cognito user as an extra 'asUserId' param to each api call and then on the server side check that the caller is ADMIN and the existence of the extra userID param and then run business logic using the userId param - but is there a better way of doing this ?

@jayair

This comment has been minimized.

Copy link
Member

jayair commented Jan 25, 2018

@walshe I don't think there is a built-in way to do this with Cognito (I'm not entirely sure though). So you'd probably have to come up with a solution that works for you.

@walshe

This comment has been minimized.

Copy link

walshe commented Jan 25, 2018

@jayair thanks

@walshe

This comment has been minimized.

Copy link

walshe commented Jan 26, 2018

re my comment above..

It turns out that BigCommerce doesnt want a user to have to login to the app a second time once validated by the oauth callback.. (i.e. when they click my app link from inside bigcommerce)

So, in my oauth serverless callback, if I create the Cognito user, (and save some info in a db table connecting the user to a bigcommerce store etc) is it possible for me to get credentials for that new user and just forward these credentials to the react app.

I could add a special hook in the app to accept the credentials which would allow it to make api gateway calls that are locked down with the iam_authorizer. This would avoid the user having to explicitly log into the app if they come from BigCommerce, while still allowing them to use the apps login form for direct access outside of BigCommerce. @jayair

@sunkay

This comment has been minimized.

Copy link

sunkay commented Apr 3, 2018

@jayair have you used the withAuthenticator HOC that comes with aws-amplify-react? Is that a good way to use for Auth?

@jayair

This comment has been minimized.

Copy link
Member

jayair commented Apr 3, 2018

@sunkay I had a brief look at it. Just didn't find enough examples on it and so I decided to stick with the simple methods that they have. I like the current setup since it's pretty flexible and you could swap it out for a different auth provider. I would need to play around with a sample app to see it in detail.

@smokeyblues

This comment has been minimized.

Copy link

smokeyblues commented May 4, 2018

Hey thanks for the tutorial. It has been informative and inspiring. Maybe too inspiring (lol) because I tried to integrate it with a gatsbyjs generated frontend and ran into a problem that doesn't have a lot of documentation online.

I have a login page with cognito and aws amplify set up but when I click the 'sign in' button I get:

_this3 is not defined

or


That is the only thing it is returning. There is no console output server-side or client-side. This may be the wrong place to ask this question but I can't find a workable solution anywhere else. Does anyone here know how to resolve this error?

Thanks in advance.

@jayair

This comment has been minimized.

Copy link
Member

jayair commented May 7, 2018

@smokeyblues Can you boil it down to the line that is throwing the error?

@jayair jayair closed this May 9, 2018

@jayair jayair reopened this May 9, 2018

@jayair

This comment has been minimized.

Copy link
Member

jayair commented May 9, 2018

@jayair jayair closed this May 9, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment