Making single sign-on work with Keycloak and Odoo

Paul Dubs
21 Jan 2023

To make our goal of making an easy to use AI marketplace and platform a reality, we need to integrate all sorts of technology.  We recently completed unifying accounts between our marketplace (which is powered by Odoo) and our platform (which uses Keycloak for authentication.) Seting up Keycloak as the OAuth2 Provider for Odoo is something that should work right out of the box in theory, but is a little nuanced in practice. Despite being two established projects and OAuth2 being a standard, the process is more challenging than it seems. In this blog post, we’ll share our experience and show you how to overcome the common pitfalls.

The idea is simple: Setup a realm in Keycloak and then put the appropriate URLs into the Odoo config. But, using Keycloak as the OAuth2 Provider for Odoo is more challenging - particularly when you use the hosted Odoo service.

The Problem

When you do the straightforward thing, i.e., configure everything as it should be, you will see error messages hinting at a wrong configuration. A quick search for “Keycloak Odoo” finds an Odoo user forum entry from early 2019 which does have a solution, but it is only for an older Odoo version and requires you to patch the software. As we take advantage of the hosted service, patching the software is something we can’t do. However, the patch does indeed hint at the source of the problem.

The patch adds a new configuration option that allows you to switch Odoo’s OAuth2 implementation when it works together with Keycloak. With that option enabled, it changes the request that Odoo sends to Keycloak.  Usually, Odoo sends its access token using a query parameter like endpoint_url?access_token=its_access_token. However, Keycloak expects to receive the access token as a bearer token in the Authorization header instead.

This difference exists because of a slight incompatibility between the OAuth2 and the OpenID Connect standards. OpenID Connect is supposed to be a superset of the OAuth2 standard. Still, it narrows the acceptable way of transmitting the access token only ever to be passed in the Authorization header. With that observation in mind, we knew what the problem was, but we still needed to fix it while avoiding having to patch either software.

Our Keycloak deployment is sitting behind a reverse proxy, and this reverse proxy configuration is where we deployed our workaround to make Keycloak and Odoo compatible with each other. 

The Solution

We use Nginx as our reverse proxy. It is not only incredibly performant, it is also surprising capable when it comes to rewriting requests on the fly. Which came in very handy in this situation.

First, we used our Keycloak realm only for Odoo, so all we had to do was to add the following line to our configuration:

proxy_set_header Authorization "Bearer $arg_access_token"

That way, it would always set the Authorization header to Bearer followed by whatever is in the access_token query parameter. However, as soon as the Odoo configuration started working, we also wanted to use that same realm for our other applications which utilize the OpenID Connect standard and therefore, need the access token in the Authorization header.

Always overriding the Authorization header with the contents of the access_token query parameter however broke our happy authorization set-up in our development environment. By conditionally setting the Authorization header depending on what the request contains we were able to fix that issue, and everything started working again.

Fortunately, Nginx makes that easy. First, we define a map that depending on the contents of the access_token query parameter, defines a new variable called auth_header:

map $arg_access_token $auth_header {  
        "" $http_authorization;  
        default "Bearer $arg_access_token";  

If the parameter is empty or not present, we set it to the value of the Authorization header. In any other case, it is the correct bearer token. Next, we use the new variable in our configuration:

proxy_set_header Authorization $auth_header;  

And with that our OpenID Connect applications and our hosted Odoo website can now use the same Keycloak realm without having to patch anything. Single Sign-On achieved.