aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.44k stars 2.13k forks source link

Not able to get any OAuth or Custom Scopes in Access Token #3732

Open hanslai opened 5 years ago

hanslai commented 5 years ago

Which Category is your question related to? Cognito, Oauth2/OIDC Access Token

What AWS Services are you utilizing? Cognito User Pool

Provide additional details e.g. code snippets Using either Auth.signIn or the Vue Authentication Components are not able to get any OAuth or Custom Scopes.
oidc+scope

Sorry, I only have a image of the source my coworker sent me. Amplify+JS

I also tested, I was able to get the OAuth scopes if I use the Token Endpoint in Postman https://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html

Or do we have to use https://github.com/aws/amazon-cognito-auth-js to get the scopes? But with Amplify I wonder do we still need to use this amazon-cognito-auth-js library? Actually, I am confused why there are two JS libraries for cognito.

both #1884 and #1370 have the same problem, which is not solved but closed. It has been almost a year on this issues already. Any update on this?

haverchuck commented 5 years ago

@hanslai Have you tried using Auth.federatedSignIn as described here?

hanslai commented 5 years ago

@haverchuck, I thought FederatedSignIn is for social provider only, but our App does not use any social providers, we are only use Cognito for now. Or I didn't understand this correctly? Is it possible for me to use FederatedSignIn without any social provider but Cognito user pool only?

I understand that Cognito User Pool itself is a valid OAuth2/OIDC Identity Provider, so I was able to get the OAuth2 Scopes from the token endpoint. Yet, just not sure how to do it with Amplify.

haverchuck commented 5 years ago

@hanslai - Apologies- I misread your question. I'll try to look into this further.

hanslai commented 5 years ago

@haverchuck any update on this?

haverchuck commented 5 years ago

@hanslai I believe that if you want to use OAuth, you would still call federatedSignIn() in order to open the Hosted UI. The social provider piece would come into play only if you setup third party providers in the User Pool or passed a provider value into the function. Simply calling federatedSignIn() without any parameters would open up Hosted UI with a basic Cognito-backed login screen. This would enable you to authenticate the user with the userpool without handling their username/password directly in your application, which is one of the primary purposes of OAuth. Using the signin component, on the other hand, means that you are handling these credentials yourself.

To do this basic OAuth flow, you would need to setup a Hosted UI domain and redirect / signout URLs, which the Amplify CLI can assist you with.

stale[bot] commented 5 years ago

This issue has been automatically closed because of inactivity. Please open a new issue if are still encountering problems.

adryanf commented 5 years ago

This is a fairly critical issue in my opinion because having the access token without scopes makes it unusable when accessing an API gateway which is secured by specifying scopes. The only options left to have a functional workflow are either to use the hosted UI (which is really limited in terms of customization) OR to use the identity token as a form authorization (which is an anti pattern)

dominiktopp commented 5 years ago

Please make this work! We switched from Cognito Hosted UI to Amplify because of the very limited configuration options of Cognito. Now we are stuck because as @adryanf said, passing the id_token to our resource server (our backend) is an anti pattern.

We need to be able to send the access token to our backend and call the userinfo endpoint there to get some information about the user.

vgaltes commented 5 years ago

I´m having the same issue. I can´t get the custom scopes back using Amplify, I only get them back using the custom UI.

pmargom commented 4 years ago

Don't understand how this issue is closed when lot of people is requesting this feature!!

sandeepsdixit commented 4 years ago

Same problem here... How do I get custom scopes in access token?

JohnTheGray commented 4 years ago

Given that you can request custom scopes when authenticating using the hosted UI via the Cognito User Pool (as opposed to Facebook), I expect authentication via the Amplify Authenticator React/VueJS component to support exactly the same feature. In both cases (hosted UI or Authenticator component), you are still using Cognito User Pool as an IdP and should be able to request your desired scopes.

shukii commented 4 years ago

Any updates on this issue? I'm having the exact same problem

DavidWells commented 4 years ago

I'm also unable to protect API gateway endpoints with the scope only set to aws.cognito.signin.user.admin

For example, (using serverless framework), I'm only able to check for aws.cognito.signin.user.admin & not real scopes like user/add, user/delete etc.

