simonrob/email-oauth2-proxy

Error refreshing access token with device authorisation grant (DAG) flow

Closed this issue · 2 comments

Hi guys,

first of all thanks a lot for the excellent piece of software :-)

I'm experiencing a refresh token issue when using device authorisation grant (DAG) flow:

Every time the access time is expired, I get errors like this:

2025-06-20 14:23:58: Error refreshing access token for account xxx@yyy.zzz - received invalid response: {'error': 'invalid_client', 'error_description': "AADSTS700025: Client is public so neither 'client_assertion' nor 'client_secret' should be presented. Trace ID: 86260b33-c971-4da3-b613-7570cacc1f00 Correlation ID: b7c78faf-d961-4d33-9692-d44607e9461f Timestamp: 2025-06-20 12:23:58Z", 'error_codes': [700025], 'timestamp': '2025-06-20 12:23:58Z', 'trace_id': '86260b33-c971-4da3-b613-7570cacc1f00', 'correlation_id': 'b7c78faf-d961-4d33-9692-d44607e9461f'}
2025-06-20 14:23:58: Caught exception while requesting OAuth 2.0 credentials for account xxx@yyy.zzz: {'error': 'invalid_client', 'error_description': "AADSTS700025: Client is public so neither 'client_assertion' nor 'client_secret' should be presented. Trace ID: 86260b33-c971-4da3-b613-7570cacc1f00 Correlation ID: b7c78faf-d961-4d33-9692-d44607e9461f Timestamp: 2025-06-20 12:23:58Z", 'error_codes': [700025], 'timestamp': '2025-06-20 12:23:58Z', 'trace_id': '86260b33-c971-4da3-b613-7570cacc1f00', 'correlation_id': 'b7c78faf-d961-4d33-9692-d44607e9461f'}

fyi.
I could fix that for me by tweaking OAuth2Helper.refresh_oauth2_access_token().
(I'm not a Python developer.)

@@ -860,9 +860,14 @@ def get_oauth2_credentials(username, password, reload_remote_accounts=True):
            if access_token or refresh_token:  # if possible, refresh the existing token(s)
                if not access_token or access_token_expiry - current_time < TOKEN_EXPIRY_MARGIN:
                    if refresh_token:
                        response = OAuth2Helper.refresh_oauth2_access_token(token_url, client_id, client_secret,
                                                                            jwt_client_assertion, username,
                        if oauth2_flow == 'device':
                            response = OAuth2Helper.refresh_oauth2_access_token(token_url, client_id, None,
                                                                            None, username,
                                                                            cryptographer.decrypt(refresh_token))
                        else:
                            response = OAuth2Helper.refresh_oauth2_access_token(token_url, client_id, client_secret,
                                                                             jwt_client_assertion, username,
                                                                             cryptographer.decrypt(refresh_token))

                        access_token = response['access_token']
                        config.set(username, 'access_token', cryptographer.encrypt(access_token))

my config

[SMTP-1587]
documentation = *** note: this server will work for both Office 365 and personal Outlook/Hotmail accounts ***
server_address = smtp-mail.outlook.com
server_port = 587
server_starttls = True
local_address = 127.0.0.1

[learning@euro-fusion.org]
permission_url = https://login.microsoftonline.com/tenant_id/oauth2/v2.0/devicecode
token_url = https://login.microsoftonline.com/tenant_id/oauth2/v2.0/token
oauth2_scope = https://outlook.office.com/SMTP.Send offline_access
oauth2_flow = device
client_id = xxx
client_secret = xxx

I'm starting the email proxy with python emailproxy.py --external-auth --no-gui --debug

Thanks for you help ...

Thanks for reporting this. If your client doesn't require a secret, you should remove the whole of this line from the proxy's configuration file. Do you still encounter the problem if you do this?

Ah, actually, I was not aware that DAG with M365 doesn't really require at token (-> client_secret).
That's perfect, as expiring tokens are always a pain.
Just tried and confirmed that everythings works smoothly without client_secret:
-> initial access + subsequent access + refreshing

Thanks a lot for your precious feedback.
Will close the issue right now.