Summary
Authentication fails for an account that has both TOTP 2FA and two-password mode (PasswordMode 2) enabled. The SRP login and the TOTP step both succeed (server returns Code:1000 and an elevated scope), but the immediately following GET /api/keys/salts is rejected with [401] Invalid access token. Auth aborts before reaching the unlock step, so no bridge password is ever generated.
Environment
- hydroxide: 0.2.32
- OS: Linux (x86_64)
- Account: TOTP 2FA enabled, two-password mode (
PasswordMode 2)
Steps to reproduce
- Use an account with both TOTP 2FA and two-password mode enabled.
- Run
hydroxide auth <username>.
- Enter the login password, then the TOTP code, then the mailbox password.
Expected behavior
Login completes and a bridge password is printed.
Actual behavior
GET /api/keys/salts returns [401] Invalid access token right after the 2FA step succeeds, and auth aborts.
Debug log (anonymized)
$ hydroxide -debug auth <username>
Password:
>> POST /api/auth/info
<< POST /api/auth/info
Code:1000 Version:4
>> POST /api/auth
<< POST /api/auth
Code:1000
Scope:"self parent user twofactor"
UID:"<redacted>"
AccessToken:"<redacted>"
RefreshToken:"<redacted>"
PasswordMode:2
TwoFactor:{Enabled:1, U2F:<nil>, TOTP:1}
ExpiresIn:1800 TokenType:"Bearer"
2FA TOTP code: ******
>> POST /api/auth/2fa
{"TwoFactorCode":"******"}
<< POST /api/auth/2fa
Code:1000
Scope:"full self payments keys parent user loggedin nondelinquent mail vpn calendar drive docs pass verified settings wallet lumo meet"
Mailbox password:
>> GET /api/keys/salts
<< GET /api/keys/salts
Code:401
request failed: GET https://mail.proton.me/api/keys/salts: [401] Invalid access token
[401] Invalid access token
Analysis
Observations from the debug log:
POST /api/auth succeeds with Scope:"self parent user twofactor" and returns an access/refresh token pair.
POST /api/auth/2fa succeeds (Code:1000) and the response carries the elevated scope "full self payments keys ... mail ...". The response struct hydroxide parses for this call (struct { resp; Scope string }) only contains the scope, no refreshed token.
- The subsequent
GET /api/keys/salts, sent with the access token obtained before the 2FA step, is rejected with 401.
- Token expiry is not the cause:
ExpiresIn was 1800s and the salts call happened within a few seconds of login.
This suggests the API invalidates or rotates the pre-2FA access token once the second factor is completed, and expects the client to obtain an updated token before issuing further requests (e.g. via POST /api/auth/refresh with the refresh token, or by reading an updated token from the 2FA response). hydroxide keeps using the pre-2FA token, whose scope was "...twofactor", and the server no longer accepts it.
I observed this with both TOTP 2FA and two-password mode enabled together; I have not tested whether either alone reproduces it.
Possibly related
#341 (POST /api/auth/refresh -> [10013] Invalid refresh token)
Summary
Authentication fails for an account that has both TOTP 2FA and two-password mode (
PasswordMode 2) enabled. The SRP login and the TOTP step both succeed (server returnsCode:1000and an elevated scope), but the immediately followingGET /api/keys/saltsis rejected with[401] Invalid access token. Auth aborts before reaching the unlock step, so no bridge password is ever generated.Environment
PasswordMode 2)Steps to reproduce
hydroxide auth <username>.Expected behavior
Login completes and a bridge password is printed.
Actual behavior
GET /api/keys/saltsreturns[401] Invalid access tokenright after the 2FA step succeeds, and auth aborts.Debug log (anonymized)
Analysis
Observations from the debug log:
POST /api/authsucceeds withScope:"self parent user twofactor"and returns an access/refresh token pair.POST /api/auth/2fasucceeds (Code:1000) and the response carries the elevated scope"full self payments keys ... mail ...". The response struct hydroxide parses for this call (struct { resp; Scope string }) only contains the scope, no refreshed token.GET /api/keys/salts, sent with the access token obtained before the 2FA step, is rejected with 401.ExpiresInwas 1800s and the salts call happened within a few seconds of login.This suggests the API invalidates or rotates the pre-2FA access token once the second factor is completed, and expects the client to obtain an updated token before issuing further requests (e.g. via
POST /api/auth/refreshwith the refresh token, or by reading an updated token from the 2FA response). hydroxide keeps using the pre-2FA token, whose scope was"...twofactor", and the server no longer accepts it.I observed this with both TOTP 2FA and two-password mode enabled together; I have not tested whether either alone reproduces it.
Possibly related
#341 (
POST /api/auth/refresh->[10013] Invalid refresh token)