functions:
  protectedViaScope:
    handler: src/protected.handler
    events:
      - http:
          path: protected
          method: post
          cors: true
          authorizer:
            type: COGNITO_USER_POOLS
            authorizerId:
              Ref: ApiGatewayAuthorizer
            scopes:
              # Hack/workaround: only aws.cognito.signin.user.admin works =( 
              - aws.cognito.signin.user.admin
              # 👇 wont work b/c scopes arent on the accessToken 
              # - users/delete
              # - users/add

How are people working around this amplify/cognito limitation? Only using IAM roles?

rupertlssmith commented 4 years ago

I think it is also true that not every user wants the admin scope

aws.cognito.signin.user.admin

A regular user of an application does not need user admin rights on Cognito, probably just wants some sort of read/write access to parts of an application back-end.

tamirrrdorrron commented 4 years ago

This is also an issue for us

LionelB5 commented 4 years ago

We are also impacted by this issue.

Very similar use case to @DavidWells. We have protected a few different API Gateway endpoints using different custom scopes and the only scope we ever get back when using AmplifyJS is the aws.cognito.signin.user.admin scope.

rubene commented 4 years ago

We are also impacted by this issue. Can't write e2e integration tests because the token does not have the necessary scopes to hit api gateway.

ddbradshaw commented 4 years ago

+1 on this being an issue for our team.

Amplify is useless if we want custom login UI and use API Gateway with custom scopes.

chrismillah commented 4 years ago

+1 also experiencing this issue

hasanbaloch commented 4 years ago

This issue is not just limited to Amplify. Amazon is forcing us to use to the Hosted UI.

No matter which cognito-app-client-id do we sign in with using SDKs, the resulting access token always contain the admin scope (aws.cognito.signin.user.admin). This is not a feature request but a bug report (if not a security loophole). What is the significance of cognito-app-client when its assigned scopes are never fetched in the access token?

elorzafe commented 4 years ago

Amplify is an open source library that allows customers to interact with Cloud Services such as Amazon Cognito.

Amazon Cognito provides a mechanism to get different scopes on the access token. This mechanism is only supported on OAuth flow using Cognito HostedUI. We’ve communicated the feedback to the Cognito team for their backlog, but I would also encourage you to open a case through your support channels to provide direct feedback outside of the GitHub forums.

DavidWells commented 4 years ago

Any word from Cognito team on this?

Many folks are experiencing this https://github.com/aws-amplify/aws-sdk-android/issues/684

Thanks for your help

AndresMWeber commented 4 years ago

We are using it as expected: to have custom authorization per token via scopes for our service without using the hosted UI (to have a more seamless login experience), however we had to home brew it for now. It would be amazing to get this functionality so no one has to hand roll anything that shouldn't be missing.

tobilg commented 4 years ago

@AndresMWeber could you elaborate how you created your home brewn solution?

AndresMWeber commented 4 years ago

@AndresMWeber could you elaborate how you created your home brewn solution?

It's tailored to our specific use case, however we validate with an API Gateway Lambda authorizer by doing our own Cognito queries against additional headers we send with the request. So really it's not that we solved the scopes issue itself, just bypassed it.

mattmarcum commented 4 years ago

Is someone going to reopen this or should we create a new bug report...?

rupertlssmith commented 4 years ago

Is someone going to reopen this or should we create a new bug report...?

The problem is that the bug is not in amplify, it is in cognito which is not an open source project. I don't know where we can get visbility into what is happening with cognito, if will be fixed, if anyone is even looking at it or cares.

This issue is also a duplicate of this one, which is still open:

https://github.com/aws-amplify/aws-sdk-android/issues/684

hheavener-kyd commented 4 years ago

This is the biggest letdown I've had with AWS. I spent a couple of weeks learning Cognito and building various functions with Amplify to test out how everything works and then come to find out I can't even control the scope of the access token. How can you provide an IDP platform without the ability to define the scopes of the access token? This is complete nonsense and now we're having to investigate other platforms.

@haverchuck are you still a member of the team working on Amplify? Can you give us any updates on this situation? A lot of people here are desperate for an update.

jj0b commented 4 years ago

