Fix 2FA support

The 2FA information is now returned after the /auth request instead of
the /auth/info one.

Closes: https://github.com/emersion/hydroxide/issues/76
This commit is contained in:
Simon Ser 2019-12-08 12:14:41 +01:00
parent 1edc88fd5a
commit 80e0fa6f3e
No known key found for this signature in database
GPG Key ID: 0FDE7BE0E88F5E48
4 changed files with 52 additions and 20 deletions

View File

@ -119,11 +119,11 @@ func authenticate(c *protonmail.Client, cachedAuth *CachedAuth, username string)
return nil, fmt.Errorf("cannot re-authenticate: failed to get auth info: %v", err)
}
if authInfo.TwoFactor == 1 {
if cachedAuth.TwoFactor.Enabled == 1 {
return nil, fmt.Errorf("cannot re-authenticate: two factor authentication enabled, please login manually")
}
auth, err = c.Auth(username, cachedAuth.LoginPassword, "", authInfo)
auth, err = c.Auth(username, cachedAuth.LoginPassword, authInfo)
if err != nil {
return nil, fmt.Errorf("cannot re-authenticate: %v", err)
}

View File

@ -153,18 +153,27 @@ func main() {
log.Fatal(err)
}
var twoFactorCode string
if authInfo.TwoFactor == 1 {
scanner := bufio.NewScanner(os.Stdin)
fmt.Printf("2FA code: ")
scanner.Scan()
twoFactorCode = scanner.Text()
}
a, err = c.Auth(username, loginPassword, twoFactorCode, authInfo)
a, err = c.Auth(username, loginPassword, authInfo)
if err != nil {
log.Fatal(err)
}
if a.TwoFactor.Enabled == 1 {
if a.TwoFactor.TOTP != 1 {
log.Fatal("Only TOTP is supported as a 2FA method")
}
scanner := bufio.NewScanner(os.Stdin)
fmt.Printf("2FA TOTP code: ")
scanner.Scan()
code := scanner.Text()
scope, err := c.AuthTOTP(code)
if err != nil {
log.Fatal(err)
}
a.Scope = scope
}
}
var mailboxPassword string

View File

@ -16,12 +16,6 @@ type authInfoReq struct {
}
type AuthInfo struct {
TwoFactor int
TwoFactorInfo struct {
Enabled int
U2F interface{} // TODO
TOTP int
} `json:"2FA"`
version int
modulus string
serverEphemeral string
@ -72,7 +66,6 @@ type authReq struct {
SRPSession string
ClientEphemeral string
ClientProof string
TwoFactorCode string
}
type PasswordMode int
@ -88,8 +81,14 @@ type Auth struct {
UID string
AccessToken string
RefreshToken string
UserID string
EventID string
PasswordMode PasswordMode
TwoFactor struct {
Enabled int
U2F interface{} // TODO
TOTP int
} `json:"2FA"`
}
type authResp struct {
@ -106,7 +105,7 @@ func (resp *authResp) auth() *Auth {
return auth
}
func (c *Client) Auth(username, password, twoFactorCode string, info *AuthInfo) (*Auth, error) {
func (c *Client) Auth(username, password string, info *AuthInfo) (*Auth, error) {
if info == nil {
var err error
if info, err = c.AuthInfo(username); err != nil {
@ -124,7 +123,6 @@ func (c *Client) Auth(username, password, twoFactorCode string, info *AuthInfo)
SRPSession: info.srpSession,
ClientEphemeral: base64.StdEncoding.EncodeToString(proofs.clientEphemeral),
ClientProof: base64.StdEncoding.EncodeToString(proofs.clientProof),
TwoFactorCode: twoFactorCode,
}
req, err := c.newJSONRequest(http.MethodPost, "/auth", reqData)
@ -147,6 +145,29 @@ func (c *Client) Auth(username, password, twoFactorCode string, info *AuthInfo)
return auth, nil
}
func (c *Client) AuthTOTP(code string) (scope string, err error) {
reqData := struct {
TwoFactorCode string
}{
TwoFactorCode: code,
}
req, err := c.newJSONRequest(http.MethodPost, "/auth/2fa", reqData)
if err != nil {
return "", err
}
respData := struct {
resp
Scope string
}{}
if err := c.doJSON(req, &respData); err != nil {
return "", err
}
return respData.Scope, nil
}
type authRefreshReq struct {
UID string `json:"Uid"`
RefreshToken string

View File

@ -157,6 +157,8 @@ func (c *Client) doJSON(req *http.Request, respData interface{}) error {
return err
}
//log.Printf("<< %v %v\n%#v", req.Method, req.URL.Path, respData)
if maybeError, ok := respData.(maybeError); ok {
if err := maybeError.Err(); err != nil {
log.Printf("request failed: %v %v: %v", req.Method, req.URL.String(), err)