TLDR; In this post I am describing a solution where a web application is the resource owner (client): the app then itself and internally grants access rights to the users, which is fairly common use case for web apps which use Role Based Access Control (RBAC). If and when there is an API Gateway as a part of the solution, its role is only for unifying and managing access to all APIs: so basically a single access point for functionalities like TCP/IP -level access control, billing, rate-limiting, etc. But basically a proxy that does not handle (OAuth2) authorization. I will be using OpenID Connect (OIDC).
RFC7662 “defines a protocol that allows authorized protected resources to query the authorization server to determine the set of metadata for a given token that was presented to them by an OAuth 2.0 client.” My solution also has some differences to the “normal” scenarios aka the Delta:
- There is a separate User Identity Provider” (UIdP) for user AuthN and self-service
- There is a separate Client Identity Provider (CIdP) for registering and un-registering clients i.e. client AuthN. Normally this would be a OAuth2 endpoint but, in my opinion, both users and clients should be separated from the rest of the OAuth functionalities to improve clarify on responsibilities and to reduce overall complexity.
- This means that Authorization Server needs be able to persist information about clients, users, information about which users are bound to which clients, which resource there are, and which resources are bound to which clients. In my opinion AuthZ should be done using ABAC (or should I say graph-based ABAC)!
- Self-contained access token will be used!
Different solution component responsibilities are:
- Authorization Server grants self-contained Access Tokens (JWTs). It also serves a public key for validating signed JWTs so that Resource Server learns it can trust the content of the self-contained access token i.e. trust will be established
- Resource Server needs to do certain things:
- Is authorization server an allowed server?
- Does public key of the server validate the access token?
- Is it expired or not?
- Is this resource server its intended audience?
- Is the requested resource a correct one?
- Client app needs to pass the access token to a user when needed
For my solution, the correct OAuth2 flow is Client Credentials Grant flow (see pic).
There is a good set of design strategy on how to do this on larger systems:
- OAuth 2 Access Token Usage Strategies for Multiple Resources (APIs): Part 1
- OAuth 2 Access Token Usage Strategies for Multiple Resources (APIs): Part 2
- OAuth 2 Access Token Usage Strategies for Multiple Resources (APIs): Part 3
Summary of these posts is that the core concept is Access Token that grants the access to a resource using the following JWT claims:
- “aud” – audience
- “scope” – scope
Example of requesting an Access Token by usingĀ multiple “resource” parameters. The following code is from pingidentity.com. The Resource describes the desired audience for the access token being requested and the Scope describes the desired scopes for the access token being requested. A popular format for access token is Json Web Token (JWT). Request uses “resource” and “scope”, and the response uses “aud” and “scope”. In the simplest case, there is only implicit resource information available: “The OAuth2 authorization request does not explicitly specify the target resource that the client application wants to access. Instead, the client application indicates the target resource by including its URI (Uniform Resource Identifier) as part of the request to the resource server.” So in summary you could say that OAuth2 access token “says” that whoever is accessing the current (URI) resource with a valid token is authorized to do so to the level defined in the scope.
// Request Access Token (JWT) GET /as/authorization.oauth2 ?response_type=code &client_id=a_blog_post_client_id &state=jdodavcljkvbpoviojdkaacvxiuzcxoivulv &redirect_uri=https%3A%2F%2Fapp1t%2Eiyasec%2Eio%2Fcallback &scope=calendar%20contacts &resource=https%3A%2F%2Fapp1.iyasec.io%2F &resource=https%3A%2F%2Fapp2.iyasec.io%2F HTTP/1.1 // Response token { "iss": "https://idp.iyasec.io", "sub": "iavlkklmsfiilblzlkjvc", "exp": 1403203400, "scope": "read", "aud": "https://app1.iyasec.io/" }
The JWT acting as an access token contains the audience claim that describes the resource (API) to which it belongs. It also contains the scope claim that describes what actions can be taken on the API in quest. The Resource Server should do following things:
- do validity checking as described above
- ‘utilize self-contained JWT checking
- use transparent reverse proxy (e.g. Jetty)
- map “aud” and “scope” to a remote resource
- allow dynamically (runtime) define
- resources
- resource urls
- whitelist “aud”
- whitelist “scope”
- do management with GUI
- note! this level should not know anything about users or clients! however, the AT must given only to a user in the context of a client!
So basically, given a valid JWT Access Token, the Resource Server would return the content of a resource from a hidden place. Access token can, and maybe it should, contain user and client information, but that information is irrelevant at the RS! “I don’t care you you are but you have a correct key to a certain lock, so you can open the lock and access the resource”.
To summarize
- Authorization Server grants self-contained JWT Access Tokens with the following claims: token id, user id, resource id, audience, scope, etc.
- Resource Server trusts JWT Access Token claims after the validation. It will act as a transparent reverse proxy when allowing access to the requested resource
P.S. An API gateway like Kong (together with JWT plugin) can act similarly meaning it can serve as a Resource Server too!
You must be logged in to post a comment.