amazon-archives / amazon-cognito-auth-js

The Amazon Cognito Auth SDK for JavaScript simplifies adding sign-up, sign-in with user profile functionality to web apps.
Apache License 2.0
423 stars 232 forks source link

Working sample of Authorization code grant flow? #52

Closed railsstudent closed 6 years ago

railsstudent commented 6 years ago

Can amazaon provide an sample of Authorization code grant flow?

I tried to use google to login Cognito User Pool but token endpoint returns 'invalid_client' When I returned client id and client secret of google in header and encrypted with base64, the endpoint returned "internal error" error message.

I am stumped here. Thanks

yuntuowang commented 6 years ago

@railsstudent Hi, I think you probably gave incorrect cognito app client id which causes 'invalid_client'.

I just reproduced your steps and get the tokens successfully using Postman. The steps:

  1. create a app client without client secret in Cognito User Pool, and enable Google as an identity provider and enable code grant flow; (If the client was issued a secret, the client must pass its client_id and client_secret in the authorization header through Basic HTTP authorization. The secret is Basic Base64Encode(client_id:client_secret).) It is in our doc: http://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html

  2. Go to the hosted UI (/oauth2/authorize?response_type=code&client_id=****&redirect_uri=https://www.amazon.com), and click "log in with Google", I get redirected to amazon website. In the callback url, I get the code.

  3. Go to postman, make a post request: URL is: /oauth2/token Header: [{"key":"Content-Type","value":"application/x-www-form-urlencoded"}]

Body: remember to select x-www-form-urlencoded: and put the following, [{"key":"grant_type","value":"authorization_code"},{"key":"client_id","value":""},{"key":"redirect_uri","value":"https://www.amazon.com"},{"key":"code","value":""}] Then click send request, you will get id_token, access_token and refresh_token.

railsstudent commented 6 years ago

I got authorization code grant flow working in my angularjs app. When creating client app in user pool, I forgot to uncheck generate client secret checkbox. I create a new client app with no secret key and get the code grant flow working.

Thank you for your help. The above steps should be included the sample app if not in the Amazon documentation.

yuntuowang commented 6 years ago

@railsstudent Glad it works! Thanks a lot for your suggestion, and I will include this part in our sample app doc.

joe455 commented 6 years ago

@yuntuowang @railsstudent I am saving facebook user into userpool using auth SDK and able to redirect to it. But I want to see the the current logged in user on the redirect page.Is it possible? If so, which method I have to use?

yuntuowang commented 6 years ago

Answered in another issue: call getCurrentUser() in your onSuccess() callback. @joe455

jorenvh1 commented 6 years ago

@yuntuowang Hey, I tried your step by step method from above but I always get the error response: {"error":"invalid_request"} Everything seems to be correct, headers are correct, code is correct and all params are present. Is there anything else I should do?

yuntuowang commented 6 years ago

@jorenvh1, I am not entirely sure why you get "invalid_request". The reason could be wrong clientId or other incorrect parameters. Also, it is important to notice that if the client was issued a secret, the client must pass its client_id and client_secret in the authorization header through Basic HTTP authorization. The secret is Basic Base64Encode(client_id:client_secret). It is in our doc: http://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html

jorenvh1 commented 6 years ago

@yuntuowang Thanks for the quick reply!

I am providing the correct clientid because if I change this, this will return a invalid_client error. There's no secret present on the client app.

Is there any way I can debug which parameter is causing the problem? Cause 'invalid_request' isn't really getting me anywhere 🤔

Thanks in advance!

yuntuowang commented 6 years ago

Hi @jorenvh1, can you successfully authenticate successfully with the code grant flow end point and get the code?

Then when you use postman to exchange the code for tokens, then you see this 'invalid_request' error??

jorenvh1 commented 6 years ago

@yuntuowang Exactly!

yuntuowang commented 6 years ago

Hi @jorenvh1, can you provide me your parameter settings in Postman when making this Post request?

So mine is: make a post request: URL is: /oauth2/token Header: [{"key":"Content-Type","value":"application/x-www-form-urlencoded"}]

Body: remember to select x-www-form-urlencoded: and put the following, [{"key":"grant_type","value":"authorization_code"},{"key":"client_id","value":""},{"key":"redirect_uri","value":"https://www.amazon.com"},{"key":"code","value":""}] Then click send request, you will get id_token, access_token and refresh_token.

This works, so I suggest you compare your parameters with this. The format should be exactly the same.

jorenvh1 commented 6 years ago

@yuntuowang Yes, I am providing those exact parameters, see screenshot below

screen shot 2018-01-24 at 08 02 47
yuntuowang commented 6 years ago

Hi @jorenvh1, 400 Bad Request means invalid parameter exception.

Your settings here seem correct. Make sure you have set your header correctly.

From the doc we can see: (https://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html) Examples of Positive Requests Exchanging an Authorization Code for Tokens Sample Request

POST https://mydomain.auth.us-east-1.amazoncognito.com/oauth2/token& Content-Type='application/x-www-form-urlencoded'& Authorization=Basic aSdxd892iujendek328uedj

grant_type=authorization_code& client_id=djc98u3jiedmi283eu928& code=AUTHORIZATION_CODE& redirect_uri=com.myclientapp://myclient/redirect Sample response

HTTP/1.1 200 OK Content-Type: application/json

{ "access_token":"eyJz9sdfsdfsdfsd", "refresh_token":"dn43ud8uj32nk2je", "id_token":"dmcxd329ujdmkemkd349r", "token_type":"Bearer", "expires_in":3600 }

jorenvh1 commented 6 years ago

@yuntuowang I finally figured it out... I was sending the parameter 'redirect_url' instead of 'redirect_uri' Thanks for all your suggestions and time to help me out!

yuntuowang commented 6 years ago

Hi @jorenvh1, awesome!! You are very welcome!

balakrishna222111 commented 6 years ago

After Successfully login with GOOGLE plus app a user identity is creatin my identity pool after that my own custom ui page is reloading properly see below

image

when am click continue with google it showing ERROR like this

image

tawfik-1990 commented 6 years ago

I have problem with post request authentification while trying to run the code I get error invalide request does anyone know how I could solve this problem, I get this error { error: 'invalid_request' }

const dat = { 'grant_type': 'authorization_code',

          'code':code ,
           "redirect_uri" : redirect
           };

const URLSearchParams = Object.keys(dat).map((key) => { return encodeURIComponent(key) + '=' + encodeURIComponent(dat[key]); }).join('&');

fetch('https://polarremote.com/v2/oauth2/token', {

method: 'POST', headers: { 'Authorization':Basic ${creds},

'Content-Type': 'application/x-www-form-urlencoded',
 'Accept':'application/json; charset=utf-8'

}, data: URLSearchParams

}).then(function(res) { return res.json(); }).then(function(body) { console.log(body); });

yuntuowang commented 6 years ago

Hi @balakrishna222111, this is possibly because your scope setting of "Allowed OAuth Scopes" under App client settings of Cognito console is not consistent with the scopes you provides in sample app.

For example, in our sample index.html line 199: TokenScopesArray : , // like ['openid','email','phone']... Here the scopes provided must be consistent with the console scope setting, can you double check this?

yuntuowang commented 6 years ago

Hi @tawfikbougerba, you can see the error "invalid_request" cause of many reasons.

Can you double check the detailed steps I posted earlier to make sure the POST parameters are exactly correct? Also, make sure the app client settings on Cognito console page is all consistent, e.g., make sure you have enabled "authorization_code" flow.

declan4 commented 6 years ago

@yuntuowang Hi, I have followed the steps above to ensure all my parameters are exactly correct, and my app client does not have a secret, but am still getting 'invalid_request' error.

Is it possible to have http://localhost:4000 as the redirect uri? Or does this have to be https? If it has to be https how is this supposed to be tested in development?

declan4 commented 6 years ago

@yuntuowang nvm, the issue was actually that axios was not converting the parameter data from JSON to a query string before sending the request. Changing it to a query string manually fixed the issue

yuntuowang commented 6 years ago

Hi @declan4, glad it worked!!

DmytroRybka commented 6 years ago

Hi @yuntuowang

I'm having issue with authorization code flow while trying to marry cognito with spring-security-oauth2
When I exchange code for access token I get 400, "invalid_client" error.

I'm using Cognito User Poll as an identity provider. Manual try with Postman says the same.

My configuration is: image

Can I see any logs from Cognito somewhere about what is happening?

yuntuowang commented 6 years ago

Hi @DmytroRybka, you cannot see logs from Cognito. "Invalid_client" error means your client setting has some problems. As I can tell, your callback url is invalid. We don't support HTTP protocol, so you need to use HTTPS. And we also don't recommend your callback url containing "localhost".
You also need to set "sign out URLs" correctly. For scopes, make sure your setting on console is exactly same as in your code configuration. Thanks.

DmytroRybka commented 6 years ago

Hi @yuntuowang , Thanks for the idea with https but unfortunately still getting the same error. I changed to use self signed certificate. The issue is that "implicit flow" works for me just fine. The only issue is when I use authorization code and try to exchange code for access token. Theoretically should not be a difference between both flow handling. Do you think I Self signed might be an issue?

yuntuowang commented 6 years ago

Hi @DmytroRybka, I don't think Self signed is an issue.

When you use authorization code grant flow, can you successfully get code parameter in the callback url? And then you see "invalid_client" error when SDK try to use this code to exchange for tokens? If yes, can you reproduce this error and tell me which region are you using our service, the timestamp(which minute), your user pool id and your client id. Then I can pull the log for you to see what happened. Thanks!

sunsoori commented 6 years ago

curl -X POST -H "Authorization: Basic c2E***XE1a2tqZ2M=" -H "Content-Type: application/x-www-form-urlencoded" -d '{ "grant_type":"authorization_code", "redirect_uri":"https://www.google.com", "code":"801c738e-f7c6-4bc3-8409e4", "client_id":"sa0adikbpbdebakl79" }' https://mycompany.auth.us-east-2.amazoncognito.com/oauth2/token

Can anybody tell what's wrong with the above command? I'm getting {"error":"invalid_request"}.

yuntuowang commented 6 years ago

Hi @sunsoori, "invalid_request" usually means the parameters you put in the command is inconsistent with the app client settings you have on cognito console. Can you double check those are correct?

sunsoori commented 6 years ago

hi @yuntuowang Thanks for the response. apparently I have to do like this: -H "Content-Type: 'application/x-www-form-urlencoded'" and single quote within double quote.

But now, I'm getting 405 Method not allowed. Any thoughts?

Here's the verbose log of the curl command

< HTTP/2 405 < date: Fri, 16 Mar 2018 18:29:20 GMT < content-length: 0 < set-cookie: XSRF-TOKEN=0d8cde7f-27c0-41d1-ab95-6535c4f1a997; Path=/; Secure; HttpOnly < x-amz-request-id: fd1eeb13-230a-4e8b-bb4b-79debb79f662 < x-application-context: application:prod:8443 < x-content-type-options: nosniff < x-xss-protection: 1; mode=block < cache-control: no-cache, no-store, max-age=0, must-revalidate < pragma: no-cache < expires: 0 < strict-transport-security: max-age=31536000 ; includeSubDomains < x-frame-options: DENY < server: Server < allow: GET

sunsoori commented 6 years ago

funny enough, I'm getting 405 (Method not allowed) for this one

curl -v -X POST https://mycompany.auth.us-east-2.amazoncognito.com/oauth2/token

yuntuowang commented 6 years ago

I am not sure about this 405 error. But your command seems missing parameters.

Sample Request:

POST https://mydomain.auth.us-east-1.amazoncognito.com/oauth2/token& Content-Type='application/x-www-form-urlencoded'& Authorization=Basic aSdxd892iujendek328uedj

grant_type=authorization_code& client_id=djc98u3jiedmi283eu928& code=AUTHORIZATION_CODE& redirect_uri=com.myclientapp://myclient/redirect

Details:
https://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html

SimoneMSR commented 6 years ago

Sorry for being so stupid @yuntuowang , but where do I take my client_id? Or where do I create my app client? I have created a project on Mobile Hub, but I do not know what is this client_id. Do I get it from the aws_config.js file?

Moreover, where do I get the hosted UI URLof the app? (for seek of clarity, I am trying to use the authorization grant flow to access aws from a native Ionic app. Using this SDK I cannot get a refreshToken from the authorization flow, so I have to implement the grant flow)

yuntuowang commented 6 years ago

Hi @SimoneMSR, you need to go to Cognito console to create a user pool and create an app client there. At the client settings tab, you can set all the fields. The hosted UI URL will be constructed from the app client settings. It looks like: https://****.auth.us-east-1.amazoncognito.com/oauth2/authorize?response_type=token&client_id=*******&redirect_uri=https://www.amazon.com

If you use the authorization code grant flow, you will get refreshToken using this SDK.

Please refer to following doc: https://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-ux.html

You can find more resources too on Cognito doc.

sunsoori commented 6 years ago

Why is cognito NOT having "Resource Owner Password Credentials Grant" flow? It only has Authcode grant, implicit and client credentials flows.

yuntuowang commented 6 years ago

Hi @sunsoori, I will make this as a feature request on behalf of you. Thanks!

sunsoori commented 6 years ago

Thankyou so much. Would you mind telling how long it usually takes, for a feature to be released after making the request?

yuntuowang commented 6 years ago

Hi @sunsoori, at this point, it is hard to comment on the timeline. But we will take this feature request in our queue for sure. Thanks!

SimoneMSR commented 6 years ago

Thank you for the reply @yuntuowang. It was just effective! But now facebook tells me that the URL is not included in the domains. I used https://CLIENT-APP-DOMAIN.auth.eu-west-1.amazoncognito.com. Where am I wrong? I cannot use https://CLIENT-APP-DOMAIN.auth.eu-west-1.amazoncognito.com/oauth2 as domain in Facebook...

yuntuowang commented 6 years ago

There is a field called "redirect url" in Facebook setting, you need to put something like this: https://your-domain-prefix.auth.eu-west-1.amazoncognito.com/oauth2/idpresponse

SimoneMSR commented 6 years ago

Done! By the way I had to add a mapping on the cognito pool, a mapping from the email facebook attribute so the email in the user pool, otherwise in the callback url I would have got an error! Thank you @yuntuowang , this is pretty nice that you are here, let me feel not alone!

YasithLokuge commented 6 years ago

I'm unable to get a token from cognito oauth2/token endpoint after getting the authorization code successfully. I'm using an app client with a client secret.

request:

curl -X POST \
https://domain.auth.us-east-1.amazoncognito.com/oauth2/token \
-H 'Authorization: Basic  xxxxxxxxx(base64 of clientId:clientSecret)' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=authorization_code&client_id=xxxxxxxx&code=xxxxx- 
xxxx-4xxaf7-xx-d5497d5e000e&redirect_uri=https://www.example.com'

response:

HTTP 500
{
    "error": "Internal Error"
}

Furthermore I'm using authorization code grant and implicit grant for the allowed oauth flows and I was able to get the token when using the implicit grant.

my auth request:

https://domain.auth.us-east-1.amazoncognito.com/oauth2/authorize?response_type=code&client_id=xxxxxxxxxx&redirect_uri=https://www.example.com.&state=STATE&scope=openid+phone+email+profile+aws.cognito.signin.user.admin

Please let me know if I'm doing anything stupid here.

yuntuowang commented 6 years ago

Hi @YasithLokuge, can you double check your scope setting under your app client setting? They must be consistent as "openid+phone+email+profile+aws.cognito.signin.user.admin".

If they are consistent, and token endpoint still doesn't work for you, can you send me an email about your UserpoolId, clientId, AWS account Id and the timestamp which you had this error(better to minute)? My email address is wyuntuo@amazon.com

Thanks a lot!

YasithLokuge commented 6 years ago

Hi @yuntuowang, Requested details have been sent via email. Thanks!

yuntuowang commented 6 years ago

Through the email discussion with @YasithLokuge, implicit grant flow and authorization code grant flow works for him. Also, /oauth2/token endpoint works for him too.

anyeone commented 6 years ago

Hi @yuntuowang: Sorry to perform thread necro but I'm having similar problems that I can't seem to resolve.

I am getting an invalid client id error trading authorization code for token even though I can't find anything wrong.

I have triple checked that the client id I am using is the one on my App Client Settings page (I only have one) and all identity providers are enabled (in my case, cognito identity pool and facebook). I have both Authorization Code Grant and Implicit Grant both enabled as well as all scopes enabled.

I have a client secret, have triple checked that value is correct, and I am using it and the client id to form the "Authentication: Basic (base64 encoded value of clientid:clientsecret) and adding this header, in addition to the Content-Type x-www-form-urlencoded.

My code to create the base64 hash: client_id=settings.COGNITO_CLIENT_APP_ID secret = settings.COGNITO_CLIENT_SECRET secret_token = client_id + ":" + secret encode_string = base64.b64encode(secret_token.encode()) hash = base64.b64encode(encode_string).decode("utf-8")

This is what the request looks like in postman:

screen shot 2018-05-02 at 2 38 28 pm screen shot 2018-05-02 at 2 38 18 pm

Please advise, I do not see any reason this should not work.

sunsoori commented 6 years ago

@yuntuowang My access token doesn't work in API gateway but my Id token works. Any reason? I've attached a cognito user pool as an authorizer to one of the API in the API gateway.

sunsoori commented 6 years ago

is there a way to set the expiration time for access toekns in cognito? I want to set it to NEVER EXPIRE

yuntuowang commented 6 years ago

Hi @sunsoori , no way to set access token and id token to other expiration time. Their valid time is one hour. However, you can set the expiration time for refresh token when you create an app client on Cognito user pool console/CLI.

Please refer here:
https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html

davidhbigelow commented 6 years ago

First off -> Super Special Thanks to Crystal (@yuntuowang) for her awesome contributions to this thread!

Now for the journey and headaches we experienced and lessons learned in the first steps of implementing this our Serverless Web Application that will use S3 static pages, Cognito Login UI and Application Gateway with Lambda Functions to talk to S3 and RDS.

We are in the beginning stages of this so please excuse any obvious errors and politely correct them as we are still learning also. One HUGE frustration was that the Amazon Engineers seem to be blessing Angular, React and Vue in their examples (especially with Amplify docs - which we were tempted to use because some older AWS libraries are on the way out) without considering more conventional approaches like jQuery (which we use extensively). So finding actual examples of how to do this has been a PAIN!

Small RANT: We often feel that Amazon functionality is presented like a minimum of five (5) HUGE boxes of Legos form different kits without any final instructions of what they will make. As we route around the box of functionality, we see a lot of really cool pieces, but no real picture of how to connect them together to get to the final goals you have. The result has been a LOT of Dead Ends, or very late discovery that other functionality that is easier to use (like Cognito UI) but also somewhat of a pain to understand the requirements for it. I hope someone from Amazon sees this and takes initiative to improve it across all their documentation. (AWS docs often reads like meeting notes for a meeting we were never invited to and the people who wrote it understand completely).

That all being said - I wanted to share what we have learned after quite a few hours of testing with limited success in the hopes that it helps others trying to implement non-Angular/React/Vue/bla/bla/bla approaches.

---- The LOGIN URL ---- https://>>yourDomain<<.auth.us-east-1.amazoncognito.com/login?response_type=code&client_id=>>yourClientId<<&redirect_uri=>>yourRedirectURLEndpoint<<

yourDomain = obviously your your application root domain name

yourClientId = your specific App Client Id that was created when you defined the App Client -- NOTE: Web Apps do NOT require a App Client Secret ID, and they are NOT created by default.

response_type = code -- NOTE: This can be either "code" or "id_token" - the "id_token" produces the one (1) hour limited token directly, the id_token does NOT include a refresh_token! If you want to obtain the refresh_token, you must request the "code" response_type to use it later. (keep reading)

redirect_uri = Callback URL in your App Client Settings -- NOTE: Notice the "i" not "redirect_url" - this is MUST be the EXACT SAME as the Callback URL in your App Client Settings - it MUST ALSO be an HTTPS endpoint!

As a side note: on the App Client Settings - we flagged Authorization Code Grant and Implicit Grant as well as openid (we did not fully debug - but these seem to follow the advice of others).

Once you have the LOGIN URL properly configured - you will get a Login Screen from Cognito when you go to it in your Browser. It works as expected. If you get an error on the login screen (or a completely blank screen), it is likely a problem with the format of your LOGIN URL and/or your settings. Recheck everything -- especially the redirect_uri as it is often overlooked for both the variable name and correct and value.

---- Using the "code" Response to get a Refresh Token ---- Since our application has a goal of being used LONGER than one (1) hour for users, the id_token is not useful in this scenario. To get around this we discovered we needed the "code" instead and then use that to obtain the refreh_token by making a call to the following URL:

` https://>>yourDomain<<.auth.us-east-1.amazoncognito.com/oauth2/token

BUT the "code" that comes back has a few "quirks" you need to be aware of.

1) I read somewhere that the "code" returned is only valid for five (5) minutes. This may or may not be the case - Amazon can better confirm this. If this is the case, it would explain a lot of problems we were possibly having while trying to debug other areas of this transaction - when the darn thing was expired and not going to work anyway. (sigh).

2) The "code" can apparently ONLY BE USED ONCE (1x) to request the oatuh/token endpoint! We have confirmed this repeatedly using both live testing and stand alone testing via Postman (a cool app if you have not tried it - it saved our bacon on figuring this out!).

---- THE AJAX REQUEST via jQuery ---- Again, I know Angular/React/Vue are apparently what the hip kids are using these days, but we prefer the old-reliable jQuery for most of our apps (and I think a bunch of you still do also).

Assuming you have jQuery loaded properly - the AJAX request is pretty simple:


Helper Function for extracting "code" from the Resulting URL function getQueryVariable(variable) { var query = window.location.search.substring(1); var vars = query.split('&'); for (var i = 0; i < vars.length; i++) { var pair = vars[i].split('='); if (decodeURIComponent(pair[0]) == variable) { return decodeURIComponent(pair[1]); } } console.log('Query variable %s not found', variable); }

Extract the 'code' using the helper function, set to 'cognito_code' variable cognito_code = getQueryVariable('code');

AJAX Body Parameters var bodyParams = { grant_type: 'authorization_code', client_id : '>>yourClientId<<', redirect_uri : '>>yourRedirectURLEndpoint<<', code : cognito_code };

AJAX Request $.ajax({ url: 'https://>>yourDomain<<.auth.us-east-1.amazoncognito.com/oauth2/token', dataType: 'json', type: 'POST', data: bodyParams, success: function (dataObj) { console.log('SUCCESSFUL REFRESH REQUEST!'); console.log(JSON.stringify(dataObj)); }, error: function (request, status, err) { console.log("got to the error!" + JSON.stringify(status) + ' --> '+JSON.stringify(err)); } });


NOTE: dataType = 'json' -- IF you put in 'application/json' you will not get a 'success' event - it will result in a 'parseError' and defer to statusCode for the handling. (found this out the hard way). So to our surprise a simple 'json' dataType was all that was required to properly handle the 'success' event.

ALSO - you will read in the docs that "Headers" are required. Specifically "Authorization" = "Basic : and "Content-Type" = "application/x-www-form-urlencoded"

We FOUND THAT THIS WAS NOT NECESSARY OR REQUIRED for applications that DO NOT have a secret_id defined for the App Client! (this was a significant point of confusion and testing to figure out).

The ONLY THING that was required was the BODY of the AJAX Request to be setup properly with the request variables and values as per the example above.

THE RESULT of the above call will produce an Object - should look like the following:


{ "id_token":".....", "access_token":".....", "refresh_token":".....", "expires_in":3600, "token_type": "Bearer" }


I hope that this post was well received and useful to anyone trying to also figure this out. We have more to do with this, but this is a milestone that we achieved to get this "basic" thing talking with Cognito... Now on to the next steps and more testing....

Elayaraja-Dhanapal commented 6 years ago

Hi,

My code execution goes like below,

var auth = new AWSCognito.CognitoIdentityServiceProvider.CognitoAuth(authData); var curUrl = window.location.href; auth .parseCognitoWebResponse(curUrl); auth .getSession();

I am able to see the login screen, once I enter username and password and click the sign in button, the control goes to auth.userhandler onFailure method.

what might be the problem? How can I check?

Could you please help?

Thank you!