3

I've read docs and seen this. Struggling to put Cognito + API GW + OAuth2 pieces together. My questions:

  1. Do I correctly understand the flow and use of Resource server scopes: client app asks the Cognito user pool for a JWT token (login/authorization happens). Request for a token contains custom scope A so as the Cognito returned JWT access token. After that client app uses obtained token making a REST API call to a "resource server" (say, to our configured API GW endpoint). API GW endpoint is set to use our Cognito user pool as authorizer + scope is set to be custom scope A. Thus scope here acts like a "role" or "permission": if client has a valid JWT token + this token has a custom scope A inside + API GW endpoint is set to use that scope - then client app is authorized to call API GW endpoint. Effectively it acts like a "resource-based IAM policy" for endpoint but no IAM is involved here.
  2. Do I correctly understand that AWS Cognito Resource server identifier is an arbitrary string? It is not the URI of a factual "resource server" (our API GW). URI format is used purely for uniqueness and there is no place in flow where Cognito Resource server identifier matters or somehow checked/validated? Also it looks like that Resource server identifier does not affect JWT token generation or token contents?

thanks for clarif.

Max
  • 1,741
  • 3
  • 23
  • 40

1 Answers1

5
  1. Application scope should not be confused with user permission. Scopes define the access an application has to the user's resources. Therefore resource access is the overlap of the two:
    • Check that the user has access
    • Check that the application has scope (access to user acccess)

Example

2 Clients with scopes:

  • A: E-Commerce (product:create, product:remove, order:create, order:update-status, order:read-status)
  • B: 3rd-party Order Tracker (order:read-status)

2 Users with permissions:

  • Customer (order:create:owned, order:read-status:owned)
  • Admin (product:all, order:all)

Therefore…

  • Customer PUT /product with client A = DENY due to missing user permission
  • Admin PUT /product with client B = DENY due to missing scope …
  1. By requiring a URI, AWS seems to enforce collision resistant identifiers as you point out. It's not a common practice and doesn't really have any real security benefit, nor is it validated by AWS that you control the resource.
Andrew Gillis
  • 3,250
  • 2
  • 13
  • 15
  • Andrew, thx. May be you could shed some light on `scopes` for me. Imagine, for #1 question, we do not use API GW but we implement our own REST endpoint + our own authorization and validation of incoming token. Is the mere presence of scope=A in incoming token considered a proof that client app has right to access the endpoint? – Max Nov 30 '21 at 14:20
  • @Max I've updated it with a bit more detail. Scope isn't something that proves access on it's own. You must consider the access of the token subject/principal. – Andrew Gillis Nov 30 '21 at 14:24
  • bear with me, there is smth i still miss in whole process. my understanding: app client directs user to a login screen, user approves app client may have access to resource server resources (in the same time this means app client will have all scopes it needs), so later, when app client call with token in header comes to API GW authorizer, authorizer checks token contains the needed scope. – Max Nov 30 '21 at 14:31
  • @Max Yeah so API GW scopes take care of part of the authorization, they validate the token and check that the required scope exists. You still have to verify the user the token was issued on behalf of has access to the resource. – Andrew Gillis Nov 30 '21 at 14:35
  • `You still have to verify the user the token was issued on behalf of has access to the resource` - how and where this check is performed? we denote "Cognito user pool" as authorizer in API GW endpoint, and User pools seem to be just a user directory... I know Cognito user identity has relation to IAM role which could potentially check access to resource? Still missing some bit... – Max Nov 30 '21 at 14:42
  • 1
    @Max Well this depends on your application. Don't bring in AWS IAM, that is different and deals strictly with access to AWS resources. For your application you define access requirements. As a simple example, if the user owns s3 objects, include the subject ID in the prefix and compare it to the `sub` value in the token before granting access. If the user has records in a DB, ensure the `sub` value matches the `owner` column. – Andrew Gillis Nov 30 '21 at 14:48
  • you mean smth like writing a Lambda authorizer that would be plugged to my mentioned API GW endpoint instead of a Cognito user pool? that lambda fn would receive JWT access token and make further decisions. or you mean passing JWT token furtner to the backend that sits behind the API GW? – Max Nov 30 '21 at 14:54
  • 1
    @Max Yeah that’s one possibility with a custom authorizer. It’s more common to split authorization checks between your api gateway and you handler however. [This blog post](https://www.alexdebrie.com/posts/lambda-custom-authorizers/#full-authorization) has some more information. – Andrew Gillis Nov 30 '21 at 15:07
  • cheers. Andrew, thx for blog post + for this separation between app VS user permissions. i used to think like "user delegates permissions given to him to app client" - and that is authorized in 1 step. – Max Nov 30 '21 at 15:13
  • 1
    @Max No problem. OAuth (scopes in particular) can be confusing so I'm glad I could help you make sense of it. If you are interested in learning more I'd suggest reading some of Aaron Parecki's stuff at https://oauth.net/articles/ – Andrew Gillis Nov 30 '21 at 15:23
  • @AndrewGillis I read all of this and got completely confused, at first your statement is that scopes need additional proof the user has access, but how so? If you modify a JWT token it will no longer validate. Later, your example indeed compares against the scopes in the tokens. Not commenting to argue about it, just confused because I'm writing a system right now where I'm considering adding a "Client" per "user"/api key, and adding a scope for each product the client has access to. Does this align with what you're saying? – Dan Chase Oct 03 '22 at 03:26
  • @DanChase So you are looking to do something closer to a [client credentials grant](https://oauth.net/2/grant-types/client-credentials/). Conceptually, the "user" in that case is the oauth client itself therefore access *could* be expressed solely with scopes However if users exist as a security principal in your application another grant type *should* be preferred. I'll update the answer with a simple example to make the difference clearer. – Andrew Gillis Oct 03 '22 at 18:01
  • @AndrewGillis If I am going to have let's say 100 different API keys with various access each, I could either use a separate client for each and do client credentials grant, or I could just pretend they're users and do a User Pool and use groups, and even though the "usernames" are API keys and the "passwords" are actually API key secrets, would there be any issue fundamentally? Going crazy, If I just use client credentials grant I will have to manage the groups myself instead of letting the user pool do it for me right? – Dan Chase Nov 25 '22 at 22:10
  • @DanChase I'd definitely recommend against any client credential approach with cognito. It is notably lacking any way to change the client secret which is a non-starter for more security sensitive applications. – Andrew Gillis Nov 27 '22 at 01:01
  • @AndrewGillis Yes and I also just like the ability to assign keys to groups within Cognito, otherwise I'd need to re-implement something new that's already there in user pools. – Dan Chase Nov 27 '22 at 16:49