I also just discovered that if I use Amplify without the hosted UI that I'm not able to get scopes with the access token. This makes Amplify an unusable solution for me, as I expect it will for anyone else needing to use scopes to gate access to their resources.

I can get an access token with scopes from the Cognito authorization server without using the hosted UI, so it seems it should be possible to have Amplify get them as well. Until then, it seems that this limitation in Amplify (and from what I can see it is an issue with Amplify, not Cognito) is just encouraging people to implement a less than ideal solution for authorizing access to their resources.

DavidWells commented 4 years ago

I can get an access token with scopes from the Cognito authorization server

@jj0b interesting... How are you doing this? 😃

Also, are you using the raw cognito sdk's in your client instead of amplify's Auth package?

jj0b commented 4 years ago

@DavidWells in my client that is using Amplify I'm using the Auth.SignIn method, not raw Cognito SDK, which gets me the token but not the scopes.

In a separate client that needs to access the same resources I am not using Amplify at all; instead I am using an OAuth 2 Client Credentials Grant flow (this client is a Raspberry Pi) and I am able to get the token as well as the scopes in that case by making a request to the Cognito authorization server.

SeFeX commented 4 years ago

Hello @jj0b ! I am using amplify sdk for javascript on the frontend and I have another client for the backend in spring boot, and it is configured as resource server, can you explain the OAuth2 client flow to get user authentication information? The access token that the frontend sends me does not have enough access to obtain the user's information from the url xxxx.amazoncognito.com/oauth2/userInfo

jj0b commented 4 years ago

@SeFeX the OAuth 2 Client Credentials Grant flow did not give me access to user authentication information. That flow is for machine-to-machine authorization, which is what I used it for. It returned an access token and scopes that I used to authorize between a backend application running on on a machine, with a service on AWS.

SeFeX commented 4 years ago

@jj0b So that token I use to obtain via api or sdk aws the user information of cognito?

elorzafe commented 4 years ago

I reopened this is issue, and mark it as a feature-request for the service team.

elorzafe commented 4 years ago

Currently from the client (Mobile and Web) the only option available to access custom scopes on the access token is using Cognito Hosted UI.

if you use Auth.signIn it will return the default scope.

The scopes that appear on oauth param are only valid when using Cognito Hosted UI.

Can anyone mention on this ticket, what is the minimum thing that will allow you to use Cognito Hosted UI?

elorzafe commented 4 years ago

@DavidWells

I want to understand more how the scope from the token will protect your API. Because what I understand is that scopes are configured on the App Client settings and they are not specific to the user unless I am missing something.

Wouldnt be more useful to use AWS IAM to restrict access to the endpoint/path?

SeFeX commented 4 years ago

@elorzafe

Hi! I would like to answer in my case, what I understand is that IAM is the user system for the AWS root account, but a user management system is needed for an app developed by itself, with specific accesses, it would be the database of users with their respective authentication, it is more efficient for cognito to maintain authorization and we only validate the token information from the backend, that is what I am trying to do, I see that it could work with a lambda function, but it would be one more layer on aws and not directly in the backend of the application

ravenscar commented 3 years ago

@elorzafe I will attempt to explain how I think scopes would help us, and also what hosted UI could do better.

We are currently looking to migrate two user bases from other systems, one is LDAP and the other is from auth0. These represent two different sets of users for us and are owned by different business units.

The LDAP users use a desktop app, it already has a login screen and we don't want to change how it looks. We would like this app to use the cognito-idp apis and have scopes available based on the client. Beta users could be moved to a new client with additional scopes not available to typical users.

This client uses some embedded web apis which require certain scopes. With other oauth implementations it has been possible to reduce the scope of the access token on a refresh, we would like to do this so the main refresh token can be used to generate a temporary access token with a single scope for a web api on an access server.

We allow access to an API base on a iss/scope whitelist. If you have a permitted iss and the right scope you can access this API, without the ability to use scopes. Without the ability to set scopes in the api calls you have to use hosted ui, and even if you can get past the look and feel of it (which has been a constant constant source of complaints) then it has its own set of limitations.

Allow pre-setting the email/username/phone. Both sets of users can use the same apps on a website. It would be nice ask for their email address first before redirecting to the hosted UI, then just ask for their password on the hosted UI. As it is we have to ask them, direct to the appropriate hosted UI, then they have to fill out their email address again. How frustrating this must be for users.

