Background Mail Access deprecated
Overview
Some mail features act at a later time, when the user may long have logged out: they need access to the user's mailbox of their own. Where users log in through OpenID Connect and reach their mail with OAuth tokens, those tokens end with the user's session. The offline grant closes that gap: the user allows background mail access once, in a popup at the identity provider, and the middleware keeps the tokens the identity provider issues for that purpose - tokens of an offline session that outlives every login - in the mail credential vault. The features refer to that grant instead of keeping credentials of their own.
This article walks through the setup of all parties involved. Keycloak and Dovecot serve as examples; the same steps apply to any identity provider that issues refresh tokens for offline access, and to any mail server that accepts OAuth access tokens.
Use cases
| Feature | What runs in the background | Details |
|---|---|---|
| Scheduled mail ("send later") | The mail is sent at its due time, and its copy is stored in the sent folder | Scheduled Mails |
| Snoozed mail | The mail returns to the folder it came from at its due time | Snooze Mails |
Both use the grant wherever the user's login offers it (see Middleware). Other features that keep credentials in the vault, e.g. push or the data export, read the user's mail and keep their own credentials; they never use the grant.
Alternatives
The offline grant is one of three ways the middleware gets a credential for background mail access, and the one meant for identity providers that offer no suitable token exchange:
- Master authentication: the mail server accepts a master password for every user. Needs no user interaction, but gives the middleware access to every mailbox.
- Token exchange: the middleware exchanges the user's access token for tokens that outlive the session, see Tokens from an exchange. Needs no user interaction, but an identity provider whose exchange yields such tokens.
- Offline grant: the user allows it once; see Tokens from an offline grant for the details behind the steps below.
How it works
- The user logs in through OpenID Connect as usual. The login itself asks for no offline access.
- The first time the user schedules or snoozes a mail, the middleware answers with
MCRED-0014. App Suite UI then asks the user to allow background mail access and opens a popup at the identity provider (<prefix>/oidc[/<backend>]/offline_grant?flow=authorize). The user may also allow it beforehand in the security settings. - The identity provider issues tokens with
offline_access; the middleware keeps them as the user's grant. App Suite UI then repeats the scheduling or snoozing by itself; the middleware keeps a credential for the mail that refers to the grant. - At the due time, the middleware reaches the mailbox with the grant's access token, refreshing it as needed. The tokens are kept alive while the grant is in use, and released once it goes unused for 90 days.
- The user can revoke the grant in the security settings at any time.
Prerequisites
- Login: an OpenID Connect back-end the users log in with.
- Mail access by OAuth:
com.openexchange.mail.authTypeset toxoauth2oroauthbearer, see Mail Access via XOAUTH2 or OAUTHBEARER. The grant holds OAuth tokens only: a setup where mail is reached with passwords keeps passwords as before. - Mail transport by OAuth or master password:
com.openexchange.mail.transport.authTypeset toxoauth2oroauthbearer, or transport with the master password. - Identity provider: authorization code flow with PKCE, refresh tokens for the
offline_accessscope (or its equivalent), and preferably a token revocation end-point (RFC 7009), which the discovery document names. - Mail server: accepts the identity provider's access tokens for IMAP and for sending, e.g. by token introspection. Narrowing the grant's tokens (optional, see Mail server) needs access control by a claim of the token, and the composition space back-end that keeps drafts on the mail server (
com.openexchange.mail.compose.mailstorage.enabled=true, the default): only that one sends a scheduled mail from where it waits, without the Drafts folder the narrowed tokens may not open. - All middleware nodes run a version that knows the grant: nodes of an earlier version cannot read the vault rows it writes. Switch it on only once every node has been updated.
Middleware
The grant is switched on per OpenID Connect back-end - with the back-end's prefix com.openexchange.oidc.<backend>. for a back-end other than the default one: the users who log in through a back-end that enables it use it for scheduled and snoozed mail, those of other back-ends and password logins do not. The settings of the features and of the vault apply per user (config cascade).
In the values of the core-mw Helm chart (in a stack chart, below its core-mw key):
properties:
# Mail access and transport by OAuth, as for the logins (or transport with the master password)
com.openexchange.mail.authType: "xoauth2"
com.openexchange.mail.transport.authType: "xoauth2"
# The features
com.openexchange.mail.scheduled.enabled: "true"
com.openexchange.mail.snoozed.enabled: "true"
# Optional: release a grant once unused for this long (default 90D; 0 keeps it until revoked)
com.openexchange.mail.credential.oauth.offlineGrantIdleTime: "90D"
# The OpenID Connect back-end the users log in with: registers the end-point offline_grant, and its users' scheduled and
# snoozed mail use the grant
com.openexchange.oidc.offlineGrantEnabled: "true"
# Asked for in addition to the login's scope and openid; mail-background only where the mail server narrows the grant's tokens
com.openexchange.oidc.offlineGrantScope: "offline_access mail-background"
# Optional: a prompt for the identity provider, e.g. consent, and further parameters as key=value pairs separated by &
# com.openexchange.oidc.offlineGrantPrompt: "consent"
# com.openexchange.oidc.offlineGrantAdditionalParameters: ""
Notes:
com.openexchange.oidc.offlineGrantEnabledis read when the back-end starts: the end-point is registered then.- With the grant, scheduled and snoozed mail are offered without master authentication;
com.openexchange.mail.scheduled.requireMasterAuthandcom.openexchange.mail.snoozed.requireMasterAuthplay no role for them. - A configured token exchange (
com.openexchange.mail.credential.oauth.tokenExchange) takes precedence over the grant. - A custom OpenID Connect back-end can adjust the authorization request and the user resolution of the grant through hooks of its own, see Tokens from an offline grant.
Identity provider
The examples show Keycloak; other identity providers need the equivalent settings, see the table in Tokens from an offline grant for Microsoft Entra ID and Google.
Offline access
The client the users log in with needs the client scope offline_access as an optional client scope - as a default one, every interactive login of App Suite UI would get an offline session too - and the users the realm role offline_access, which Keycloak's default roles grant. The offline session that backs a grant ends after Offline Session Idle (realm settings, Sessions; 30 days by default) without use; the middleware keeps it alive, so that the grant lasts as long as the middleware keeps it.
Narrowing claim (optional)
The grant's tokens work while the user is not logged in, for as long as the grant is in use. To limit what they can do, the mail server can give them fewer rights than a login gets: only what scheduled and snoozed mail need (see Mail server). For that, it has to recognize them, by a claim in the access token that a login's tokens never carry - here mail_acl_group with the value background, which Dovecot turns into an ACL group. The identity provider adds the claim through an optional client scope, here mail-background, that has no other purpose: the grant asks for it, the login never does.
In the admin console:
- Client scopes > Create client scope: name
mail-background, type None, Include in token scope on. - In the new scope, Mappers > Configure a new mapper > Hardcoded claim: token claim name
mail_acl_group, claim valuebackground, claim JSON type String, Add to access token and Add to token introspection on, Add to ID token and Add to userinfo off. - Clients > the client the users log in with > Client scopes > Add client scope:
mail-backgroundas Optional.
The same as part of a realm import:
"clientScopes": [ {
"name": "mail-background",
"description": "Background mail access (send later, snooze): only the offline grant asks for it, a login never does",
"protocol": "openid-connect",
"attributes": {
"include.in.token.scope": "true",
"display.on.consent.screen": "true",
"consent.screen.text": "Access your mailbox while you are not signed in, to send emails later and return snoozed emails"
},
"protocolMappers": [ {
"name": "mail-acl-group",
"protocol": "openid-connect",
"protocolMapper": "oidc-hardcoded-claim-mapper",
"consentRequired": false,
"config": {
"claim.name": "mail_acl_group",
"claim.value": "background",
"jsonType.label": "String",
"access.token.claim": "true",
"introspection.token.claim": "true",
"id.token.claim": "false",
"userinfo.token.claim": "false"
}
} ]
} ]
and, in the login client, "mail-background" added to its "optionalClientScopes", next to "offline_access".
Revocation
The grant is authorized in the browser the user is logged in with, so with Keycloak and the login's client it shares the login's session there: revoking the grant's tokens would end the login as well. The middleware therefore revokes a grant at the identity provider only while no live session of the user shares that session, and otherwise leaves its tokens to the offline session's idle timeout; see Revoking a grant.
Mail server
Without further configuration, the grant's tokens reach the mailbox with the rights of the user. Narrowing them is optional, but limits what a background job can do to what it needs. The examples show Dovecot, which turns the claim into an ACL group and narrows that group with group-override entries, which win over the owner's own rights - first in the configuration syntax of Dovecot 2.4, then in the older one of Dovecot 2.3.
Warning: Dovecot 2.4 changed the configuration syntax, and neither version accepts the other's: use the example that matches the mail server's version. See the Dovecot documentation on ACLs and OAuth2 for 2.4, on ACLs and OAuth2 for 2.3, and upgrading from 2.3 to 2.4.
Dovecot 2.4
oauth2 {
# introspection_url, username_attribute, active_attribute, ... as for the logins
fields {
# The claim of the grant's tokens becomes the user's ACL group; a login's tokens carry none
userdb_acl_groups = %{oauth2:mail_acl_group | default}
}
}
mail_plugins {
acl = yes
}
acl_driver = vfile
# The user's own namespace, by the name the configuration gives it. Of the entries matching a mailbox, the last one wins: the pattern
# comes first.
namespace inbox {
mailbox * {
acl "group-override=background" {
rights = lisw
}
}
mailbox Scheduled {
acl "group-override=background" {
rights = lrswite
}
}
mailbox Snoozed {
acl "group-override=background" {
rights = lrswte
}
}
}
Dovecot 2.3
The OAuth2 settings sit in the file the oauth2 passdb names in its args. A passdb attribute with the prefix userdb_ is handed on to the user's lookup, so the claim ends up as the user's acl_groups; a token without it, as every login's, leaves the group empty:
# e.g. /etc/dovecot/dovecot-oauth2.conf.ext
# introspection_url, username_attribute, active_attribute, ... as for the logins
# The claim of the grant's tokens becomes the user's ACL group; a login's tokens carry none. Appended with a space where pass_attrs
# is already set.
pass_attrs = userdb_acl_groups=%{oauth2:mail_acl_group}
The ACL plugin reads the entries from a global ACL file, i.e. a vfile path that names a file rather than a directory; the ACLs kept per mailbox apply as before:
mail_plugins = $mail_plugins acl
protocol imap {
mail_plugins = $mail_plugins imap_acl
}
plugin {
acl = vfile:/etc/dovecot/dovecot-acl
}
The file /etc/dovecot/dovecot-acl holds one entry per line, <mailbox pattern> <identifier> <rights>. Fields are separated by a single space: with tabs or several spaces, Dovecot rejects the file, and every access to every mailbox fails. Mailbox names include the namespace's prefix, if it has one (e.g. INBOX/Scheduled). Unlike in 2.4, the rights of all entries matching a mailbox add up, whatever their order, so Snoozed also gets i from the pattern, which does no harm:
* group-override=background lisw
Scheduled group-override=background lrswite
Snoozed group-override=background lrswte
Rights
Both grant the same rights:
| Mailbox | Rights | Needed by |
|---|---|---|
Scheduled | lrswite | Scheduled mail: the due mail is sent right from there and deleted afterwards; a failed one is marked there |
Snoozed | lrswte | Snoozed mail: the due mail is read and deleted there |
| any other | lisw | Scheduled mail appends the copy to the sent folder; snoozed mail appends the mail to the folder it came from. Neither opens the folder |
Notes:
- The entries name mailboxes, not special-use flags: users whose scheduled or snoozed folder has another name, e.g. a localized one, need entries for that name.
GETACLlists the entries on every mailbox. Dovecot 2.3 and Dovecot Pro mark them as global ones (#!$background), which cannot be set; the middleware leaves them out of a folder's permissions and never sets them. Dovecot CE 2.4 lists them as!$background, like an entry of the mailbox itself; setting or deleting such an entry per mailbox does not change the rights the configuration grants. Other clients that show ACLs show them as they are.- Since the grant's tokens read no mailbox but
ScheduledandSnoozed, a scheduled reply or forward does not mark the mail it refers to as answered or forwarded (logged as a warning withIMAP-2041), and a returned snoozed mail shows up at the client's next refresh rather than by push. - The ACL applies to IMAP. Sending with the grant's tokens is subject to the mail server's usual checks for the user.
- The grant's connections are the background job's own. The middleware caches and pools mail connections per user, not per credential, so a session through the grant neither takes a connection a session of the user opened nor leaves its own for one: the narrowed token never reaches a folder through the user's connection, and the user's next request never inherits the narrowed rights.
Details: Narrowing the grant's tokens at the mail server.
Checking the setup
- Log in through OpenID Connect and schedule a mail. App Suite UI asks for background mail access; once it is allowed in the popup, the UI schedules the mail by itself. Within the same login session, the identity provider usually does not ask to sign in again.
- The provisioning API lists the user's kept credentials (
GET /prov/v1/contexts/{context_id}/users/{user_id}/mail-credentials, see Administration): one with featureoffline-grantand kindofflinein statekept_alive, and one with kinddelegatedfor the scheduled mail. - Log out and wait for the due time: the mail is sent, and its copy is in the sent folder. The credential of kind
delegatedis released once the mail went out; the grant stays. - With narrowing: schedule a reply. It is sent, the replied mail stays unmarked, and the middleware logs a warning with
IMAP-2041- proof that the background job worked with the narrowed rights.
App Suite UI shows the grant's state in the security settings; it reads it from GET <prefix>/oidc[/<backend>]/offline_grant?flow=status&session=<id>, which answers e.g. {"state":"granted","features":["scheduled-mail","snoozed-mail"]}. The end-point and its flows are described in OpenID Connect 1.0 SSO.
Troubleshooting
| Symptom | Cause |
|---|---|
Scheduling or snoozing fails with MCRED-0014 | The user has not allowed background mail access, or the identity provider ended the grant (state rejected): the user has to allow it again |
The popup ends with offlineGrant=failed, OIDC-0030 logged | The identity provider issued no refresh token: check the offline_access scope of the client and role of the user |
The popup ends with offlineGrant=failed, OIDC-0031 logged | Another user signed in at the identity provider than the one logged in to App Suite; the tokens issued to that user are revoked |
OIDC-0033 logged | The App Suite session ended while the user was at the identity provider |
| A due mail waits in the scheduled folder, the user was told by mail | The grant was revoked or ended; the mail goes out once the user allowed background access again, on the next run |
| The background job's login fails at the mail server | The mail server does not accept the grant's access tokens: check that it validates tokens of the login's client and, with narrowing, that the ACL group is resolved |