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.
Azure AD
In Azure AD B2C we need to:
- Register an App for Roundcube
- Register an App for Dovecot
- Create a User Flow to log Users into Roundcube
- 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:
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.
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.
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.
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:
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
16auth_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
5tokeninfo_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
7grant_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.