====
This is example of an OIDC setup with James.
The API Gateway for example is Apisix, we can use Apisix for websocket gateway, horizontal scaling, etc...
This docker-compose will start the following services:
linagora/apisix:3.2.0-debian-javaplugin was created by Linagora. It based on apisix:3.2.0-debian, it already contain apisix plugin for SLO (Single Logout) and rewrite the X-User header.XUserAuthenticationStrategy for JMAPoidc realm, oidc client (Authorization Code Flow, should dedicate for JMAP), james-thunderbird client (Client Credentials Flow, dedicated for IMAP/SMTP) and connected to the LDAP for its user basejames-user@tmail.com and his password secretHere is an architecture diagram showing how Single Sign On works for this example:
SSO auto-discovery might require the set up of a .well-known/webfinger endpoint described in this spec via external means (not provided here).
Here is an architecture diagram showing how Single Log Out works for this example, using the backchannel OIDC flow:
Here is an architecture diagram showing how to authenticate JAMES IMAP/SMTP using OIDC Provider:
docker-compose up -d
Before test it, we need to modify the /etc/hosts first.
127.0.0.1 sso.example.com apisix.example.com
There is no frontend in this example to interact directly with Keycloak and get a valid JWT token from it.
However, you can use the Keycloak playground example with the following steps (based on Authorization Code Flow):
Save:http://sso.example.com:8080/authoidcoidcSign in and you will get redirected to your Keycloak login screenjames-user@localhost / secretaccess_token sent back from KeycloakMailbox/get) with curl, Postman, ... towards the /oidc/jmap endpoint of Apisix:POST http://apisix.example.com:9080/oidc/jmapfe100f0103112aa50a585b7ca037c6b9387352991fc35cec15faf7ce4edd8d03Authorization header as a Bearer tokenAccept header as well with the value application/json; jmapVersion=rfc-8621 to use the JMAP spec from the RFC-8621If everything goes well, you should get a valid response back.
We can discover the oauth2 by endpoint http://sso.example.com:8080/auth/realms/oidc/.well-known/openid-configuration
If you want to test SSO, SLO with curl, you can use the bellow command:
Based on Client Credentials Flow
GET_TOKEN_RESPONSE=`curl --location 'http://sso.example.com:8080/auth/realms/oidc/protocol/openid-connect/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=password' \ --data-urlencode 'scope=openid profile email' \ --data-urlencode 'client_id=james-thunderbird' \ --data-urlencode 'client_secret=Xw9ht1veTu0Tk5sMMy03PdzY3AiFvssw' \ --data-urlencode 'username=james-user@localhost' \ --data-urlencode 'password=secret' 2>/dev/null` ACCESS_TOKEN=`echo $GET_TOKEN_RESPONSE 2>/dev/null |perl -pe 's/^.*"access_token"\s*:\s*"(.*?)".*$/$1/'` REFRESH_TOKEN=`echo $GET_TOKEN_RESPONSE 2>/dev/null |perl -pe 's/^.*"refresh_token"\s*:\s*"(.*?)".*$/$1/'`
curl 'http://apisix.example.com:9080/oidc/jmap/session' \ --header 'Accept: application/json; jmapVersion=rfc-8621' \ --header 'Authorization: Bearer '$ACCESS_TOKEN
The response example:
HTTP/1.1 200 OK
{
"capabilities": {...}
"username": "james-user@tmail.com",
"state": "2c9f1b12-b35a-43e6-9af2-0106fb53a943"
}
Logout from Keycloak
curl --location 'http://sso.example.com:8080/auth/realms/oidc/protocol/openid-connect/logout' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'client_id=james-thunderbird' \ --data-urlencode 'client_secret=Xw9ht1veTu0Tk5sMMy03PdzY3AiFvssw' \ --data-urlencode 'refresh_token='$REFRESH_TOKEN
Verify the revoked access token can't access the JMAP server anymore.
curl 'http://apisix.example.com:9080/oidc/jmap/session' \ --header 'Accept: application/json; jmapVersion=rfc-8621' \ --header 'Authorization: Bearer '$ACCESS_TOKEN
The response example:
HTTP/1.1 401 Unauthorized <html> <head><title>401 Authorization Required</title></head> <body> <center><h1>401 Authorization Required</h1></center> <hr><center>openresty</center> <p><em>Powered by <a href="https://apisix.apache.org/">APISIX</a>.</em></p></body> </html>
Use websocket with endpoint ws://apisix.example.com:9080/oidc/jmap/ws and the same access token.
We would use Thunderbird version 91.4.1 as a mail client (above versions should work).
Open /thunderbird/omni.ja in your host, find and modify OAuth2Providers.jsm:
["localhost", ["james.local", "email"]],james-thunderbird Keycloak client in kIssuers:[
"james.local",
[
"james-thunderbird", //client_id from keycloak
"Xw9ht1veTu0Tk5sMMy03PdzY3AiFvssw", // client_secret from keycloak
"http://keycloak.local:8080/auth/realms/oidc/protocol/openid-connect/auth",
"http://keycloak.local:8080/auth/realms/oidc/protocol/openid-connect/token",
],
]
Adding a line 127.0.0.1 keycloak.local to your /etc/hosts so Thunderbird can resolve the address of keycloak.
Run Thunderbird, configure it using james-user@localhost account against these IMAP/SMTP settings:
Click Get Messsages in your INBOX tab, a popup will show up ask you to login against Keycloak. After logging in succeed, you can use James IMAP/SMTP. Let try to send a mail to yourself:
Then it should work:
A remark here is that if you generate a new client_secret for james-thunderbird client in Keycloak, you have to modify it accordingly in OAuth2Providers.jsm.