Skip to content

Commit 8b2c0ad

Browse files
committed
Improve CCG documentation; require oauth2_flow to be set
See #214
1 parent a43fa2e commit 8b2c0ad

File tree

2 files changed

+47
-23
lines changed

2 files changed

+47
-23
lines changed

emailproxy.config

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,17 @@ documentation = Accounts are specified using your email address as the section h
121121
- The proxy supports the client credentials grant (CCG) and resource owner password credentials grant (ROPCG) OAuth
122122
2.0 flows (both currently only known to be available for Office 365). To use either of these flows, add an account
123123
entry as normal, but do not add a `permission_url` value (it does not apply, and its absence signals to the proxy to
124-
use the appropriate token retrieval mechanism). An example is given at the end of the sample account entries below.
125-
The CCG flow is the default in this mode; to specify the ROPCG flow add `oauth2_flow = password` in your account
126-
configuration, and use a normal `oauth2_scope` value rather than the CCG-specific `.default`. Please note also that
127-
Office 365's CCG flow does not support SMTP, and by default it has essentially no local access control when using
128-
IMAP/POP in this mode (no user consent is involved, so the proxy cannot validate passwords). Because of this, you
129-
are encouraged to enable the proxy's secret encryption option - see `encrypt_client_secret_on_first_use` at the end
130-
of this file. In addition, if you are using the proxy in an environment where there is any possibility of malicious
131-
access attempts before the first valid login then pre-encrypting account entries is highly recommended - see the
132-
example setup script at https://github.com/simonrob/email-oauth2-proxy/issues/61#issuecomment-1259110336.
124+
use the appropriate token retrieval mechanism). An example is given for both methods towards the end of the sample
125+
account entries below.
126+
127+
- WARNING: Please note that by default the CCG flow has essentially no local access control when creating new
128+
accounts (no user consent is involved, so the proxy cannot validate login attempts unless an account entry
129+
already exists its configuration file). This is especially important when using the proxy's catch-all feature
130+
(which is likely to be the case given the typical use-cases for the CCG flow). Because of this, you are highly
131+
encouraged to enable the proxy's secret encryption option - see `encrypt_client_secret_on_first_use` at the end
132+
of this file. In addition, if you are using the proxy in an environment where there is any possibility of
133+
malicious access attempts before the first valid login, pre-encrypting account entries is highly recommended.
134+
See the example script at https://github.com/simonrob/email-oauth2-proxy/issues/61#issuecomment-1259110336.
133135

134136
Advanced account configuration:
135137
- For most configurations the default `redirect_uri` value of `http://localhost` is correct, unless you have
@@ -181,7 +183,7 @@ redirect_uri = http://localhost
181183
client_id = *** your client id here ***
182184
client_secret = *** your client secret here ***
183185

184-
[ccg.or.ropcg.[email protected]]
186+
185187
documentation = *** note: this is an advanced O365 account example; in most cases you want the version above instead ***
186188
token_url = https://login.microsoftonline.com/*** your tenant id here ***/oauth2/v2.0/token
187189
oauth2_scope = https://outlook.office365.com/.default
@@ -190,6 +192,15 @@ redirect_uri = http://localhost
190192
client_id = *** your client id here ***
191193
client_secret = *** your client secret here ***
192194

195+
196+
documentation = *** note: this is an advanced O365 account example; in most cases you want the version above instead ***
197+
token_url = https://login.microsoftonline.com/*** your tenant id here ***/oauth2/v2.0/token
198+
oauth2_scope = https://outlook.office365.com/IMAP.AccessAsUser.All https://outlook.office365.com/POP.AccessAsUser.All https://outlook.office365.com/SMTP.Send offline_access
199+
oauth2_flow = password
200+
redirect_uri = http://localhost
201+
client_id = *** your client id here ***
202+
client_secret = *** your client secret here ***
203+
193204
[Advanced proxy configuration]
194205
documentation = The parameters below control advanced options for the proxy. In most cases you will not need to modify
195206
the values in this section. If any of these values are not found, the proxy will assume the default value, which
@@ -210,13 +221,16 @@ documentation = The parameters below control advanced options for the proxy. In
210221
cached access tokens) using the password that is given when accessing an account via IMAP/POP/SMTP. It does not do
211222
this for values that are not sensitive. In the most common operation mode (i.e., interactively authorising account
212223
access), the `client_secret` value falls into this category - it is not actually secret, and there is no real need
213-
to prevent access to it. However, when using the Office 365 client credentials grant flow there is no user involved,
214-
and possession of the secret grants full access to an account. If you use this method and it is possible that others
215-
may gain access to the proxy's configuration file, set `encrypt_client_secret_on_first_use` to True and the proxy
216-
will replace the `client_secret` value with a new property `client_secret_encrypted` at the next token refresh. Note
217-
that this option is not compatible with `allow_catch_all_accounts` unless all accounts use the same login password.
218-
In addition, if you are using the proxy's `--cache-store` parameter you will need to manually remove unencrypted
219-
secrets from this configuration file after the encrypted secret has been created (i.e., it will not be automatic).
224+
to prevent access to it. However, when using the client credentials grant (CCG) flow or a service account, there is
225+
no user involved, and possession of the secret grants full access to an account. If you use either of these methods
226+
and it is possible that others may gain access to the proxy's configuration file; or, you are using catch-all
227+
accounts (see below) and others may attempt to log in with accounts that the secret has access to but that you have
228+
not yet set up with the proxy, set `encrypt_client_secret_on_first_use` to True and the proxy will replace the
229+
`client_secret` value with a new property `client_secret_encrypted` at the next token refresh. Note that this option
230+
is not fully compatible with `allow_catch_all_accounts` unless all accounts use the same login password, or you
231+
undertake some additional manual setup configuration (see below for further details). In particular, if you are
232+
using catch-all accounts or the proxy's `--cache-store` parameter you must manually remove unencrypted secrets from
233+
the local configuration file after the encrypted secret has been created (i.e., this will not be automatic).
220234

