Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
0cd6243
Dovecot xoauth2 login
Aug 13, 2023
e661240
OAuth2 on top
keval6b Nov 2, 2023
a003caa
Remove oauth2 account provisioner
keval6b Nov 3, 2023
6ea253f
Ensure auth_mechanism change is done last
keval6b Nov 3, 2023
634983a
Remove userdb to force underlying
keval6b Nov 3, 2023
07b446c
Switch to introspection
keval6b Nov 3, 2023
4a6cfca
Initial documentation
keval6b Nov 4, 2023
d1e40d9
Fix PR number
keval6b Nov 4, 2023
facacb7
Test bracket
keval6b Nov 15, 2023
5591774
Finalise test
keval6b Nov 15, 2023
413ae1e
Link for PR in docs
keval6b Nov 15, 2023
43f121d
Apply suggestions from code review
keval6b Dec 23, 2023
fe837a0
Apply suggestions from code review
keval6b Dec 24, 2023
c4e086b
More cleanup
keval6b Dec 26, 2023
58688a9
Token and XOAUTH maintainer documentation
keval6b Dec 26, 2023
127b3e1
Tabbed and improved user docs
keval6b Dec 26, 2023
9c44199
Update missed environment.md
keval6b Jan 1, 2024
b1cebc8
Changelog
keval6b Jan 1, 2024
de388b1
Apply suggestions from code review
georglauterbach Jan 2, 2024
cdcefe0
Apply suggestions from code review
keval6b Jan 3, 2024
5295e91
More suggested changes
keval6b Jan 3, 2024
a31fbf9
Fix false positive test
keval6b Jan 3, 2024
de24679
Remove unneeded client id and secret
keval6b Jan 3, 2024
e62a870
Update docs/content/config/environment.md
keval6b Jan 3, 2024
9b7dda8
Update CHANGELOG.md
keval6b Jan 3, 2024
0c5d615
Update README.md
keval6b Jan 3, 2024
3ac93c1
Update target/scripts/startup/setup.d/oauth2.sh
keval6b Jan 3, 2024
ccfd43f
Update docs/content/config/environment.md
keval6b Jan 3, 2024
70b82e2
Update docs/content/config/environment.md
keval6b Jan 3, 2024
7facf79
Apply suggestions from code review
polarathene Jan 6, 2024
6a80188
Apply suggestions from code review
polarathene Jan 6, 2024
2a9a2c2
Merge branch 'master' into oauth2
polarathene Jan 6, 2024
7d4db46
Update mailserver.env
polarathene Jan 6, 2024
b01201f
fix: Sync to testing changes from master branch
polarathene Jan 6, 2024
5852ae5
Update target/scripts/startup/setup.d/oauth2.sh
casperklein Jan 6, 2024
5a6f2b1
Update target/dovecot/dovecot-oauth2.conf.ext
polarathene Jan 6, 2024
b24096d
Apply suggestions from code review
polarathene Jan 7, 2024
a13c8d4
Update test/tests/serial/mail_with_oauth2.bats
polarathene Jan 12, 2024
acfc44d
Merge branch 'master' into oauth2
polarathene Jan 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#################################################

.env
compose.override.yaml
Comment thread
keval6b marked this conversation as resolved.
docs/site/
docker-data/

Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ All notable changes to this project will be documented in this file. The format

> **Note**: Changes and additions listed here are contained in the `:edge` image tag. These changes may not be as stable as released changes.

### Features

- **Authentication with OIDC / OAuth 2.0** 🎉
- DMS now supports authentication via OAuth2 (_via `XOAUTH2` or `OAUTHBEARER` SASL mechanisms_) from capable services (_like Roundcube_).
- This does not replace the need for an `ACCOUNT_PROVISIONER` (`FILE` / `LDAP`), which is required for an account to receive or send mail.
- Successful authentication (_via Dovecot PassDB_) still requires an existing account (_lookup via Dovecot UserDB_).

### Updates

