forked from Silverfish/proton-bridge
feat(BRIDGE-14): HV3 implementation - GUI & CLI; ownership verification & CAPTCHA are supported
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@ -168,6 +168,7 @@ message ReportBugRequest {
|
||||
message LoginRequest {
|
||||
string username = 1;
|
||||
bytes password = 2;
|
||||
optional bool useHvDetails = 3;
|
||||
}
|
||||
|
||||
message LoginAbortRequest {
|
||||
@ -308,6 +309,7 @@ message LoginEvent {
|
||||
LoginTwoPasswordsRequestedEvent twoPasswordRequested = 3;
|
||||
LoginFinishedEvent finished = 4;
|
||||
LoginFinishedEvent alreadyLoggedIn = 5;
|
||||
LoginHvRequestedEvent hvRequested = 6;
|
||||
}
|
||||
}
|
||||
|
||||
@ -319,6 +321,7 @@ enum LoginErrorType {
|
||||
TFA_ABORT = 4;
|
||||
TWO_PASSWORDS_ERROR = 5;
|
||||
TWO_PASSWORDS_ABORT = 6;
|
||||
HV_ERROR = 7;
|
||||
}
|
||||
|
||||
message LoginErrorEvent {
|
||||
@ -339,6 +342,10 @@ message LoginFinishedEvent {
|
||||
bool wasSignedOut = 2;
|
||||
}
|
||||
|
||||
message LoginHvRequestedEvent {
|
||||
string hvUrl = 1;
|
||||
}
|
||||
|
||||
//**********************************************************
|
||||
// Update related events
|
||||
//**********************************************************
|
||||
|
||||
@ -100,6 +100,10 @@ func NewLoginAlreadyLoggedInEvent(userID string) *StreamEvent {
|
||||
return loginEvent(&LoginEvent{Event: &LoginEvent_AlreadyLoggedIn{AlreadyLoggedIn: &LoginFinishedEvent{UserID: userID}}})
|
||||
}
|
||||
|
||||
func NewLoginHvRequestedEvent(hvChallengeURL string) *StreamEvent {
|
||||
return loginEvent(&LoginEvent{Event: &LoginEvent_HvRequested{HvRequested: &LoginHvRequestedEvent{HvUrl: hvChallengeURL}}})
|
||||
}
|
||||
|
||||
func NewUpdateErrorEvent(errorType UpdateErrorType) *StreamEvent {
|
||||
return updateEvent(&UpdateEvent{Event: &UpdateEvent_Error{Error: &UpdateErrorEvent{Type: errorType}}})
|
||||
}
|
||||
|
||||
@ -38,6 +38,7 @@ import (
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/bridge"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/certs"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/events"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/hv"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/safe"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/service"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
|
||||
@ -95,6 +96,9 @@ type Service struct { // nolint:structcheck
|
||||
parentPID int
|
||||
parentPIDDoneCh chan struct{}
|
||||
showOnStartup bool
|
||||
|
||||
hvDetails *proton.APIHVDetails
|
||||
useHvDetails bool
|
||||
}
|
||||
|
||||
// NewService returns a new instance of the service.
|
||||
@ -412,6 +416,7 @@ func (s *Service) loginClean() {
|
||||
s.password[i] = '\x00'
|
||||
}
|
||||
s.password = s.password[0:0]
|
||||
s.useHvDetails = false
|
||||
}
|
||||
|
||||
func (s *Service) finishLogin() {
|
||||
@ -424,6 +429,11 @@ func (s *Service) finishLogin() {
|
||||
|
||||
wasSignedOut := s.bridge.HasUser(s.auth.UserID)
|
||||
|
||||
var hvDetails *proton.APIHVDetails
|
||||
if s.useHvDetails {
|
||||
hvDetails = s.hvDetails
|
||||
}
|
||||
|
||||
if len(s.password) == 0 || s.auth.UID == "" || s.authClient == nil {
|
||||
s.log.
|
||||
WithField("hasPass", len(s.password) != 0).
|
||||
@ -439,8 +449,20 @@ func (s *Service) finishLogin() {
|
||||
defer done()
|
||||
|
||||
ctx := context.Background()
|
||||
userID, err := s.bridge.LoginUser(ctx, s.authClient, s.auth, s.password)
|
||||
userID, err := s.bridge.LoginUser(ctx, s.authClient, s.auth, s.password, hvDetails)
|
||||
if err != nil {
|
||||
if hv.IsHvRequest(err) {
|
||||
s.handleHvRequest(err)
|
||||
performCleanup = false
|
||||
return
|
||||
}
|
||||
|
||||
if apiErr := new(proton.APIError); errors.As(err, &apiErr) && apiErr.Code == proton.HumanValidationInvalidToken {
|
||||
s.hvDetails = nil
|
||||
_ = s.SendEvent(NewLoginError(LoginErrorType_HV_ERROR, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
s.log.WithError(err).Errorf("Finish login failed")
|
||||
s.twoPasswordAttemptCount++
|
||||
errType := LoginErrorType_TWO_PASSWORDS_ABORT
|
||||
@ -614,6 +636,18 @@ func (s *Service) monitorParentPID() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) handleHvRequest(err error) {
|
||||
hvDet, hvErr := hv.VerifyAndExtractHvRequest(err)
|
||||
if hvErr != nil {
|
||||
_ = s.SendEvent(NewLoginError(LoginErrorType_HV_ERROR, hvErr.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
s.hvDetails = hvDet
|
||||
hvChallengeURL := hv.FormatHvURL(hvDet)
|
||||
_ = s.SendEvent(NewLoginHvRequestedEvent(hvChallengeURL))
|
||||
}
|
||||
|
||||
// computeFileSocketPath Return an available path for a socket file in the temp folder.
|
||||
func computeFileSocketPath() (string, error) {
|
||||
tempPath := os.TempDir()
|
||||
|
||||
@ -396,6 +396,14 @@ func (s *Service) RequestKnowledgeBaseSuggestions(_ context.Context, userInput *
|
||||
func (s *Service) Login(_ context.Context, login *LoginRequest) (*emptypb.Empty, error) {
|
||||
s.log.WithField("username", login.Username).Debug("Login")
|
||||
|
||||
var hvDetails *proton.APIHVDetails
|
||||
if login.UseHvDetails != nil && *login.UseHvDetails {
|
||||
hvDetails = s.hvDetails
|
||||
s.useHvDetails = true
|
||||
} else {
|
||||
s.useHvDetails = false
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer async.HandlePanic(s.panicHandler)
|
||||
|
||||
@ -407,7 +415,7 @@ func (s *Service) Login(_ context.Context, login *LoginRequest) (*emptypb.Empty,
|
||||
return
|
||||
}
|
||||
|
||||
client, auth, err := s.bridge.LoginAuth(context.Background(), login.Username, password)
|
||||
client, auth, err := s.bridge.LoginAuth(context.Background(), login.Username, password, hvDetails)
|
||||
if err != nil {
|
||||
defer s.loginClean()
|
||||
|
||||
@ -421,6 +429,13 @@ func (s *Service) Login(_ context.Context, login *LoginRequest) (*emptypb.Empty,
|
||||
case proton.PaidPlanRequired:
|
||||
_ = s.SendEvent(NewLoginError(LoginErrorType_FREE_USER, ""))
|
||||
|
||||
case proton.HumanVerificationRequired:
|
||||
s.handleHvRequest(apiErr)
|
||||
|
||||
case proton.HumanValidationInvalidToken:
|
||||
s.hvDetails = nil
|
||||
_ = s.SendEvent(NewLoginError(LoginErrorType_HV_ERROR, err.Error()))
|
||||
|
||||
default:
|
||||
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, err.Error()))
|
||||
}
|
||||
@ -522,7 +537,6 @@ func (s *Service) LoginAbort(_ context.Context, loginAbort *LoginAbortRequest) (
|
||||
|
||||
go func() {
|
||||
defer async.HandlePanic(s.panicHandler)
|
||||
|
||||
s.loginAbort()
|
||||
}()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user