1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-05 23:53:33 +00:00

Compare commits

...

6 Commits

Author SHA1 Message Date
github-actions[bot]
08d07603a8 Bumped version to 2023.3.1 (#2432)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-03-23 18:10:10 +01:00
mp-bw
b9bb21b1db Fix migration crash (#2430) 2023-03-23 11:48:43 -04:00
github-actions[bot]
39d275453e Bumped version to 2023.3.0 (#2423)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
(cherry picked from commit c6bdb67981)
2023-03-22 10:45:56 -04:00
André Bispo
e23ab78bd8 [PM-1497] Known device api error (#2418)
* [PM-1497] Ignore know device api error.
2023-03-17 15:08:29 +00:00
André Bispo
4ed5980cb5 [PM-1431] [Defect] [Android] New accounts are not able to log in (#2417)
* [PM-1431] Do not clear Master password if login is ongoing.
2023-03-16 12:50:26 +00:00
André Bispo
8c273f7d93 [PM-1078] Login with Device - Change mobile to not get fingerprint from API (#2390)
* [PM-1078] Fingerprint phrase gets calculated from pub key on AuthService instead of coming as a property from the api.

(cherry picked from commit ccd71202de)
2023-03-13 18:27:59 -04:00
13 changed files with 61 additions and 21 deletions

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2023.2.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2023.3.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" /> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.NFC" /> <uses-permission android:name="android.permission.NFC" />

View File

@@ -217,7 +217,7 @@ namespace Bit.App
Id = loginRequestData.Id, Id = loginRequestData.Id,
IpAddress = loginRequestData.RequestIpAddress, IpAddress = loginRequestData.RequestIpAddress,
Email = await _stateService.GetEmailAsync(), Email = await _stateService.GetEmailAsync(),
FingerprintPhrase = loginRequestData.RequestFingerprint, FingerprintPhrase = loginRequestData.FingerprintPhrase,
RequestDate = loginRequestData.CreationDate, RequestDate = loginRequestData.CreationDate,
DeviceType = loginRequestData.RequestDeviceType, DeviceType = loginRequestData.RequestDeviceType,
Origin = loginRequestData.Origin Origin = loginRequestData.Origin

View File

@@ -40,6 +40,7 @@ namespace Bit.App.Pages
private string _masterPassword; private string _masterPassword;
private bool _isEmailEnabled; private bool _isEmailEnabled;
private bool _isKnownDevice; private bool _isKnownDevice;
private bool _isExecutingLogin;
public LoginPageViewModel() public LoginPageViewModel()
{ {
@@ -149,15 +150,21 @@ namespace Bit.App.Pages
{ {
Email = await _stateService.GetRememberedEmailAsync(); Email = await _stateService.GetRememberedEmailAsync();
} }
var deviceIdentifier = await _appIdService.GetAppIdAsync();
IsKnownDevice = await _apiService.GetKnownDeviceAsync(Email, deviceIdentifier);
CanRemoveAccount = await _stateService.GetActiveUserEmailAsync() != Email; CanRemoveAccount = await _stateService.GetActiveUserEmailAsync() != Email;
await _deviceActionService.HideLoadingAsync(); IsKnownDevice = await _apiService.GetKnownDeviceAsync(Email, await _appIdService.GetAppIdAsync());
}
catch (ApiException apiEx) when (apiEx.Error.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
_logger.Exception(apiEx);
} }
catch (Exception ex) catch (Exception ex)
{ {
HandleException(ex); HandleException(ex);
} }
finally
{
await _deviceActionService.HideLoadingAsync();
}
} }
public async Task LogInAsync(bool showLoading = true, bool checkForExistingAccount = false) public async Task LogInAsync(bool showLoading = true, bool checkForExistingAccount = false)
@@ -192,6 +199,7 @@ namespace Bit.App.Pages
ShowPassword = false; ShowPassword = false;
try try
{ {
_isExecutingLogin = true;
if (checkForExistingAccount) if (checkForExistingAccount)
{ {
var userId = await _stateService.GetUserIdAsync(Email); var userId = await _stateService.GetUserIdAsync(Email);
@@ -253,14 +261,21 @@ namespace Bit.App.Pages
AppResources.AnErrorHasOccurred, AppResources.Ok); AppResources.AnErrorHasOccurred, AppResources.Ok);
} }
} }
finally
{
_isExecutingLogin = false;
}
} }
public void ResetPasswordField() public void ResetPasswordField()
{ {
try try
{ {
MasterPassword = string.Empty; if (!_isExecutingLogin)
ShowPassword = false; {
MasterPassword = string.Empty;
ShowPassword = false;
}
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -171,7 +171,7 @@ namespace Bit.App.Pages
var response = await _authService.PasswordlessCreateLoginRequestAsync(_email); var response = await _authService.PasswordlessCreateLoginRequestAsync(_email);
if (response != null) if (response != null)
{ {
FingerprintPhrase = response.RequestFingerprint; FingerprintPhrase = response.FingerprintPhrase;
_requestId = response.Id; _requestId = response.Id;
_requestAccessCode = response.RequestAccessCode; _requestAccessCode = response.RequestAccessCode;
_requestKeyPair = response.RequestKeyPair; _requestKeyPair = response.RequestKeyPair;

View File

@@ -39,7 +39,7 @@
Padding="0, 10, 0 ,0" Padding="0, 10, 0 ,0"
FontAttributes="Bold"/> FontAttributes="Bold"/>
<controls:MonoLabel <controls:MonoLabel
FormattedText="{Binding RequestFingerprint}" FormattedText="{Binding FingerprintPhrase}"
Grid.Row="1" Grid.Row="1"
Grid.ColumnSpan="2" Grid.ColumnSpan="2"
FontSize="Small" FontSize="Small"

View File

@@ -99,7 +99,7 @@ namespace Bit.App.Pages
Id = loginRequestData.Id, Id = loginRequestData.Id,
IpAddress = loginRequestData.RequestIpAddress, IpAddress = loginRequestData.RequestIpAddress,
Email = await _stateService.GetEmailAsync(), Email = await _stateService.GetEmailAsync(),
FingerprintPhrase = loginRequestData.RequestFingerprint, FingerprintPhrase = loginRequestData.FingerprintPhrase,
RequestDate = loginRequestData.CreationDate, RequestDate = loginRequestData.CreationDate,
DeviceType = loginRequestData.RequestDeviceType, DeviceType = loginRequestData.RequestDeviceType,
Origin = loginRequestData.Origin Origin = loginRequestData.Origin

View File

@@ -11,7 +11,7 @@ namespace Bit.Core.Models.Response
public string PublicKey { get; set; } public string PublicKey { get; set; }
public string RequestDeviceType { get; set; } public string RequestDeviceType { get; set; }
public string RequestIpAddress { get; set; } public string RequestIpAddress { get; set; }
public string RequestFingerprint { get; set; } public string FingerprintPhrase { get; set; }
public string Key { get; set; } public string Key { get; set; }
public string MasterPasswordHash { get; set; } public string MasterPasswordHash { get; set; }
public DateTime CreationDate { get; set; } public DateTime CreationDate { get; set; }

View File

@@ -494,18 +494,21 @@ namespace Bit.Core.Services
public async Task<List<PasswordlessLoginResponse>> GetPasswordlessLoginRequestsAsync() public async Task<List<PasswordlessLoginResponse>> GetPasswordlessLoginRequestsAsync()
{ {
return await _apiService.GetAuthRequestAsync(); var response = await _apiService.GetAuthRequestAsync();
return await PopulateFingerprintPhrasesAsync(response);
} }
public async Task<List<PasswordlessLoginResponse>> GetActivePasswordlessLoginRequestsAsync() public async Task<List<PasswordlessLoginResponse>> GetActivePasswordlessLoginRequestsAsync()
{ {
var requests = await GetPasswordlessLoginRequestsAsync(); var requests = await GetPasswordlessLoginRequestsAsync();
return requests.Where(r => !r.IsAnswered && !r.IsExpired).OrderByDescending(r => r.CreationDate).ToList(); var activeRequests = requests.Where(r => !r.IsAnswered && !r.IsExpired).OrderByDescending(r => r.CreationDate).ToList();
return await PopulateFingerprintPhrasesAsync(activeRequests);
} }
public async Task<PasswordlessLoginResponse> GetPasswordlessLoginRequestByIdAsync(string id) public async Task<PasswordlessLoginResponse> GetPasswordlessLoginRequestByIdAsync(string id)
{ {
return await _apiService.GetAuthRequestAsync(id); var response = await _apiService.GetAuthRequestAsync(id);
return await PopulateFingerprintPhraseAsync(response, await _stateService.GetEmailAsync());
} }
public async Task<PasswordlessLoginResponse> GetPasswordlessLoginResponseAsync(string id, string accessCode) public async Task<PasswordlessLoginResponse> GetPasswordlessLoginResponseAsync(string id, string accessCode)
@@ -520,7 +523,8 @@ namespace Bit.Core.Services
var encryptedKey = await _cryptoService.RsaEncryptAsync(masterKey.EncKey, publicKey); var encryptedKey = await _cryptoService.RsaEncryptAsync(masterKey.EncKey, publicKey);
var encryptedMasterPassword = await _cryptoService.RsaEncryptAsync(Encoding.UTF8.GetBytes(await _stateService.GetKeyHashAsync()), publicKey); var encryptedMasterPassword = await _cryptoService.RsaEncryptAsync(Encoding.UTF8.GetBytes(await _stateService.GetKeyHashAsync()), publicKey);
var deviceId = await _appIdService.GetAppIdAsync(); var deviceId = await _appIdService.GetAppIdAsync();
return await _apiService.PutAuthRequestAsync(id, encryptedKey.EncryptedString, encryptedMasterPassword.EncryptedString, deviceId, requestApproved); var response = await _apiService.PutAuthRequestAsync(id, encryptedKey.EncryptedString, encryptedMasterPassword.EncryptedString, deviceId, requestApproved);
return await PopulateFingerprintPhraseAsync(response, await _stateService.GetEmailAsync());
} }
public async Task<PasswordlessLoginResponse> PasswordlessCreateLoginRequestAsync(string email) public async Task<PasswordlessLoginResponse> PasswordlessCreateLoginRequestAsync(string email)
@@ -538,9 +542,30 @@ namespace Bit.Core.Services
{ {
response.RequestKeyPair = keyPair; response.RequestKeyPair = keyPair;
response.RequestAccessCode = accessCode; response.RequestAccessCode = accessCode;
response.FingerprintPhrase = fingerprintPhrase;
} }
return response; return response;
} }
private async Task<List<PasswordlessLoginResponse>> PopulateFingerprintPhrasesAsync(List<PasswordlessLoginResponse> passwordlessLoginList)
{
if (passwordlessLoginList == null)
{
return null;
}
var userEmail = await _stateService.GetEmailAsync();
foreach (var passwordlessLogin in passwordlessLoginList)
{
await PopulateFingerprintPhraseAsync(passwordlessLogin, userEmail);
}
return passwordlessLoginList;
}
private async Task<PasswordlessLoginResponse> PopulateFingerprintPhraseAsync(PasswordlessLoginResponse passwordlessLogin, string userEmail)
{
passwordlessLogin.FingerprintPhrase = string.Join("-", await _cryptoService.GetFingerprintAsync(userEmail, CoreHelpers.Base64UrlDecode(passwordlessLogin.PublicKey)));
return passwordlessLogin;
}
} }
} }

