Now that I make sense of keycloak, openid connect, oauth 2.0, jwt, jws, I know how to create a client that connects to an authorization server, gets an access token and gets resources from the resource server.
What if I want to create my own resource server ?
There is already a flow defined on OpenID Connect to validate the structured ID Token. This is defined with only communications between the authorization server and the client in mind (see OpenID Connect Discovery).
- the client (C) initially gets the public keys of the authorization server (AS), encoded in JWKS,
- the client (C) use an OAuth 2.0 flow to get an ID Token,
- the client (C) validates the ID Token using the public key,
See how the resource server is left alone in this flow?
The OpenID Connect Discovery inspired RFC8414, defining the well-known/oauth-authorization-server endpoint. Like its inspiration, it does not deal with resource server at all. It only takes case of defining how the client (relying party in OIDC language) discusses with the authorization server (the OpenID Connect Provider in OIDC language).
It’s to be noted that in most cases, the client can only use asymmetric cryptography (and thus a public key) to validate the token. Using HS256 would make no sense in the case of a web client for instance.
Also, OpenID Connect actually says that the client must validate the access token, but because there is no certainty that the access token is signed, it describes an algorithm using the hash of the authorization server that the provider must return alongside the ID Token.
We will not find indication of how to make the authorization server and the resource server communication in OIDC, but what about OAuth 2.0? It does not say much about how the access token should be constructed or validated. This is even reminded in rfc7662 1, that describes a mean to call an introspection endpoint to get information about the token.
The only thing it claims is that the access token should be opaque for the client, because even though the client bears it, it is meant to be read by the resource server (see is the bearer token opaque?)
It a later standards, OAuth 2.0 suggests a profile for self-encoded access tokens and emphasizes that the client MUST not read it2, but it does not say more about how the signature is validated.
We would like very much to have some standard to tell us how the resource server is supposed to get access to the public keys (using a well-known url for instance) that allow validating an access token in case we use a self-encoded access token.
It can be noted that since the resource server and the authorization server are expected to be secured, we could use a predefined secret and use a symmetric scheme such as HS256. But I definitely prefer using an asymmetric scheme that allows, among other things, key rotation.
In short, I would like to do the same kind of flow that OIDC has, but with the resource server.
- the resource server (RS) initially gets the public keys of the authorization server (AS), ideally encoded in JWKS,
- the client (C) gets an access token (AT) using an OAuth 2.0 flow,
- the client (C) sends the access token (AT) to the resource server (RS) to get access to some resources,
- the resource server (RS) validates the access token (AT) using the previously got public key,
Of course, I would like to keep the OIDC flow as well. Therefore, the more complete flow I would like to have looks like this:
- the client (C) and the resource server (RS) would initially communicate with the authorization server (AS) to get its public keys, ideally encoded in JWKS.
- using an OAuth 2.0 flow, the client would get a self-encoded access token (AT) as well as an ID Token,
- the client (C) would validate the ID Token (actually the access token (AT) as well, but I don’t go into the details here),
- the client (C) would communicate with the resource server (RS) using the access token (AT),
- the resource server (RS) would use the public key to validate the access token (AT) before doing any more verification.
The OIDC is clearly defined in the standard, hence there are plenty of good implementation to deal with it.
The second one appears to be what a lot of people do internally, but the lack of standard around it appears to cause a lack of implementations. It looks like people write this from scratch.
The lack of standard around this flow also causes that I don’t even know the exact keywords to look for. When I try “validate jwt oidc resource server”, I find plenty of implementations of either the OAuth 2.0 Token Introspection mechanism, of the standard OpenID Connect Discovery flow. To be honest, if I created the library to implement my flow, I would not know how to call it.
- keycloak provides self-encoded access tokens signed with a key available via its well-known/openid-configuration entry point and suggest we implement its flow ourselves 3, 4.
- https://pypi.org/project/fastapi-resource-server, gets use of well-known/openid-configuration to validate the access token from the resource server. It has the issue of the bad naming I mentioned earlier. Nothings in its name suggests it uses an OpenID Connect Discovery mechanism under the hood.
- https://robertoprevato.github.io/Validating-JWT-Bearer-tokens-from-Azure-AD-in-Python/ tries to validate the access token fetching the well-known/openid-configuration keys,
- in this python init episode, the author of https://django-hurricane.io/ explains that they use a side car container to use the OIDC flow in their backend.
Trying to validate the access token in python
Now, let’s get our hands dirty.
I will assume I use keycloak to issue JWS encoded access tokens. I will also assume that I use asymmetric cryptography (RS256) and not HMAC (HS256).
When the client connects to the resource server to get access to the resources, the resource server needs to validate this token using the public key of the authorization server.
The flow I want to do is:
- get https://mykeycloak/auth/realms/myrealm/.well-known/openid-configuration
- get the well_knowns["jwks_uri"] in JWKS format
- use the JWK key that as the jwk[“kid”] == my_access_token.jws_header[“kid”]
- check the token validity using that key
I could reconstruct an x509 aware certificate, using x5c with a code like this (https://community.auth0.com/t/token-validation-with-python/21589).
from cryptography.x509 import load_pem_x509_certificate from cryptography.hazmat.backends import default_backend import jwt import textwrap def extract_public_key(cert): cert_string = textwrap.wrap(cert, width=64) cert = '-----BEGIN CERTIFICATE-----\n' for line in cert_string: cert += line + '\n' cert += '-----END CERTIFICATE-----\n' cert_obj = load_pem_x509_certificate(cert.encode(), default_backend()) return cert_obj.public_key() def validate_token(token, jwks, env): public_key = extract_public_key(jwks['x5c']) return jwt.decode(token['access_token'], public_key, audience=env['audience'], algorithms=['RS256'])
This can be simplified using a jwks aware library. This library already takes care of finding the appropriate key in the key set.
from jwks import TokenValidator v = TokenValidator(jwks_uri=well_known["jwks_uri"], audience="myaudience", issuer="https://mykeycloak/auth/realms/myrealm") v.validate_token(acces_token)
I want to have more than that, I want to only provide the root url and let the library use well-known/openid-configuration to find the keys.
In the case of python (more particularly if my server is in fastapi), someone already did this: https://pypi.org/project/fastapi-resource-server/.
That way I only have to provide the url to the realm and let fastapi-resource-server use OpenID Connect Discovery to find the keys.
The given example is pretty self explicit and show exactly what I want.
In more generic OAuth 2.0 compatible terms, it looks like this:
class Token(BaseModel): claim1: str claim2: str auth_scheme = OidcResourceServer("http://localhost:8888/auth/realms/master") def get_token(claims: dict = Security(auth_scheme)): return Token.parse_obj(claims) @app.get("/resource") def resource(token: Token = Depends(get_token)): if token.claim1 == "foo" and token.claim2 == "bar": return "Some Resource"
Finding a golang library
Now, I want to do this in golang go.
There are a few libraries dealing with OIDC, like https://github.com/coreos/go-oidc, but as mentioned above, they all deal with implementing a relying party.
Then, to get the JWKS and validate the signature I can:
- either write the code that fetches well-known/openid-configuration, then use a library to deal with JWKS,
- or use a relying party library.
Because most of the security checks5 are part of the good practices when
Using an API Gateway like krakend
As a matter of fact, we might want to delegate the work of checking an access token to a component in front of the one dealing with the domain logic.
As a matter of fact, it sounds like a job for an API Gateway.
By a quick search, it sounds like KrakenD already does that:
It deal with all the details, like key rotation.
KrakenD can validate the Keycloak signature by itself, it does not need to call the Keycloak server to validate the token every time. Instead, KrakenD queries Keycloak every 15 minutes (configurable) to ensure the key has not rotated
OAuth 2.0 does not define a protocol for the resource server to learn meta-information about a token that it has received from an authorization server
The client MUST NOT inspect the content of the access token: the authorization server and the resource server might decide to change the token format at any time (for example, by switching from this profile to opaque tokens); hence, any logic in the client relying on the ability to read the access token content would break without recourse. The OAuth 2.0 framework assumes that access tokens are treated as opaque by clients.
Validating access tokens
If you need to manually validate access tokens issued by Keycloak you can invoke the Introspection Endpoint. The downside to this approach is that you have to make a network invocation to the Keycloak server. This can be slow and possibly overload the server if you have too many validation requests going on at the same time. Keycloak issued access tokens are JSON Web Tokens (JWT) digitally signed and encoded using JSON Web Signature (JWS). Because they are encoded in this way, this allows you to locally validate access tokens using the public key of the issuing realm. You can either hard code the realm’s public key in your validation code, or lookup and cache the public key using the certificate endpoint with the Key ID (KID) embedded within the JWS. Depending what language you code in, there are a multitude of third party libraries out there that can help you with JWS validation.
This is strange that they provide so much implementation of relying party as so called keycloak adapters and not any resource server adapter. ↩︎
like ensuring alg is not None or checking the audience and the issuer ↩︎