Some notes on configuring Dovecot to authenticate via OIDC/OAuth2
Suppose, not hypothetically, that you have a relatively modern Dovecot server and a shiny new OIDC identity provider server ('OP' in OIDC jargon, 'IdP' in common usage), and you would like to get Dovecot to authenticate people's logins via OIDC. Ignoring certain practical problems, the way this is done is for your mail clients to obtain an OIDC token from your IdP, provide it to Dovecot via SASL OAUTHBEARER, and then for Dovecot to do the critical step of actually validating that token it received is good, still active, and contains all the information you need. Dovecot supports this through OAuth v2.0 authentication as a passdb (password database), but in the usual Dovecot fashion, the documentation on how to configure the parameters for validating tokens with your IdP is a little bit lacking in explanations. So here are some notes.
If you have a modern OIDC IdP, it will support OpenID Connect Discovery, including the provider configuration request on the path /.well-known/openid-configuration. Once you know this, if you're not that familiar with OIDC things you can request this URL from your OIDC IdP, feed the result through 'jq .', and then use it to pick out the specific IdP URLs you want to set up in things like the Dovecot file with all of the OAuth2 settings you need. If you do this, the only URL you want for Dovecot is the userinfo_endpoint URL. You will put this into Dovecot's introspection_url, and you'll leave introspection_mode set to the default of 'auth'.
You don't want to set tokeninfo_url to anything. This setting is (or was) used for validating tokens with OAuth2 servers before the introduction of RFC 7662. Back then, the defacto standard approach was to make a HTTP GET approach to some URL with the token pasted on the end (cf), and it's this URL that is being specified. This approach was replaced with RFC 7662 token introspection, and then replaced again with OpenID Connect UserInfo. If both tokeninfo_url and introspection_url are set, as in Dovecot's example for Google, the former takes priority.
(Since I've just peered deep into the Dovecot source code, it appears
that setting 'introspection_mode = post
' actually performs an
(unauthenticated) token introspection request. The 'get' mode
seems to be the same as setting tokeninfo_url. I think that
if you set the 'post' mode, you also want to set active_attribute
and perhaps active_value
, but I don't know what to, because
otherwise you aren't necessarily fully validating that the token
is still active. Does my head hurt? Yes. The moral here is that you
should use an OIDC IdP that supports OpenID Connect UserInfo.)
If your IdP serves different groups and provides different 'issuer'
('iss') values to them, you may want to set the Dovecot 'issuers
=
' to the specific issuer that applies to you. You'll also want
to set 'username_attribute
' to whatever OIDC claim is where
your IdP puts what you consider the Dovecot username, which might
be the email address or something else.
It would be nice if Dovecot could discover all of this for itself
when you set openid_configuration_url
, but in the current
Dovecot, all this does is put that URL in the JSON of the error
response that's sent to IMAP clients when they fail OAUTHBEARER
authentication. IMAP clients may or may not do anything useful
with it.
As far as I can tell from the Dovecot source code, setting 'scope =' primarily requires that the token contains those scopes. I believe that this is almost entirely a guard against the IMAP client requesting a token without OIDC scopes that contain claims you need elsewhere in Dovecot. However, this only verifies OIDC scopes, it doesn't verify the presence of specific OIDC claims.
So what you want to do is check your OIDC IdP's /.well-known/openid-configuration URL to find out its collection of endpoints, then set:
# Modern OIDC IdP/OP settings introspection_url = <userinfo_endpoint> username_attribute = <some claim, eg 'email'> # not sure but seems common in Dovecot configs? pass_attrs = pass=%{oauth2:access_token} # optionally: openid_configuration_url = <stick in the URL> # you may need: tls_ca_cert_file = /etc/ssl/certs/ca-certificates.crt
The OIDC scopes that IMAP clients should request when getting tokens
should include a scope that gives the username_attribute
claim,
which is 'email' if the claim is 'email', and also apparently the
requested scopes should include the offline_access
scope.
If you want a test client to see if you've set up Dovecot correctly, one option is to appropriately modify a contributed Python program for Mutt (also the README), which has the useful property that it has an option to check all of IMAP, POP3, and authenticated SMTP once you've obtained a token. If you're just using it for testing purposes, you can change the 'gpg' stuff to 'cat' to just store the token with no fuss (and no security). Another option, which can be used for real IMAP clients too if you really want to, is an IMAP/etc OAuth2 proxy.
(If you want to use Mutt with OAuth2 with your IMAP server, see this article on it also, also, also. These days I would try quite hard to use age instead of GPG.)
|
|