diff --git a/android/app/src/main/kotlin/io/lantern/model/SessionModel.kt b/android/app/src/main/kotlin/io/lantern/model/SessionModel.kt index ca8d64fd6..bfb8acff2 100644 --- a/android/app/src/main/kotlin/io/lantern/model/SessionModel.kt +++ b/android/app/src/main/kotlin/io/lantern/model/SessionModel.kt @@ -205,7 +205,14 @@ class SessionModel( } else { throw IllegalArgumentException("No URL provided for webview") } + } + + "login" -> { + Internalsdk.login(call.argument("email")!!, url = call.argument("password")!!) + } + "signOut" -> { + Internalsdk.logOut() } "trackUserAction" -> { diff --git a/go.mod b/go.mod index 49f502345..e760d6f28 100644 --- a/go.mod +++ b/go.mod @@ -36,14 +36,13 @@ require ( github.com/getlantern/eventual v1.0.0 github.com/getlantern/eventual/v2 v2.0.2 github.com/getlantern/filepersist v0.0.0-20210901195658-ed29a1cb0b7c - github.com/getlantern/flashlight/v7 v7.6.87 + github.com/getlantern/flashlight/v7 v7.6.90 github.com/getlantern/fronted v0.0.0-20230601004823-7fec719639d8 github.com/getlantern/golog v0.0.0-20230503153817-8e72de7e0a65 github.com/getlantern/i18n v0.0.0-20181205222232-2afc4f49bb1c github.com/getlantern/idletiming v0.0.0-20231030193830-6767b09f86db github.com/getlantern/ipproxy v0.0.0-20240305190756-6b5b6347158b github.com/getlantern/jibber_jabber v0.0.0-20210901195950-68955124cc42 - github.com/getlantern/lantern-client v0.0.0-20240529061800-bef437cdab3e github.com/getlantern/launcher v0.0.0-20230622120034-fe87f9bff286 github.com/getlantern/memhelper v0.0.0-20220104170102-df557102babd github.com/getlantern/mtime v0.0.0-20200417132445-23682092d1f7 diff --git a/go.sum b/go.sum index 130bd8429..9df9378c0 100644 --- a/go.sum +++ b/go.sum @@ -284,8 +284,11 @@ github.com/getlantern/fdcount v0.0.0-20210503151800-5decd65b3731/go.mod h1:XZwE+ github.com/getlantern/filepersist v0.0.0-20160317154340-c5f0cd24e799/go.mod h1:8DGAx0LNUfXNnEH+fXI0s3OCBA/351kZCiz/8YSK3i8= github.com/getlantern/filepersist v0.0.0-20210901195658-ed29a1cb0b7c h1:mcz27xtAkb1OuOLBct/uFfL1p3XxAIcFct82GbT+UZM= github.com/getlantern/filepersist v0.0.0-20210901195658-ed29a1cb0b7c/go.mod h1:8DGAx0LNUfXNnEH+fXI0s3OCBA/351kZCiz/8YSK3i8= +github.com/getlantern/flashlight v0.0.0-20230403092335-5f84ae10c585 h1:lVu4HyDAxXkX4kJEJQWpJJz+Poprb0crUGW5zhoh3GU= github.com/getlantern/flashlight/v7 v7.6.87 h1:IRwthjTQQS2xcLHHKaIsloKKxg11DODPSiIa9bm88HU= github.com/getlantern/flashlight/v7 v7.6.87/go.mod h1:A+/KlrUCdU+nO7S4yOtsmfcNb7xec4nYk6uXRdwFnew= +github.com/getlantern/flashlight/v7 v7.6.90 h1:6ji9BMyR5WTYY1IsbFULtCvEcDagnDn8BiNvAHatD+o= +github.com/getlantern/flashlight/v7 v7.6.90/go.mod h1:A+/KlrUCdU+nO7S4yOtsmfcNb7xec4nYk6uXRdwFnew= github.com/getlantern/framed v0.0.0-20190601192238-ceb6431eeede h1:yrU6Px3ZkvCsDLPryPGi6FN+2iqFPq+JeCb7EFoDBhw= github.com/getlantern/framed v0.0.0-20190601192238-ceb6431eeede/go.mod h1:nhnoiS6DE6zfe+BaCMU4YI01UpsuiXnDqM5S8jxHuuI= github.com/getlantern/fronted v0.0.0-20230601004823-7fec719639d8 h1:r/Z/SPPIfLXDI3QA7/tE6nOfPncrqeUPDjiFjnNGP50= diff --git a/internalsdk/android.go b/internalsdk/android.go index 9b3eec2e1..5256ceab8 100644 --- a/internalsdk/android.go +++ b/internalsdk/android.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "net" + "net/http" "os" "path/filepath" "sync" @@ -25,9 +26,12 @@ import ( "github.com/getlantern/flashlight/v7/geolookup" "github.com/getlantern/flashlight/v7/logging" "github.com/getlantern/flashlight/v7/ops" + "github.com/getlantern/flashlight/v7/proxied" "github.com/getlantern/golog" "github.com/getlantern/lantern-client/internalsdk/analytics" + "github.com/getlantern/lantern-client/internalsdk/auth" "github.com/getlantern/lantern-client/internalsdk/common" + "github.com/getlantern/lantern-client/internalsdk/webclient" "github.com/getlantern/mtime" // import gomobile just to make sure it stays in go.mod @@ -49,6 +53,14 @@ var ( dnsGrabEventual = eventual.NewValue() dnsGrabAddrEventual = eventual.NewValue() errNoAdProviderAvailable = errors.New("no ad provider available") + dialTimeout = 30 * time.Second + authClient = auth.NewClient(fmt.Sprintf("https://%s", common.V1BaseUrl), &webclient.Opts{ + HttpClient: &http.Client{ + Transport: proxied.Fronted(dialTimeout), + Timeout: dialTimeout, + }, + }) + userConfig *UserConfig ) type Settings interface { @@ -81,7 +93,7 @@ type Session interface { GetDNSServer() (string, error) Provider() (string, error) IsStoreVersion() (bool, error) - Email() (string, error) + GetEmail() (string, error) Currency() (string, error) DeviceOS() (string, error) IsProUser() (bool, error) @@ -118,11 +130,11 @@ type PanickingSession interface { GetTimeZone() string Code() string GetCountryCode() string + GetEmail() string GetForcedCountryCode() string GetDNSServer() string Provider() string IsStoreVersion() bool - Email() string Currency() string DeviceOS() string IsProUser() bool @@ -158,6 +170,12 @@ func (s *panickingSessionImpl) GetAppName() string { return s.wrapped.GetAppName() } +func (s *panickingSessionImpl) GetEmail() string { + result, err := s.wrapped.GetEmail() + panicIfNecessary(err) + return result +} + func (s *panickingSessionImpl) GetDeviceID() string { result, err := s.wrapped.GetDeviceID() panicIfNecessary(err) @@ -254,12 +272,6 @@ func (s *panickingSessionImpl) IsStoreVersion() bool { return result } -func (s *panickingSessionImpl) Email() string { - result, err := s.wrapped.Email() - panicIfNecessary(err) - return result -} - func (s *panickingSessionImpl) Currency() string { result, err := s.wrapped.Currency() panicIfNecessary(err) @@ -310,6 +322,7 @@ type UserConfig struct { func (uc *UserConfig) GetAppName() string { return common.DefaultAppName } func (uc *UserConfig) GetDeviceID() string { return uc.session.GetDeviceID() } +func (uc *UserConfig) GetEmail() string { return uc.session.GetEmail() } func (uc *UserConfig) GetUserID() int64 { return uc.session.GetUserID() } func (uc *UserConfig) GetToken() string { return uc.session.GetToken() } func (uc *UserConfig) GetEnabledExperiments() []string { return nil } @@ -513,6 +526,16 @@ func ReverseDns(grabber dnsgrab.Server) func(string) (string, error) { } } +func logIn(email, password string) { + log.Debug("Received sign in request") + authClient.Login(userConfig, email, password) +} + +func logOut() { + log.Debug("Received sign out request") + authClient.SignOut(context.Background(), userConfig) +} + func run(configDir, locale string, settings Settings, session PanickingSession) { appdir.SetHomeDir(configDir) @@ -554,7 +577,8 @@ func run(configDir, locale string, settings Settings, session PanickingSession) config.ForceCountry(forcedCountryCode) } - userConfig := NewUserConfig(session) + userConfig = NewUserConfig(session) + globalConfigChanged := make(chan interface{}) geoRefreshed := geolookup.OnRefresh() diff --git a/internalsdk/auth/auth.go b/internalsdk/auth/auth.go index 46348b6ec..bcb3086e0 100644 --- a/internalsdk/auth/auth.go +++ b/internalsdk/auth/auth.go @@ -23,14 +23,14 @@ type authClient struct { type AuthClient interface { //Sign up methods - SignUp(ctx context.Context, signupData *protos.SignupRequest) (bool, error) + SignUp(email string, password string) ([]byte, error) SignupEmailResendCode(ctx context.Context, data *protos.SignupEmailResendRequest) (bool, error) SignupEmailConfirmation(ctx context.Context, data *protos.ConfirmSignupRequest) (bool, error) //Login methods GetSalt(ctx context.Context, email string) (*protos.GetSaltResponse, error) LoginPrepare(ctx context.Context, loginData *protos.PrepareRequest) (*protos.PrepareResponse, error) - Login(ctx context.Context, loginData *protos.LoginRequest) (*protos.LoginResponse, error) + Login(uc common.UserConfig, email string, password string) (*protos.LoginResponse, []byte, error) // Recovery methods StartRecoveryByEmail(ctx context.Context, loginData *protos.StartRecoveryByEmailRequest) (bool, error) CompleteRecoveryByEmail(ctx context.Context, loginData *protos.CompleteRecoveryByEmailRequest) (bool, error) @@ -42,7 +42,7 @@ type AuthClient interface { DeleteAccount(ctc context.Context, loginData *protos.DeleteUserRequest) (bool, error) //Logout - SignOut(ctx context.Context, logoutData *protos.LogoutRequest) (bool, error) + SignOut(ctx context.Context, uc common.UserConfig) (bool, error) } // NewClient creates a new instance of AuthClient @@ -80,8 +80,8 @@ func (c *authClient) GetSalt(ctx context.Context, email string) (*protos.GetSalt } // Sign up API -// SignUp is used to sign up a new user with the SignupRequest -func (c *authClient) SignUp(ctx context.Context, signupData *protos.SignupRequest) (bool, error) { +// signUp is used to sign up a new user with the SignupRequest +func (c *authClient) signUp(ctx context.Context, signupData *protos.SignupRequest) (bool, error) { var resp protos.EmptyResponse err := c.webclient.PostPROTOC(ctx, "/users/signup", nil, signupData, &resp) if err != nil { @@ -124,7 +124,7 @@ func (c *authClient) LoginPrepare(ctx context.Context, loginData *protos.Prepare } // Login is used to login a user with the LoginRequest -func (c *authClient) Login(ctx context.Context, loginData *protos.LoginRequest) (*protos.LoginResponse, error) { +func (c *authClient) login(ctx context.Context, loginData *protos.LoginRequest) (*protos.LoginResponse, error) { var resp protos.LoginResponse err := c.webclient.PostPROTOC(ctx, "/users/login", nil, loginData, &resp) if err != nil { @@ -201,11 +201,19 @@ func (c *authClient) DeleteAccount(ctx context.Context, accountData *protos.Dele // DeleteAccount is used to delete the account of a user // Once account is delete make sure to create new account -func (c *authClient) SignOut(ctx context.Context, logoutData *protos.LogoutRequest) (bool, error) { - var resp protos.EmptyResponse - err := c.webclient.PostPROTOC(ctx, "/users/logout", nil, logoutData, &resp) - if err != nil { - return false, err +func (c *authClient) SignOut(ctx context.Context, uc common.UserConfig) (bool, error) { + email := uc.GetEmail() + deviceId := uc.GetDeviceID() + token := uc.GetToken() + userId := uc.GetUserID() + signoutData := &protos.LogoutRequest{ + Email: email, + DeviceId: deviceId, + LegacyToken: token, + LegacyUserID: userId, } - return true, nil + log.Debugf("Sign out request %+v", signoutData) + var resp protos.EmptyResponse + err := c.webclient.PostPROTOC(ctx, "/users/logout", nil, signoutData, &resp) + return err == nil, err } diff --git a/internalsdk/common/user_config.go b/internalsdk/common/user_config.go index 276f2110b..24f582559 100644 --- a/internalsdk/common/user_config.go +++ b/internalsdk/common/user_config.go @@ -16,6 +16,7 @@ type AuthConfig interface { type UserConfig interface { AuthConfig + GetEmail() string GetLanguage() string GetTimeZone() (string, error) GetInternalHeaders() map[string]string @@ -26,6 +27,7 @@ type UserConfig interface { type UserConfigData struct { AppName string DeviceID string + Email string UserID int64 Token string Language string @@ -34,6 +36,7 @@ type UserConfigData struct { func (uc *UserConfigData) GetAppName() string { return uc.AppName } func (uc *UserConfigData) GetDeviceID() string { return uc.DeviceID } +func (uc *UserConfigData) GetEmail() string { return uc.Email } func (uc *UserConfigData) GetUserID() int64 { return uc.UserID } func (uc *UserConfigData) GetToken() string { return uc.Token } func (uc *UserConfigData) GetLanguage() string { return uc.Language } diff --git a/internalsdk/session_model.go b/internalsdk/session_model.go index 74c8d5b39..2971c1cea 100644 --- a/internalsdk/session_model.go +++ b/internalsdk/session_model.go @@ -138,22 +138,10 @@ func NewSessionModel(mdb minisql.DB, opts *SessionModelOpts) (*SessionModel, err Timeout: dialTimeout, }, UserConfig: func() common.UserConfig { - deviceID, _ := m.GetDeviceID() - userID, _ := m.GetUserID() - token, _ := m.GetToken() - lang, _ := m.Locale() - internalHeaders := map[string]string{ + return sessionToUserConfig(m, map[string]string{ common.PlatformHeader: opts.Platform, common.AppVersionHeader: common.ApplicationVersion, - } - return common.NewUserConfig( - common.DefaultAppName, - deviceID, - userID, - token, - internalHeaders, - lang, - ) + }) }, } m.proClient = pro.NewClient(fmt.Sprintf("https://%s", common.ProAPIHost), webclientOpts) @@ -164,6 +152,21 @@ func NewSessionModel(mdb minisql.DB, opts *SessionModelOpts) (*SessionModel, err return m, nil } +func sessionToUserConfig(m *SessionModel, internalHeaders map[string]string) common.UserConfig { + deviceID, _ := m.GetDeviceID() + userID, _ := m.GetUserID() + token, _ := m.GetToken() + lang, _ := m.Locale() + return common.NewUserConfig( + common.DefaultAppName, + deviceID, + userID, + token, + internalHeaders, + lang, + ) +} + func (m *SessionModel) doInvokeMethod(method string, arguments Arguments) (interface{}, error) { switch method { case "getBandwidth": @@ -341,7 +344,7 @@ func (m *SessionModel) doInvokeMethod(method string, arguments Arguments) (inter } return true, nil case "signOut": - err := signOut(*m) + err := signOut(m) if err != nil { return nil, err } @@ -797,7 +800,7 @@ func (m *SessionModel) IsStoreVersion() (bool, error) { return pathdb.Get[bool](m.db, pathStoreVersion) } -func (m *SessionModel) Email() (string, error) { +func (m *SessionModel) GetEmail() (string, error) { return pathdb.Get[string](m.db, pathEmailAddress) } @@ -1208,32 +1211,10 @@ func submitApplePayPayment(m *SessionModel, email string, planId string, purchas // Then use srpClient.Verifier() to generate verifierKey func signup(session *SessionModel, email string, password string) error { lowerCaseEmail := strings.ToLower(email) - err := setEmail(session.baseModel, lowerCaseEmail) - if err != nil { - return err - } - salt, err := GenerateSalt() - if err != nil { - return err - } - - srpClient := srp.NewSRPClient(srp.KnownGroups[group], GenerateEncryptedKey(password, lowerCaseEmail, salt), nil) - verifierKey, err := srpClient.Verifier() - if err != nil { - return err - } - signUpRequestBody := &protos.SignupRequest{ - Email: lowerCaseEmail, - Salt: salt, - Verifier: verifierKey.Bytes(), - SkipEmailConfirmation: true, - } - log.Debugf("Sign up request email %v, salt %v verifier %v verifiter in bytes %v", lowerCaseEmail, salt, verifierKey, verifierKey.Bytes()) - signupResponse, err := session.authClient.SignUp(context.Background(), signUpRequestBody) + salt, err := session.authClient.SignUp(lowerCaseEmail, password) if err != nil { return err } - log.Debugf("sign up response %v", signupResponse) //Request successfull then save salt err = pathdb.Mutate(session.db, func(tx pathdb.TX) error { return pathdb.PutAll(tx, map[string]interface{}{ @@ -1288,67 +1269,8 @@ func signupEmailConfirmation(session *SessionModel, email string, code string) e func login(session *SessionModel, email string, password string) error { lowerCaseEmail := strings.ToLower(email) start := time.Now() - // Get the salt - salt, err := getUserSalt(session, lowerCaseEmail) - if err != nil { - return err - } - - encryptedKey := GenerateEncryptedKey(password, lowerCaseEmail, salt) - log.Debugf("Encrypted key %v Login", encryptedKey) - // Prepare login request body - client := srp.NewSRPClient(srp.KnownGroups[group], encryptedKey, nil) - //Send this key to client - A := client.EphemeralPublic() - //Create body - prepareRequestBody := &protos.PrepareRequest{ - Email: lowerCaseEmail, - A: A.Bytes(), - } - srpB, err := session.authClient.LoginPrepare(context.Background(), prepareRequestBody) - if err != nil { - return err - } - log.Debugf("Login prepare response %v", srpB) - - // // Once the client receives B from the server Client should check error status here as defense against - // // a malicious B sent from server - B := big.NewInt(0).SetBytes(srpB.B) - - if err = client.SetOthersPublic(B); err != nil { - log.Errorf("Error while setting srpB %v", err) - return err - } - - // client can now make the session key - clientKey, err := client.Key() - if err != nil || clientKey == nil { - return log.Errorf("user_not_found error while generating Client key %v", err) - } - - // // Step 3 - - // // check if the server proof is valid - if !client.GoodServerProof(salt, lowerCaseEmail, srpB.Proof) { - return log.Errorf("user_not_found error while checking server proof%v", err) - } - - clientProof, err := client.ClientProof() - if err != nil { - return log.Errorf("user_not_found error while generating client proof %v", err) - } - deviceId, err := session.GetDeviceID() - if err != nil { - return err - } - loginRequestBody := &protos.LoginRequest{ - Email: lowerCaseEmail, - Proof: clientProof, - DeviceId: deviceId, - } - log.Debugf("Login request body %v", loginRequestBody) - - login, err := session.authClient.Login(context.Background(), loginRequestBody) + uc := NewUserConfig(NewPanickingSession(session)) + login, salt, err := session.authClient.Login(uc, lowerCaseEmail, password) if err != nil { return err } @@ -1445,13 +1367,13 @@ func startRecoveryByEmail(session *SessionModel, email string) error { func completeRecoveryByEmail(session *SessionModel, email string, code string, password string) error { //Create body lowerCaseEmail := strings.ToLower(email) - newsalt, err := GenerateSalt() + newsalt, err := auth.GenerateSalt() if err != nil { return err } log.Debugf("Slat %v and length %v", newsalt, len(newsalt)) - encryptedKey := GenerateEncryptedKey(password, lowerCaseEmail, newsalt) + encryptedKey := auth.GenerateEncryptedKey(password, lowerCaseEmail, newsalt) log.Debugf("Encrypted key %v completeRecoveryByEmail", encryptedKey) srpClient := srp.NewSRPClient(srp.KnownGroups[group], encryptedKey, nil) verifierKey, err := srpClient.Verifier() @@ -1511,7 +1433,7 @@ func startChangeEmail(session SessionModel, email string, newEmail string, passw } // Prepare login request body - client := srp.NewSRPClient(srp.KnownGroups[group], GenerateEncryptedKey(password, lowerCaseEmail, salt), nil) + client := srp.NewSRPClient(srp.KnownGroups[group], auth.GenerateEncryptedKey(password, lowerCaseEmail, salt), nil) //Send this key to client A := client.EphemeralPublic() @@ -1567,13 +1489,13 @@ func startChangeEmail(session SessionModel, email string, newEmail string, passw func completeChangeEmail(session SessionModel, email string, newEmail string, password string, code string) error { // Create new salt and verifier with new email and new slat - newsalt, err := GenerateSalt() + newsalt, err := auth.GenerateSalt() if err != nil { return err } log.Debugf("Slat %v and length %v", newsalt, len(newsalt)) - srpClient := srp.NewSRPClient(srp.KnownGroups[group], GenerateEncryptedKey(password, newEmail, newsalt), nil) + srpClient := srp.NewSRPClient(srp.KnownGroups[group], auth.GenerateEncryptedKey(password, newEmail, newsalt), nil) verifierKey, err := srpClient.Verifier() if err != nil { return err @@ -1596,37 +1518,9 @@ func completeChangeEmail(session SessionModel, email string, newEmail string, pa } // Clear slat and change accoutn state -func signOut(session SessionModel) error { - email, err := session.Email() - if err != nil { - return log.Errorf("Email not found %v", err) - } - - deviceId, err := session.GetDeviceID() - if err != nil { - return log.Errorf("deviceId not found %v", err) - } - - token, err := session.GetToken() - if err != nil { - return log.Errorf("token not found %v", err) - } - - userId, err := session.GetUserID() - if err != nil { - return log.Errorf("userid not found %v", err) - } - - signoutData := &protos.LogoutRequest{ - Email: email, - DeviceId: deviceId, - LegacyToken: token, - LegacyUserID: userId, - } - - log.Debugf("Sign out request %+v", signoutData) - - loggedOut, logoutErr := session.authClient.SignOut(context.Background(), signoutData) +func signOut(session *SessionModel) error { + uc := NewUserConfig(NewPanickingSession(session)) + loggedOut, logoutErr := session.authClient.SignOut(context.Background(), uc) if logoutErr != nil { return log.Errorf("Error while signing out %v", logoutErr) } @@ -1634,9 +1528,7 @@ func signOut(session SessionModel) error { if !loggedOut { return log.Errorf("Error while signing out %v", logoutErr) } - - err = clearLocalUserData(session) - if err != nil { + if err := clearLocalUserData(*session); err != nil { return log.Errorf("Error while clearing local data %v", err) } return session.userCreate(context.Background()) @@ -1663,7 +1555,7 @@ func clearLocalUserData(session SessionModel) error { } func deleteAccount(session SessionModel, password string) error { - email, err := session.Email() + email, err := session.GetEmail() if err != nil { return err } @@ -1678,7 +1570,7 @@ func deleteAccount(session SessionModel, password string) error { } // Prepare login request body - client := srp.NewSRPClient(srp.KnownGroups[group], GenerateEncryptedKey(password, lowerCaseEmail, salt), nil) + client := srp.NewSRPClient(srp.KnownGroups[group], auth.GenerateEncryptedKey(password, lowerCaseEmail, salt), nil) //Send this key to client A := client.EphemeralPublic() diff --git a/internalsdk/utils.go b/internalsdk/utils.go index c0f9fef60..c4e6261b9 100644 --- a/internalsdk/utils.go +++ b/internalsdk/utils.go @@ -2,9 +2,7 @@ package internalsdk import ( "bytes" - "crypto/rand" cryptoRand "crypto/rand" - "crypto/sha256" "encoding/binary" "fmt" "math" @@ -16,7 +14,6 @@ import ( "github.com/getlantern/errors" "github.com/getlantern/lantern-client/internalsdk/protos" "github.com/getlantern/pathdb" - "golang.org/x/crypto/pbkdf2" ) func BytesToFloat64LittleEndian(b []byte) (float64, error) { @@ -102,15 +99,6 @@ func GenerateRandomString(length int) string { } return random } -func GenerateSalt() ([]byte, error) { - salt := make([]byte, 16) - if n, err := rand.Read(salt); err != nil { - return nil, err - } else if n != 16 { - return nil, errors.New("failed to generate 16 byte salt") - } - return salt, nil -} func ToString(value int64) string { return fmt.Sprintf("%d", value) @@ -165,12 +153,3 @@ func ConvertToUserDetailsResponse(userResponse *protos.LoginResponse) *protos.Us return &userData } - -// Takes password and email, salt and returns encrypted key -func GenerateEncryptedKey(password string, email string, salt []byte) *big.Int { - lowerCaseEmail := strings.ToLower(email) - combinedInput := password + lowerCaseEmail - encryptedKey := pbkdf2.Key([]byte(combinedInput), salt, 4096, 32, sha256.New) - encryptedKeyBigInt := big.NewInt(0).SetBytes(encryptedKey) - return encryptedKeyBigInt -} diff --git a/lib/account/account.dart b/lib/account/account.dart index 4b8226fa0..893d8f728 100644 --- a/lib/account/account.dart +++ b/lib/account/account.dart @@ -37,7 +37,7 @@ class _AccountMenuState extends State { } } - void showSingOutDialog(BuildContext context) { + void showSignOutDialog(BuildContext context) { CDialog( title: 'sign_out'.i18n, description: "sign_out_message".i18n, @@ -213,12 +213,12 @@ class _AccountMenuState extends State { openSettings(context); }, ), - if (Platform.isIOS) + if (Platform.isMobile) if (hasUserLoggedIn) ListItemFactory.settingsItem( icon: ImagePaths.signOut, content: 'sign_out'.i18n, - onTap: () => showSingOutDialog(context), + onTap: () => showSignOutDialog(context), ) ]; }