Setting up Dovecot and Roundcube with Azure Active Directory B2C

This post explains how to configure Roundcube that connects to Dovecot to fetch mails letting the user authenticate against an Azure Active Directory B2C.

Nice diagram

Azure AD

In Azure AD B2C we need to:

  1. Register an App for Roundcube
  2. Register an App for Dovecot
  3. Create a User Flow to log Users into Roundcube
  4. Create a resource owner password credential (ROPC) flow to log Users into Dovecot whit PLAIN

App registrations

First of all let's register a new app in AAD App registrations for Roundcube:
app registration

Change the domain of the redirect uri to the domain of your Roundcube installation.

Now let's create an OAuth client secret for Roundcube. Go to Certificates and secrets for the App we just registered, and create a new secret.
Certs and secrets

Save this secret for later, we will need it when configuring Roundcube.

Do the same for Dovecot, but for the Dovecot app, under Advanced settings -> Enable the following mobile and desktop flows, select Yes to treat the application as a public client.

Allow public client flows

Also, for the Dovecot app, we need to set "oauth2AllowImplicitFlow": true in the App Manifest.

This setting is required for the ROPC flow we will create in the next chapter.

Sign in/up and ROPC flow

Now let's configure the Sign in/up flow, so users can log in to Roundcube. But first we need to add a custom attribute for our users, that will hold the Dovecot username for login.
Custom attr

How to populate this user attribute is left as exercise for the reader (use API connectors).

And now create a new flow. Call it something you like (mine is called B2C_1_SignUpIn), and add the attribute as claim for Roundcube:
Flow

This flow is used by Roundcube to log users in.

We need one more, to allow Dovecot to log users in with username and password for plain auth, but authenticating them against our B2C AD. To do that, we need to create a resource owner password credential flow.

Once created, add the custom user attribute to the application claims like for the flow created previously.

We are done here. Next up is Roundcube.

Roundcube

Download and install it (at the time of writing the actual version is 1.6.0, and I tested everything with this version), and following this wiki page, we can configure OAuth.

We just have to add some parameters in roundcube/config/config.inc.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// ----------------------------------
// OAuth
// ----------------------------------

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

// Provider name to be displayed on the login button
$config['oauth_provider_name'] = 'Simons mail server';

// Mandatory: OAuth client ID for your Roundcube installation
$config['oauth_client_id'] = '<Roundcube app registration client id>';

// Mandatory: OAuth client secret
$config['oauth_client_secret'] = '<Roundcube client secret>';

// Mandatory: URI for OAuth user authentication (redirect)
$config['oauth_auth_uri'] = 'https://<yourB2Cdomain>.b2clogin.com/<yourB2Cdomain>.onmicrosoft.com/oauth2/v2.0/authorize?p=B2C_1_SignUpIn';

// Mandatory: Endpoint for OAuth authentication requests (server-to-server)
$config['oauth_token_uri'] = 'https://<yourB2Cdomain>.b2clogin.com/<yourB2Cdomain>.onmicrosoft.com/B2C_1_SignUpIn/oauth2/v2.0/token';

// Optional: Endpoint to query user identity if not provided in auth response
$config['oauth_identity_uri'] = null;

// Optional: disable SSL certificate check on HTTP requests to OAuth server
// See http://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'] = '<Roundcube app registration client id> openid';

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

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

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

That's it for Roundcube.

Dovecot

Dovecot docs are here. We need to configure conf.d/10-auth.conf like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
auth_mechanisms = plain oauthbearer xoauth2

# this is for oauth auth
passdb {
driver = oauth2
mechanisms = xoauth2 oauthbearer
args = /etc/dovecot/dovecot-oauth2.conf.ext
}

# this is for plain auth
passdb {
driver = oauth2
mechanisms = plain login
args = /etc/dovecot/dovecot-oauth2.plain.conf.ext
}

/etc/dovecot/dovecot-oauth2.conf.ext looks like this:

1
2
3
4
5
tokeninfo_url = https://<yourB2Cdomain>.b2clogin.com/<yourB2Cdomain>.onmicrosoft.com/b2c_1_signupin/oauth2/v2.0/token?token=
introspection_mode = local
openid_configuration_url = https://<yourB2Cdomain>.b2clogin.com/<yourB2Cdomain>.onmicrosoft.com/B2C_1_SignUpIn/v2.0/.well-known/openid-configuration
local_validation_key_dict = fs:posix:prefix=/etc/dovecot/keys/
username_attribute = extension_PreferredLoginUsername

and /etc/dovecot/dovecot-oauth2.plain.conf.ext looks like this:

1
2
3
4
5
6
7
grant_url = https://<yourB2Cdomain>.b2clogin.com/<yourB2Cdomain>.onmicrosoft.com/b2c_1_ropc_auth/oauth2/v2.0/token?scope=<Dovecot app registration client id>
client_id = <Dovecot app registration client id>
client_secret = <Dovecot client secret>
username_attribute = extension_PreferredLoginUsername
use_grant_password = yes
introspection_mode = local
local_validation_key_dict = fs:posix:prefix=/etc/dovecot/keys/

Now we need to populate /etc/dovecot/keys/ with the keys from our OAuth provider (AAD). Go to https://<yourB2Cdomain>.b2clogin.com/<yourB2Cdomain>.onmicrosoft.com/b2c_1_signupin/discovery/v2.0/keys, and copy the keys key. It's something like this:

1
{"kid":"X5eXk4xyo...aNk","nbf":1493763266,"use":"sig","kty":"RSA","e":"AQAB","n":"tVKUtcx_...Xw"}

Create a file /etc/dovecot/keys/<App registration client id>/RS256/<kid> and put the key you copied above in that file.

The end

Restart Apache/Nginx, restard Dovecot, and try to log in. Everything should work.

Nice.

Bye.

avatar

Simons stuff