221235
- allow_catch_all_accounts (default = False): The default behaviour of the proxy is to require a full separate
222236
configuration file entry for each account. However, when proxying multiple accounts from the same domain it can be
@@ -225,9 +239,11 @@ documentation = The parameters below control advanced options for the proxy. In
225239
example, add a section [@domain.com] with all of the standard required account values, and the proxy will intercept
226240
authentication requests for all usernames at `domain.com`. Whenever a previously unseen account attempts to connect,
227241
account authorisation will take place as normal, and the proxy will automatically create a new account-level section
228-
that does not need to be configured manually. Any account-level configuration will override domain-level values.
229-
If needed, the global catch-all section [@] can also be used. Please note that this option is not compatible with
230-
`encrypt_client_secret_on_first_use` unless all IMAP/POP/SMTP accounts at the same domain use the same password.
242+
that does not need to be configured manually. Any account-level configuration will override domain-level values
243+
(except for account access and refresh tokens). If needed, the global catch-all section [@] can also be used. Please
244+
note that this option is not fully compatible with `encrypt_client_secret_on_first_use` unless all IMAP/POP/SMTP
245+
accounts at the same domain use the same password, or you undertake additional manual configuraton steps - see the
246+
discussion at https://github.com/simonrob/email-oauth2-proxy/issues/214#issuecomment-1861593781 for details.
231247

232248
[emailproxy]
233249
delete_account_token_on_password_error = True

emailproxy.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
__author__ = 'Simon Robinson'
77
__copyright__ = 'Copyright (c) 2023 Simon Robinson'
88
__license__ = 'Apache 2.0'
9-
__version__ = '2023-11-19' # ISO 8601 (YYYY-MM-DD)
9+
__version__ = '2023-12-20' # ISO 8601 (YYYY-MM-DD)
1010
__package_version__ = '.'.join([str(int(i)) for i in __version__.split('-')]) # for pyproject.toml usage only
1111

1212
import abc
@@ -799,7 +799,11 @@ def get_account_with_catch_all_fallback(option):
799799
return False, '%s: Login failed for account %s: %s' % (APP_NAME, username, auth_result)
800800

801801
if not oauth2_flow:
802-
oauth2_flow = 'client_credentials' # default to CCG over ROPCG if not set (ROPCG is `password`)
802+
Log.error('No `oauth2_flow` value specified for', username, '- aborting login')
803+
return (False, '%s: Incomplete config file entry found for account %s - please make sure an '
804+
'`oauth2_flow` value is specified when using a method that does not require a '
805+
'`permission_url`' % (APP_NAME, username))
806+
803807
response = OAuth2Helper.get_oauth2_authorisation_tokens(token_url, redirect_uri, client_id,
804808
client_secret, auth_result, oauth2_scope,
805809
oauth2_flow, username, password)
@@ -852,7 +856,11 @@ def get_account_with_catch_all_fallback(option):
852856
return OAuth2Helper.get_oauth2_credentials(username, password, reload_remote_accounts=False)
853857

854858
except InvalidToken as e:
855-
if AppConfig.get_global('delete_account_token_on_password_error', fallback=True):
859+
# regardless of the `delete_account_token_on_password_error` setting, we only reset tokens for standard or
860+
# ROPCG flows; when using CCG or a service account it is far safer to deny account access and require a
861+
# config file edit in order to reset an account rather than allowing *any* password to be used for access
862+
if AppConfig.get_global('delete_account_token_on_password_error', fallback=True) and (
863+
permission_url or oauth2_flow not in ['client_credentials', 'service_account']):
856864
config.remove_option(username, 'access_token')
857865
config.remove_option(username, 'access_token_expiry')
858866
config.remove_option(username, 'token_salt')

0 commit comments

Comments
 (0)