View File

@@ -394,10 +394,10 @@ namespace Bit.Core.Services
// use values from first userId to apply globals // use values from first userId to apply globals
if (firstUserId != null) if (firstUserId != null)
{ {
var theme = await GetValueAsync<int?>(Storage.LiteDb, V3Keys.ThemeKey(firstUserId)); var theme = await GetValueAsync<string>(Storage.LiteDb, V3Keys.ThemeKey(firstUserId));
await SetValueAsync(Storage.LiteDb, V4Keys.ThemeKey, theme); await SetValueAsync(Storage.LiteDb, V4Keys.ThemeKey, theme);
var autoDarkTheme = await GetValueAsync<int?>(Storage.LiteDb, V3Keys.AutoDarkThemeKey(firstUserId)); var autoDarkTheme = await GetValueAsync<string>(Storage.LiteDb, V3Keys.AutoDarkThemeKey(firstUserId));
await SetValueAsync(Storage.LiteDb, V4Keys.AutoDarkThemeKey, autoDarkTheme); await SetValueAsync(Storage.LiteDb, V4Keys.AutoDarkThemeKey, autoDarkTheme);
var disableFavicon = await GetValueAsync<bool?>(Storage.LiteDb, V3Keys.DisableFaviconKey(firstUserId)); var disableFavicon = await GetValueAsync<bool?>(Storage.LiteDb, V3Keys.DisableFaviconKey(firstUserId));

View File

@@ -11,7 +11,7 @@
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>com.8bit.bitwarden.autofill</string> <string>com.8bit.bitwarden.autofill</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>2023.2.1</string> <string>2023.3.1</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>1</string>
<key>CFBundleLocalizations</key> <key>CFBundleLocalizations</key>

View File

@@ -11,7 +11,7 @@
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>com.8bit.bitwarden.find-login-action-extension</string> <string>com.8bit.bitwarden.find-login-action-extension</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>2023.2.1</string> <string>2023.3.1</string>
<key>CFBundleLocalizations</key> <key>CFBundleLocalizations</key>
<array> <array>
<string>en</string> <string>en</string>

View File

@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>XPC!</string> <string>XPC!</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>2023.2.1</string> <string>2023.3.1</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>1</string>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>

View File

@@ -11,7 +11,7 @@
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>com.8bit.bitwarden</string> <string>com.8bit.bitwarden</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>2023.2.1</string> <string>2023.3.1</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>1</string>
<key>CFBundleIconName</key> <key>CFBundleIconName</key>