Silent refresh. Hosted UI must start supporting silent refresh! Storing the refresh token in local storage as amplify does is nuts! How can anyone take cognito seriously as a security product whilst you are advocating this ridiculousness!

Hosted UI error messages should be customisable.

Support passwordless. The users moving from auth0 are sent passwordless links to their mail. These have a seven day expiry, the AWS custom auth flow has a 3 minute expiry so this is not a great fit. It would be nice if Hosted UI would support passwordless links with a longer expiry. Or even passwordless links out of the box.

Fix device tracking. At the moment if you want to use device tracking then you cannot use USER_PASSWORD_AUTH properly as it is incompatible with device tracking and therefore the refresh token cannot be used.

Use SRP. The ENTIRE reason to use SRP is so the password is never transmitted in plain text even over SSL. However I note that when ALLOW_USER_SRP_AUTH is enabled and ALLOW_USER_PASSWORD_AUTH is disabled... The password is still send across the wire to the HOSTED_UI. I would love to know why the cognito team wants people to jump through so many hoops to use SRP (which is mandatory if you are using the cognito-idp api with device tracking turned on in the pool) when they don't even bother to use it themselves, and lazily send the password themselves.

Document things please. The USER_SRP_AUTH method is completely undocumented. It does not follow the RFC as it uses a different value for the generator (02 when the RFC has 05), the hash padding is weird (typically its padded to the size of the prime number, but not in cognito) and is completely incompatible with every compliant javascript implementation of SRP I found outside of the cognito-identity-js one. Go put "Caldera Derived Key" into google and find the top dozen results being people frustrated trying to use USER_SRP_AUTH.

Anyway, I hope this helps the Amplify and Cognito teams understand a few of the shortcomings of their products from the perspective of someone who has been instructed to use them.

hanslai commented 3 years ago

@elorzafe Scope value can protect WebApi because Cognito's Resource Server's Scopes are returned as scope in Access Token by Cognito.

API have to examine if the Access Token contain this resource in its scope before it allow authorization to the Api resource.

Returning the custom scope is particularly important for Cognito, because Cognito Access Token does not include "Aud", (There are issues complaint about this for over a year, but nothing is done also.), if "Aud" is include, API can just check if Access Token contain the correct value of "Aud", then us as programmer can write a lot less code to check authorization.

and I agree with @ravenscar that Cognito's different flow are very poor documented, that makes Cognito very difficult to use.

ravenscar commented 3 years ago

It's so weird that AWS have implemented the ability to use scopes to protect resources in API Gateway but not set them up to be useful in Cognito. So if you are using a compliant authorization server then you can have a finer grained control over scopes and then all good with APIGW but if you're using User Pools then:

It's like the Cognito team took all the practices people were using for years and threw them out the window.

I especially like the little quirks like when you turn on mandatory device tracking that Cognito won't let you use a refresh token without sending the device id... But it gives you access tokens before you confirm a new device.

Hey, I like the pricing though!

hanslai commented 3 years ago

Even I am still trying to use Cognito myself because I try to use the same cloud provider for everything, but actually Azure AD B2C has the same price now. 50,000 user free per month. And I learned people have switched to other IDP due to the fact that Cognito is difficult to use especially for Blazor programmers.

tobilg commented 3 years ago

Basically @ravenscar summed up all the concerns an problems we're having. Thanks for the write-up!

rolandnyamo commented 3 years ago

ugh. been chasing an issue for hours, almost going crazy. but this was it. I wonder what we can do to prioritize it. It's not like cognito is opensource. In the meantime, I guess add the user.admin scope to all API calls?

michaelbrewer commented 3 years ago

@ravenscar sums it up well. I really hope the Congnito team has the time to start looking at this growing backlog of issues. And it is not like we have much of a choice.

tobilg commented 3 years ago

We're currently using a kind of workaround where we use id tokens instead of access tokens, because they can be updated/verified/etc. with the PreToken Lambda trigger. If I understand it correctly, if a user knows which scopes are available within the hosted UI, he/she could assign the scopes to him/herself by just adding them to the /token or /authorize URL, and there's no current way to verify if a user is allowed to have these scopes or not.

