OAuth2 Is Difficult: How To Integrate Authorization Server, Resource Server and Client App?

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:

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!

Designing OAuth2 Resource Server (RS)

How to design a OAuth2 Resource Server (RS)? Do I even care – shouldn’t there be already several existing open source implementations? Well, not exactly. The only implementation I have found is Authlete. In addition, the OAuth2 specifications are not covering Resource server implementation.

Different things to consider:

In OAuth2, you grant a (client) application to access a resource in RS on behalf of the user / resource owner / you. The client needs have an access token in order to access the requested resource. The level of access is defined as scopes, which are part of the OAuth2 standards.

What does a scope mean? Oauth2 scopes are used to express what app can do on behalf of a user. They are used for handling delegation scenarios. Resource endpoints are responsible for combining the incoming scopes and actual user privileges to decide effective permissions and make access control decisions. In a way, scope is a aggregate / interface which is then implemented on RS – and not on the Authorization server (AS)! In AS, clients are managed, all available scopes are managed, and UI for allowing user to give consents are there.

Resource server access control should start with HTTP CRUD access control: i.e. POST, GET, PUT, DELETE. See my other post about organizing REST APIs! Identity of a user comes from a same domain JWT ID Token, so the user information is present. Note that scopes are client specific and they are basically transformed into access control policies! What I mean by this is that actually scopes cover only a subset of all access rights in RS! So what people usually get wrong when designing a RS is that they think that scopes are the basis of controlling access to resources. Unfortunately, I haven’t found any good resources on designing this part – have you?

OIDC Is Just OAuth2 Flow

What is OAuth2? It is a security framework that can be used to grant a Client Application a right to access a (remote) resource that is usually some digital object. For OpenID Connect (OIDC) that particular resource is the “openid” user identifier. The reason for writing this post is that it took for me quite long to finally understand that OIDC is ultimately OAuth2. The two images below depict the operation of the OAuth2 in different ways. The core idea is that to access a resource like “openid” you need a valid access token. So it is fair to say that OIDC extends OAuth2, but the spec is not very clear where for example authentication takes place or where the OpenID Provider (OP) is located. The conclusion I came to was that there could have been a better way to specify the OIDC. How? Well, Identity Provider (IdP) could have been specified as a separate server i.e. Identity server. Also, separate Authorization server (Authority server) should be present, and finally the “openid” should be located in the Resource server as an object! Note that Relying Party (RP) is the client app that receives the Access token

oidc is just oauth2

(source: oracle.com)