Scenario

Needed to get my hands on a Keycloak instance to work on an integration and make a bunch of tests.

Essentially, I was working on a Node.js app that would need to interact with Keycloak’s Client Registration Endpoint, which is a protected one.

Working it out

The obvious/simplest path is Docker. Here’s a compose file to run Keycloak with a PostgreSQL database.

Notes: This is obviously a very basic compose file. Stuff like passwords should be passed via variables, secrets or what have you.

This looks pretty simple. But of course, there’s always a catch.

After having all containers running, I tried said requests from Node.js and surprisingly got a 401 with 'Failed decode token' message. I had seen this before with a bad token before so I went in the direction of figuring out if my token was wrongly copied, if node-fetch was somehow messing with the Authorization header, etc.

I ended up with:

  • 401: node-fetch request, stripped down from variables, all hard coded, from app container to keycloak container
  • 201: exact same curl request from host to keycloak container

Long story short, the problem was related with the token issuer/audience. In my host, it matched because localhost. In the app container the story is a different one.

After searching for configurations that would allow me to overcome this problem it became clear that the best solution would be to serve keycloak behind a reverse proxy. Since this would also be handy for other to experiment in the future, I went on to setup this on a server, with a public domain and so on.

Server scenario:

  • Keycloak running with the same compose file as before
  • Nginx reverse proxies to upstream container
  • HTTPS handled on Nginx, Nginx->upstream is HTTP

Easy-peasy. Keyloak was now available on the configured subdomain. Clicked on “Administration Console” link. Blank…

Out of the couple dozen JavaScript files loaded by the UI via HTTPS, keycloak.js is requested over HTTP. Mixed Content block.

Sigh.

After some googling/stackoverflowing, two extra things were required:

  • add PROXY_ADDRESS_FORWARDING: 'true' to keycloak service environment variables in the compose file (this is actually descibed in the dockerhub page for when running behind a reverse proxy…)
  • add X-Forwarded-Proto to the Nginx location configuration (the basic config I had done only had -For and -Host). Since Nginx is handling SSL and is then forwarding upstream as HTTP, we need to let Keycloak know of the original request scheme (HTTPS)

Here’s the juice from the server block I created on Nginx:

location / {
  proxy_pass         http://localhost:7777;
  proxy_redirect     off;
  proxy_set_header   Host $host;
  proxy_set_header   X-Real-IP $remote_addr;
  proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header   X-Forwarded-Host $server_name;
  proxy_set_header   X-Forwarded-Proto $scheme;
}