This is less than ideal, because we need to use REST instead of HTTP APIs on API Gateway, because the latter doesn't support private endpoints (yet).

When using REST, you loose the ability to just directly use the Cognito Authorizer, because the builtin one does check for the token type, and will only verify access tokens, but no id token (why this is the case, no one knows)... You can adapt the Lambda integration so that you can add the desired scopes as properties, which then works again with id tokens. But that comes to the prize that there's a lot more IAC to write, and handling the Lambda responses/errors gets much more inconvenient.

So we ended up writing a custom Lambda authorizer, which basically does what the Cognito authorizer does, to just be able to use custom verified scopes in our id tokens.

Is there something we misunderstood here? This is much too complex/incomplete to be trustfully run in such an important area like authorization/authentication.

disbelief commented 3 years ago

I wasted 3 days trying to figure out why the access tokens that Amplify Auth fetches couldn't access my API Gateway methods. Naturally, I assumed it was a problem with my infrastructure config (which is non-trivial), but it seems I should have just searched the Amplify bugs list first.

And yes this seems a lot more like a bug than a "feature request".

Using Amplify + Cognito to authorize API Gateway methods is one of the most common and AWS-promoted scenarios. I'm utterly baffled as to why this doesn't already work, and has not worked for years.

I'm also baffled this limitation isn't mentioned in any of the docs. In fact there are places in the docs that indicate it works as expected (eg. "If the client doesn't request any scopes, the authentication server uses all custom scopes associated with the client").

</rant>

@hasanbaloch and/or @rupertlssmith you mentioned above that the bug lies in Cognito. Just to confirm, this means that the only way to get an access token with other scopes is via the Hosted UI? Ie. if I try to implement the Cognito token API request myself with an HTTP library I'd have the same problem?

@tobilg thanks for describing your workaround. I'm curious why you felt that the preToken Cognito Lambda trigger is more complex/error-prone than a custom Lambda Authorizer? If you hard-code the scopes in the preToken Lambda trigger at least you can avoid the user guessing them and the Lambda just blindly adding them. What are the failure modes here?

ravenscar commented 3 years ago

@disbelief I may be able to answer these questions as I have been writing a library for dealing with cognito and I have a few integration tests which tries stuff like this.

If you get a refresh token via the API calls, rather than Hosted UI, you will not be able to use it in the https://dewfewfewfewf.auth.ap-southeast-2.amazoncognito.com/oauth2/token endpoint. You will get a response such as {"error":"invalid_client"}. To use that endpoint you must obtain a refresh token via the hosted UI's code_flow.

As far as I am aware there is no way to get any OAuth scopes other than via the Hosted UI. I and my team have tried many many things and also contacted AWS support using our enterprise plan, and we have been told the same by them.

Also please note that tobilg's workaround only modifies the id token, not the access token, so it is useless for OAuth2/OIDC. Scopes have no purpose on the id token, e.g. you could call it anything at that point.

For me I can do without scopes but not being able to use something like code_flow with the API is a real pain when you are doing mobile development with your own embedded UI, as you have no secure way to deliver the tokens back to your app.

As for what are the failure modes? Well who knows, all this is pretty far of the reservation. Cognito's lack of flexibility has caused some people to go to extreme lengths to get basic things working.

If this service was provided by another company and not amazon I am fairly sure they would have almost no customers given the offering they provide.

disbelief commented 3 years ago

Thanks @ravenscar for the run down, saved me surely quite a few hours (or days) trying to implement myself. I did try a couple more things without any luck.

First I experimented with hitting the token endpoint directly in Postman but couldn't get it working (invalid_client was a common response).

I also tried the preToken trigger that tobilg mentioned, but as you say the scopes only get added to the id token, and API Gateway doesn't accept that token regardless.

In the end I've decided to "roll my own" custom authorizer lambda, which talks to Cognito directly to verify ID tokens. I don't have fine-grained scopes in my system — all users can access the same APIs — so it is enough for me to just verify the user is authenticated.

It goes against security best practices and defeats the purpose of OAuth2 + OIDC, but it seems AWS people are comfortable with offering their customers this level of compliance.