====
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:
- apisix: The image
linagora/apisix:3.2.0-debian-javaplugin
was created by Linagora. It based onapisix:3.2.0-debian
, it already contain apisix plugin for SLO (Single Logout) and rewrite theX-User
header. - james: Memory version using the LDAP and
XUserAuthenticationStrategy
for JMAP - sso.example.com: keycloak image with a pre-configured
oidc
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 base - ldap: with a pre-provisioned user
[email protected]
and his passwordsecret
- redis: for storage of the revoked token. This is optional, we can un-use it if we want to store it in-memory with Apisix standalone.
Here 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):
- Open your browser and go to https://www.keycloak.org/app/
- Fill the form with your local Keycloak info and click
Save
:- Keycloak URL:
http://sso.example.com:8080/auth
- Realm:
oidc
- Client:
oidc
- Keycloak URL:
- Click
Sign in
and you will get redirected to your Keycloak login screen - Open the Developer Tools of your browser and check the network connections
- Enter the credentials of the user:
james-user@localhost / secret
- Get the response of the token request and save the
access_token
sent back from Keycloak - Do a JMAP request (like a
Mailbox/get
) with curl, Postman, ... towards the/oidc/jmap
endpoint of Apisix:- URL:
POST http://apisix.example.com:9080/oidc/jmap
- AccountId of the user (for the JMAP request body):
fe100f0103112aa50a585b7ca037c6b9387352991fc35cec15faf7ce4edd8d03
- Put the JWT token you got from Keycloak in the
Authorization
header as a Bearer token - Don't forget the
Accept
header as well with the valueapplication/json; jmapVersion=rfc-8621
to use the JMAP spec from the RFC-8621
- URL:
If 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": "[email protected]",
"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 modifyOAuth2Providers.jsm
:- Add James hostname in kHostnames:
["localhost", ["james.local", "email"]],
- Register using
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", ], ]
- Add James hostname in kHostnames:
-
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
.