Contact us: contact@deburen.uk

SSO in Roundcube and Dovecot 2.3 since Keycloak 26

Keycloak 26 now validates that the client performing token introspection is in the token's aud (audience) claim which means you likely need to make some changes to your Client configuration in Keycloak to keep Roundcube successfully logging into Dovecot 2.3 using SSO.

The Problem

Keycloak 26 rightly tightened security around audience validation during introspection which stopped our Roundcube webmail platform from successfully logging into Dovecot 2.3 using SSO.

The key indicators of this issue in the logs are:

Keycloak

type="INTROSPECT_TOKEN_ERROR", error="invalid_token", reason="Client 'dovecot-oauth' is not in the token audience", token_issued_for="roundcube-oauth"

Dovecot

oauth2 failed: Introspection failed: No username returned

The fix

We use the following Client names in our Keycloak setup:

To get this working you need to make some changes in the Keycloak admin interface for your Realm:

Then save your new Audience mapper, and the Roundcube login to Dovecot over SSO should now be working.

Bonus: working Roundcube and Dovecot set up

Since there have been a few changes in how SSO is set up in Roundcube since version 1.7, here are our latest working configurations:

Roundcube

First of all make sure the PHP APCu module is enabled (you can also use redis or memcache).

config/config.inc.php

// Enable OAuth2 by defining a provider.
$config['oauth_provider'] = 'generic';

// Introduced in 1.7 to force xoauth2 and not use oauthbearer
$config['oauth_auth_type'] = 'XOAUTH2';

// Provider name to be displayed on the login button
$config['oauth_provider_name'] = '*** The text you want on the login button ***;

// Mandatory: OAuth client ID for your Roundcube installation
$config['oauth_client_id'] = 'roundcube-oauth';

// Mandatory: OAuth client secret
$config['oauth_client_secret'] = '*** roundcube-oauth Client secret here ***';

// NEW in 1.7: OIDC discovery URL - replaces oauth_auth_uri, oauth_token_uri, oauth_identity_uri
$config['oauth_config_uri'] = 'https://keycloak-hostname/realms/your-realm/.well-known/openid-configuration';

// NEW in 1.7: highly recommended / mandatory when using oauth_config_uri
// Use a supported cache driver, e.g. 'apcu', 'redis', or 'memcache'
$config['oauth_cache'] = 'apcu';

// Optional: disable SSL certificate check on HTTP requests to OAuth server
// See https://docs.guzzlephp.org/en/stable/request-options.html#verify for possible values
$config['oauth_verify_peer'] = true;

// Mandatory: OAuth scopes to request (space-separated string)
$config['oauth_scope'] = 'email openid profile';

// Optional: additional query parameters to send with login request (hash array)
$config['oauth_auth_parameters'] = [];

// Optional: array of field names used to resolve the username within the identity information
$config['oauth_identity_fields'] = ['email'];

// Boolean: automatically redirect to OAuth login when opening Roundcube without a valid session
$config['oauth_login_redirect'] = false;

// Optional: For backends that don't support XOAUTH2/OAUTHBEARER method we can still use
// OpenIDC protocol to get a short-living password (claim) for the user to log into IMAP/SMTP.
// That password have to have (at least) the same expiration time as the token, and will be
// renewed on token refresh.
// Note: The claim have to be added to 'oauth_scope' above.
$config['oauth_password_claim'] = null;

Dovecot 2.3

dovecot-oauth2.conf.ext

client_id = dovecot-oauth
client_secret = *** dovecot-oauth Client secret here ***
introspection_url = https://dovecot-oauth:*** dovecot-oauth Client secret here ***@keycloak-hostname/realms/your-realm/protocol/openid-connect/token/introspect
introspection_mode = post
username_attribute = email
scope = email openid

conf.d/auth-oauth2.conf.ext

auth_mechanisms = $auth_mechanisms xoauth2

passdb {
  driver = oauth2
  mechanisms = xoauth2
  args = *** path to dovecot-oauth2.conf.ext ***
}

conf.d/10-auth.conf

Make sure the following line is added near the bottom of this file to get the oauth configuration above included.

!include auth-oauth2.conf.ext

Document history