- **Tests**:
Expand Down
7 changes: 7 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ EOF
COPY target/rspamd/local.d/ /etc/rspamd/local.d/
COPY target/rspamd/scores.d/* /etc/rspamd/scores.d/

# -----------------------------------------------
# --- OAUTH2 ------------------------------------
# -----------------------------------------------

COPY target/dovecot/auth-oauth2.conf.ext /etc/dovecot/conf.d
COPY target/dovecot/dovecot-oauth2.conf.ext /etc/dovecot

# -----------------------------------------------
# --- LDAP & SpamAssassin's Cron ----------------
# -----------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ If you have issues, please search through [the documentation][documentation::web
- Support for [LetsEncrypt](https://letsencrypt.org/), manual and self-signed certificates
- A [setup script](https://docker-mailserver.github.io/docker-mailserver/latest/config/setup.sh) for easy configuration and maintenance
- SASLauthd with LDAP authentication
- OAuth2 authentication (_via `XOAUTH2` or `OAUTHBEARER` SASL mechanisms_)
69 changes: 69 additions & 0 deletions docs/content/config/advanced/auth-oauth2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
title: 'Advanced | Basic OAuth2 Authentication'
---

## Introduction

!!! warning "This is only a supplement to the existing account provisioners"

Accounts must still be managed via the configured [`ACCOUNT_PROVISIONER`][env::account-provisioner] (FILE or LDAP).

Reasoning for this can be found in [#3480][gh-pr::oauth2]. Future iterations on this feature may allow it to become a full account provisioner.

[gh-pr::oauth2]: https://github.com/docker-mailserver/docker-mailserver/pull/3480
[env::account-provisioner]: ../environment.md#account_provisioner

The present OAuth2 support provides the capability for 3rd-party applications such as Roundcube to authenticate with DMS (dovecot) by using a token obtained from an OAuth2 provider, instead of passing passwords around.

Comment on lines +16 to +17
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should perhaps raise awareness here about relevant config.

  • Dovecot username_attribute = email (default) could be configured to something else when necessary.
  • Likewise from Dovecot docs introspection_mode relation to the introspection_url
    • From what I understand the common userinfo/ endpoint is public and should be sufficient.
    • introspection/ or equivalent may require auth (where the client id + secret) could be provided in the URL AFAIK (eg: https://client_id:[email protected]/admin/oauth2/introspect) which is syntax for providing basic auth headers 🤔

Moving away from ENV config, we would document the basics we're familiar with for that Dovecot file. We could optionally mention the Docker Compose configs feature with an example, but that's not necessary.

I can contribute this in a follow-up PR that drops ENV implementation (we'll just merge this PR and address that before release in the follow-up).

## Example (Authentik & Roundcube)
Comment thread
keval6b marked this conversation as resolved.

This example assumes you have:

- A working DMS server set up
- An Authentik server set up ([documentation](https://goauthentik.io/docs/installation/))
- A Roundcube server set up (either [docker](https://hub.docker.com/r/roundcube/roundcubemail/) or [bare metal](https://github.com/roundcube/roundcubemail/wiki/Installation))

!!! example "Setup Instructions"

=== "1. Docker Mailserver"
Edit the following values in `mailserver.env`:
```env
# -----------------------------------------------
# --- OAUTH2 Section ----------------------------
# -----------------------------------------------

# empty => OAUTH2 authentication is disabled
# 1 => OAUTH2 authentication is enabled
ENABLE_OAUTH2=1

# Specify the user info endpoint URL of the oauth2 provider
OAUTH2_INTROSPECTION_URL=https://authentik.example.com/application/o/userinfo/
```

=== "2. Authentik"
1. Create a new OAuth2 provider
2. Note the client id and client secret
3. Set the allowed redirect url to the equivalent of `https://roundcube.example.com/index.php/login/oauth` for your RoundCube instance.

=== "3. Roundcube"
Add the following to `oauth2.inc.php` ([documentation](https://github.com/roundcube/roundcubemail/wiki/Configuration)):

```php
$config['oauth_provider'] = 'generic';
$config['oauth_provider_name'] = 'Authentik';
$config['oauth_client_id'] = '<insert client id here>';
$config['oauth_client_secret'] = '<insert client secret here>';
$config['oauth_auth_uri'] = 'https://authentik.example.com/application/o/authorize/';
$config['oauth_token_uri'] = 'https://authentik.example.com/application/o/token/';
$config['oauth_identity_uri'] = 'https://authentik.example.com/application/o/userinfo/';

// Optional: disable SSL certificate check on HTTP requests to OAuth server. For possible values, see:
// http://docs.guzzlephp.org/en/stable/request-options.html#verify
$config['oauth_verify_peer'] = false;

$config['oauth_scope'] = 'email openid profile';
$config['oauth_identity_fields'] = ['email'];
Comment on lines +64 to +65
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Context comment (no action required)

This is related to the implicit default for the Dovecot username_attribute = email.


// Boolean: automatically redirect to OAuth login when opening Roundcube without a valid session
$config['oauth_login_redirect'] = false;
```
22 changes: 20 additions & 2 deletions docs/content/config/environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,15 @@ The Group ID assigned to the static vmail group for `/var/mail` (_Mail storage m

Configures the provisioning source of user accounts (including aliases) for user queries and authentication by services managed by DMS (_Postfix and Dovecot_).

User provisioning via OIDC is planned for the future, see [this tracking issue](https://github.com/docker-mailserver/docker-mailserver/issues/2713).
!!! tip "OAuth2 Support"

Presently DMS supports OAuth2 only as an supplementary authentication method.

- A third-party service must provide a valid token for the user which Dovecot validates with the authentication service provider. To enable this feature reference the [OAuth2 configuration example guide][docs::auth::oauth2-config-guide].
- User accounts must be provisioned to receive mail via one of the supported `ACCOUNT_PROVISIONER` providers.
- User provisioning via OIDC is planned for the future, see [this tracking issue](https://github.com/docker-mailserver/docker-mailserver/issues/2713).

[docs::auth::oauth2-config-guide]: ./advanced/auth-oauth2.md

- **empty** => use FILE
- LDAP => use LDAP authentication
Expand Down Expand Up @@ -716,9 +724,19 @@ Enable or disable `getmail`.

- **5** => `getmail` The number of minutes for the interval. Min: 1; Max: 30; Default: 5.

#### LDAP

#### OAUTH2

##### ENABLE_OAUTH2

- **empty** => OAUTH2 authentication is disabled
- 1 => OAUTH2 authentication is enabled

##### OAUTH2_INTROSPECTION_URL
Comment thread
georglauterbach marked this conversation as resolved.

- => Specify the user info endpoint URL of the oauth2 provider (_eg: `https://oauth2.example.com/userinfo/`_)

#### LDAP

##### LDAP_START_TLS

Expand Down
1 change: 1 addition & 0 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ nav:
- 'Postfix': config/advanced/override-defaults/postfix.md
- 'Modifications via Script': config/advanced/override-defaults/user-patches.md
- 'LDAP Authentication': config/advanced/auth-ldap.md
- 'OAuth2 Authentication': config/advanced/auth-oauth2.md
- 'Email Filtering with Sieve': config/advanced/mail-sieve.md
- 'Email Gathering with Fetchmail': config/advanced/mail-fetchmail.md
- 'Email Gathering with Getmail': config/advanced/mail-getmail.md
Expand Down
12 changes: 12 additions & 0 deletions mailserver.env
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,18 @@ ENABLE_GETMAIL=0
# The number of minutes for the interval. Min: 1; Max: 30.
GETMAIL_POLL=5

# -----------------------------------------------
# --- OAUTH2 Section ----------------------------
# -----------------------------------------------

# empty => OAUTH2 authentication is disabled
# 1 => OAUTH2 authentication is enabled
ENABLE_OAUTH2=

# Specify the user info endpoint URL of the oauth2 provider
# Example: https://oauth2.example.com/userinfo/
OAUTH2_INTROSPECTION_URL=

# -----------------------------------------------
# --- LDAP Section ------------------------------
# -----------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions target/dovecot/10-auth.conf
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ auth_mechanisms = plain login
#!include auth-sql.conf.ext
#!include auth-ldap.conf.ext
!include auth-passwdfile.inc
#!include auth-oauth2.conf.ext
Comment thread
polarathene marked this conversation as resolved.
#!include auth-checkpassword.conf.ext
#!include auth-vpopmail.conf.ext
#!include auth-static.conf.ext
Expand Down
7 changes: 7 additions & 0 deletions target/dovecot/auth-oauth2.conf.ext
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
auth_mechanisms = $auth_mechanisms oauthbearer xoauth2

passdb {
driver = oauth2
mechanisms = xoauth2 oauthbearer
args = /etc/dovecot/dovecot-oauth2.conf.ext
}
4 changes: 4 additions & 0 deletions target/dovecot/dovecot-oauth2.conf.ext
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
introspection_url =
# Dovecot defaults:
introspection_mode = auth
username_attribute = email
5 changes: 5 additions & 0 deletions target/scripts/start-mailserver.sh
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ function _register_functions() {
;;
esac

if [[ ${ENABLE_OAUTH2} -eq 1 ]]; then
_environment_variables_oauth2
_register_setup_function '_setup_oauth2'
fi

if [[ ${ENABLE_SASLAUTHD} -eq 1 ]]; then
_environment_variables_saslauthd
_register_setup_function '_setup_saslauthd'
Expand Down
11 changes: 11 additions & 0 deletions target/scripts/startup/setup.d/oauth2.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash

function _setup_oauth2() {
_log 'debug' 'Setting up OAUTH2'

# Enable OAuth2 PassDB (Authentication):
sedfile -i -e '/\!include auth-oauth2\.conf\.ext/s/^#//' /etc/dovecot/conf.d/10-auth.conf
_replace_by_env_in_file 'OAUTH2_' '/etc/dovecot/dovecot-oauth2.conf.ext'

return 0
}
6 changes: 6 additions & 0 deletions target/scripts/startup/variables-stack.sh
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,12 @@ function __environment_variables_general_setup() {
VARS[UPDATE_CHECK_INTERVAL]="${UPDATE_CHECK_INTERVAL:=1d}"
}

function _environment_variables_oauth2() {
_log 'debug' 'Setting OAUTH2-related environment variables now'

VARS[OAUTH2_INTROSPECTION_URL]="${OAUTH2_INTROSPECTION_URL:=}"
}

# This function handles environment variables related to LDAP.
# NOTE: SASLAuthd and Dovecot LDAP support inherit these common ENV.
function _environment_variables_ldap() {
Expand Down
56 changes: 56 additions & 0 deletions test/config/oauth2/provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# OAuth2 mock service
#
# Dovecot will query this service with the token it was provided.
# If the session for the token is valid, a response provides an attribute to perform a UserDB lookup on (default: email).

import json
Comment thread
keval6b marked this conversation as resolved.
Comment thread
polarathene marked this conversation as resolved.
import base64
from http.server import BaseHTTPRequestHandler, HTTPServer

# OAuth2.0 Bearer token (paste into https://jwt.io/ to check it's contents).
# You should never need to edit this unless you REALLY need to change the issuer.
token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwOi8vcHJvdmlkZXIuZXhhbXBsZS50ZXN0OjgwMDAvIiwic3ViIjoiODJjMWMzMzRkY2M2ZTMxMWFlNGFhZWJmZTk0NmM1ZTg1OGYwNTVhZmYxY2U1YTM3YWE3Y2M5MWFhYjE3ZTM1YyIsImF1ZCI6Im1haWxzZXJ2ZXIiLCJ1aWQiOiI4OU4zR0NuN1M1Y090WkZNRTVBeVhNbmxURFdVcnEzRmd4YWlyWWhFIn0.zuCytArbphhJn9XT_y9cBdGqDCNo68tBrtOwPIsuKNyF340SaOuZa0xarZofygytdDpLtYr56QlPTKImi-n1ZWrHkRZkwrQi5jQ-j_n2hEAL0vUToLbDnXYfc5q2w7z7X0aoCmiK8-fV7Kx4CVTM7riBgpElf6F3wNAIcX6R1ijUh6ISCL0XYsdogf8WUNZipXY-O4R7YHXdOENuOp3G48hWhxuUh9PsUqE5yxDwLsOVzCTqg9S5gxPQzF2eCN9J0I2XiIlLKvLQPIZ2Y_K7iYvVwjpNdgb4xhm9wuKoIVinYkF_6CwIzAawBWIDJAbix1IslkUPQMGbupTDtOgTiQ"

# This is the string the user-facing client (e.g. Roundcube) should send via IMAP to Dovecot.
# We include the user and the above token separated by '\1' chars as per the XOAUTH2 spec.
xoauth2 = base64.b64encode(f"[email protected]\1auth=Bearer {token}\1\1".encode("utf-8"))
# If changing the user above, use the new output from the below line with the contents of the AUTHENTICATE command in test/test-files/auth/imap-oauth2-auth.txt
print("XOAUTH2 string: " + str(xoauth2))
Comment on lines +10 to +18
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Context (no action required)

The token value is not the correct kind from what I understand?

  • Google XOAUTH2 docs imply the Access Token, not ID Token that this appears to be?

    image

  • My own experience with Ory Hydra was the Access Token too.


I was a bit curious with why my initial attempt with Ory Hydra to use an ID Token was failing (without this mock server), so I'm under the impression that the Roundcube + Authentik setup may not have the behaviour this mock server is implying?

When I complete an auth code grant flow with user login, Ory Hydra would respond with both access token and ID token. Roundcube docs indicate that if the response provides the extra data they need like username/email, that'll be used otherwise it'll use the access token to fetch that information IIRC.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From Ory docs, ID Token (OIDC) vs Access Token (OAuth2):

image


So Dovecot provides the Access Token to the authentication server that we have mocked here, and the JSON returned would be either of the above depending on endpoint AFAIK. You can see the common fields involved, but for the email claim, that'd be an ID Token.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is due to a lack of documentation on Authentik's part (the service I've been testing with). I have been barely aware that it actually has an introspect endpoint until now. The userinfo endpoint worked so I had no reason to venture elsewhere.

Hence I will concur, the access token appears to be more correct.



class HTTPRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
auth = self.headers.get("Authorization")
if auth is None:
self.send_response(401)
self.end_headers()
return
if len(auth.split()) != 2:
self.send_response(401)
self.end_headers()
return
auth = auth.split()[1]
# Valid session, respond with JSON containing the expected `email` claim to match as Dovecot username:
if auth == token:
Comment thread
polarathene marked this conversation as resolved.
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps({
"email": "[email protected]",
"email_verified": True,
"sub": "82c1c334dcc6e311ae4aaebfe946c5e858f055aff1ce5a37aa7cc91aab17e35c"
}).encode("utf-8"))
else:
self.send_response(401)
self.end_headers()

server = HTTPServer(('', 80), HTTPRequestHandler)
print("Starting server", flush=True)

try:
server.serve_forever()
except KeyboardInterrupt:
print()
print("Received keyboard interrupt")
finally:
print("Exiting")
4 changes: 4 additions & 0 deletions test/files/auth/imap-oauth2-auth.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
a0 NOOP See test/config/oauth2/provider.py to generate the below XOAUTH2 string
a1 AUTHENTICATE XOAUTH2 dXNlcj11c2VyMUBsb2NhbGhvc3QubG9jYWxkb21haW4BYXV0aD1CZWFyZXIgZXlKaGJHY2lPaUpTVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnBjM01pT2lKb2RIUndPaTh2Y0hKdmRtbGtaWEl1WlhoaGJYQnNaUzUwWlhOME9qZ3dNREF2SWl3aWMzVmlJam9pT0RKak1XTXpNelJrWTJNMlpUTXhNV0ZsTkdGaFpXSm1aVGswTm1NMVpUZzFPR1l3TlRWaFptWXhZMlUxWVRNM1lXRTNZMk01TVdGaFlqRTNaVE0xWXlJc0ltRjFaQ0k2SW0xaGFXeHpaWEoyWlhJaUxDSjFhV1FpT2lJNE9VNHpSME51TjFNMVkwOTBXa1pOUlRWQmVWaE5ibXhVUkZkVmNuRXpSbWQ0WVdseVdXaEZJbjAuenVDeXRBcmJwaGhKbjlYVF95OWNCZEdxRENObzY4dEJydE93UElzdUtOeUYzNDBTYU91WmEweGFyWm9meWd5dGREcEx0WXI1NlFsUFRLSW1pLW4xWldySGtSWmt3clFpNWpRLWpfbjJoRUFMMHZVVG9MYkRuWFlmYzVxMnc3ejdYMGFvQ21pSzgtZlY3S3g0Q1ZUTTdyaUJncEVsZjZGM3dOQUljWDZSMWlqVWg2SVNDTDBYWXNkb2dmOFdVTlppcFhZLU80UjdZSFhkT0VOdU9wM0c0OGhXaHh1VWg5UHNVcUU1eXhEd0xzT1Z6Q1RxZzlTNWd4UFF6RjJlQ045SjBJMlhpSWxMS3ZMUVBJWjJZX0s3aVl2VndqcE5kZ2I0eGhtOXd1S29JVmluWWtGXzZDd0l6QWF3QldJREpBYml4MUlzbGtVUFFNR2J1cFREdE9nVGlRAQE=
a2 EXAMINE INBOX
a3 LOGOUT
66 changes: 66 additions & 0 deletions test/tests/serial/mail_with_oauth2.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
load "${REPOSITORY_ROOT}/test/helper/setup"
load "${REPOSITORY_ROOT}/test/helper/common"

BATS_TEST_NAME_PREFIX='[OAuth2] '
CONTAINER1_NAME='dms-test_oauth2'
CONTAINER2_NAME='dms-test_oauth2_provider'

function setup_file() {
export DMS_TEST_NETWORK='test-network-oauth2'
export DMS_DOMAIN='example.test'
export FQDN_MAIL="mail.${DMS_DOMAIN}"
export FQDN_OAUTH2="oauth2.${DMS_DOMAIN}"

# Link the test containers to separate network:
# NOTE: If the network already exists, test will fail to start.
docker network create "${DMS_TEST_NETWORK}"

# Setup local oauth2 provider service:
docker run --rm -d --name "${CONTAINER2_NAME}" \
--hostname "${FQDN_OAUTH2}" \
--network "${DMS_TEST_NETWORK}" \
--volume "${REPOSITORY_ROOT}/test/config/oauth2/:/app/" \
docker.io/library/python:latest \
python /app/provider.py

_run_until_success_or_timeout 20 sh -c "docker logs ${CONTAINER2_NAME} 2>&1 | grep 'Starting server'"

#
# Setup DMS container
#

# Add OAUTH2 configuration so that Dovecot can reach out to our mock provider (CONTAINER2)
local ENV_OAUTH2_CONFIG=(
--env ENABLE_OAUTH2=1
--env OAUTH2_INTROSPECTION_URL=http://oauth2.example.test/userinfo/
)

export CONTAINER_NAME=${CONTAINER1_NAME}
local CUSTOM_SETUP_ARGUMENTS=(
"${ENV_OAUTH2_CONFIG[@]}"

--hostname "${FQDN_MAIL}"
--network "${DMS_TEST_NETWORK}"
)

_init_with_defaults
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
_wait_for_tcp_port_in_container 143

# Set default implicit container fallback for helpers:
export CONTAINER_NAME=${CONTAINER1_NAME}
}

function teardown_file() {
docker rm -f "${CONTAINER1_NAME}" "${CONTAINER2_NAME}"
docker network rm "${DMS_TEST_NETWORK}"
}


@test "oauth2: imap connect and authentication works" {
# An initial connection needs to be made first, otherwise the auth attempt fails
_run_in_container_bash 'nc -vz 0.0.0.0 143'

_nc_wrapper 'auth/imap-oauth2-auth.txt' '-w 1 0.0.0.0 143'
assert_output --partial 'Examine completed'
}