1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-10 21:33:36 +00:00

Compare commits

..

92 Commits

Author SHA1 Message Date
Jacob Fink
a9368be106 Merge commit 'c34d1da6' into temp/tde-master-merge 2023-08-13 15:36:25 -04:00
André Bispo
b2df06a7a1 [PM-3379] Fix key rotation on trusted device. (#2680) 2023-08-11 19:10:11 +01:00
André Bispo
3f4892fcc8 [PM-3381] Fix TDE login 2FA flow (#2678)
* [PM-3381] Check for vault lock on 2FA screen

* [PM-3381] Move logic to ViewModel

* [PM-3381] Fix null vm error
2023-08-11 17:32:51 +01:00
André Bispo
5f4cd62f66 [PM-3303] Fix biometric login after key migration (#2679)
* [PM-3303] Add condition to biometric unlock
2023-08-11 15:16:01 +01:00
André Bispo
9b5cde6a46 Fix app fresh install user login with master password. (#2676) 2023-08-10 20:10:02 +01:00
Jake Fink
62b6d21371 [PM-2713] Final merge from Key Migration branch to TDE Feature branch (#2667)
* [PM-2713] add async to key connector service methods

* [PM-2713] rename ephemeral pin key

* add state for biometric key and accept UserKey instead of string for auto key

* Get UserKey from bio state on unlock

* PM-2713 Fix auto-migrating EncKeyEncrypted into MasterKey encrypted UserKey when requesting DecryptUserKeyWithMasterKeyAsync is called

* renaming bio key and fix build

* PM-3194 Fix biometrics button to be shown on upgrade when no UserKey is present yet

* revert removal of key connector service from auth service

* PM-2713 set user key when using KC

* clear enc user key after migration

* use is true for nullable bool

* PR feedback, refactor kc service

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
2023-08-10 14:09:53 -04:00
Federico Maccaroni
9001fa1ccf [PM-3102] Update Master password reprompt to be based on MP instead of Key Connector (#2653)
* PM-3102 Added check to see if a user has master password set replacing previous usage of key connector.

* PM-3102 Fix formatting
2023-08-10 13:23:04 -03:00
André Bispo
af016cd13c [PM-3362] Fix auth request approval (#2675)
* [PM-3362] Fix auth request approval

* [PM-3362] Add new exception type
2023-08-10 16:48:03 +01:00
André Bispo
3b9a9fc049 [PM-3357] Fix copy for Login Initiated (#2674) 2023-08-09 14:41:55 +01:00
André Bispo
8205c0763c [PM-3341] Vault Timeout Action not persisted correctly (#2673)
* [PM-3341] Fix timeout action change when navigating
2023-08-08 20:16:07 +01:00
André Bispo
e9afc75f0a [PM-3333] Check for purged admin auth requests (#2671)
* [PM-3333] Check for purged admin auth requests

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
2023-08-08 20:15:35 +01:00
André Bispo
446b8c2d35 [PM-2289] Change header title based on auth request type (#2670)
* [PM-2289] Change header title based on auth request type
2023-08-08 20:15:14 +01:00
André Bispo
24378d937e [PM-3319] Check for admin request in Lock page (#2668)
* [PM-3319] Ignore admin auth request when choosing mp as decryption option.
2023-08-08 20:12:01 +01:00
André Bispo
6b2700bcaa [PM-3342] Not you button logs user out. (#2672) 2023-08-08 19:36:49 +01:00
André Bispo
aac66b23bd [PM-3337] Fix admin request deny error (#2669) 2023-08-08 14:15:38 -04:00
André Bispo
8221d55647 [PM-3320][PM-3321] Fix labels and UI tweaks (#2666)
* [PM-3320] Fix UI copy and remember me default ON.

* [PM-3321] Fix UI on Log in with device screen.
2023-08-07 15:16:52 +01:00
André Bispo
53e3429f6d [PM-3313] Null email in Approval Options screen (#2664)
* [PM-3313] Fix null email in approval options screen
2023-08-05 11:39:06 +01:00
André Bispo
faa9b1a9f7 [PM-3313] Fix Android SSO Login (#2663)
* [PM-3313] Catch exception on AuthPendingRequest

* [PM-3313] Fix lock timeout action if user doesn't have a master password.

* code format
2023-08-04 20:37:02 -04:00
Federico Maccaroni
c34d1da6e6 PM-3298 Updated region selector to be logging in on and also its options (#2657) 2023-08-04 11:44:39 -03:00
github-actions[bot]
c4e64e082b Autosync the updated translations (#2660)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2023-08-04 10:55:27 +00:00
Jacob Fink
4f62f5e5bc Fix build after merge 2023-08-03 22:03:28 -04:00
Jacob Fink
9bddc1d456 Merge branch 'auth/pm-2713/drop-master-key-dependency' into feature/pm-1029-tde-login 2023-08-03 22:01:38 -04:00
Jacob Fink
78004dbdb9 [PM-2713] clear service cache when adding new account 2023-08-03 16:28:04 -04:00
Jacob Fink
e820408a64 [PM-2713] make method async again
- returning null from a task thats not async throws
2023-08-03 15:44:41 -04:00
Jacob Fink
c595b1626e fix formatting 2023-08-03 14:34:50 -04:00
Jacob Fink
270a395d9f [PM-2713] set enc user key during kc onboarding 2023-08-03 13:41:19 -04:00
Jacob Fink
4fa8d2ba28 [PM-2713] set user key on set password page 2023-08-03 13:30:45 -04:00
André Bispo
c75bcccf20 [PM-1208] Add TDE flows for new users (#2655)
* [PM-1208] Create new user on SSO. Logout if not password is setup or has pending admin auth request.

* [PM-1208] Fix new user UserKey decryption.

* [PM-1208] Add new user continue to vault logic. Auto enrol user on continue.

* [PM-1208] Trust device only if needed

* [PM-1208] Add logic for New User SSO.

* [PM-1208] Add logic for New User SSO (missing file).
2023-08-03 18:20:51 +01:00
Jacob Fink
e076c9fe04 [PM-2713] add auto unlock key to mobile 2023-08-03 10:49:55 -04:00
André Bispo
78788276ef Merge branch 'master' into feature/pm-1029-tde-login 2023-08-03 14:50:15 +01:00
Federico Maccaroni
5aaff1ea20 PM-3249 Removed back button from block autofill uris to be aligned to other views (#2654) 2023-08-02 10:54:01 -03:00
Jacob Fink
ee0dcd23f5 rename account keys to be more descriptive 2023-08-01 20:08:41 -04:00
Jacob Fink
1e8ed1b5ce [PM-2713] replace generic with inherited class 2023-08-01 10:47:02 -04:00
Jacob Fink
7fb89fa1a5 [PM-2713] consolidate attachment key creation
- also fix ios files missed during symbol rename
2023-08-01 09:30:00 -04:00
Jacob Fink
b1eb263fef [PM-2713] combine makeDataEncKey methods 2023-08-01 08:54:19 -04:00
Jacob Fink
61aac20555 [PM-2713] rename state methods 2023-08-01 08:46:02 -04:00
Jacob Fink
3e87d74061 [PM-2713] revert feedback for build 2023-07-31 17:09:47 -04:00
Jacob Fink
89a9185b20 [PM-2713] rename get pin lock type method 2023-07-31 16:49:41 -04:00
Jacob Fink
e323e196c0 [PM-2713] PR feedback 2023-07-31 15:31:46 -04:00
Jacob Fink
c793260689 [PM-2713] pr feedback 2023-07-31 13:13:29 -04:00
Jacob Fink
c2ddbb7eff [PM-2713] rename toggle method, don't reset enc user key 2023-07-31 12:57:50 -04:00
Jacob Fink
bb5a7383a8 [PM-2713] don't pass user key as param when encrypting 2023-07-31 12:42:56 -04:00
André Bispo
6fe8fc39ab Merge branch 'master' into feature/pm-1029-tde-login
# Conflicts:
#	src/App/Resources/AppResources.resx
2023-07-28 07:33:54 +01:00
Jacob Fink
de5113ede7 [PM-2713] rename PinLockEnum to PinLockType 2023-07-27 16:26:23 -04:00
Jacob Fink
ba6d260565 [PM-2713] rename PrivateKey methods to UserPrivateKey on crypto service 2023-07-27 16:24:31 -04:00
Jacob Fink
7562c688c5 [PM-2713] deconstruct new key pair 2023-07-27 16:20:33 -04:00
André Bispo
c25906206e [PM-2289] [PM-2293] TDE Login with device Admin Request (#2642) 2023-07-27 21:18:36 +01:00
André Bispo
dfc7c55b77 [PM-2297] Login with trusted device (Flow 2) (#2623)
* [PM-2297] Add DecryptUserKeyWithDeviceKey method

* [PM-2297] Add methods to DeviceTrustCryptoService update decryption options model

* [PM-2297] Update account decryption options model

* [PM-2297] Fix TrustedDeviceOption and DeviceResponse model. Change StateService device key get set to have default user id

* [PM-2297] Update navigation to decryption options

* [PM-2297] Add missing action navigations to iOS extensions

* [PM-2297] Fix trust device bug/typo

* [PM-2297] Fix model bug

* [PM-2297] Fix state var crash

* [PM-2297] Add trust device login logic to auth service

* [PM-2297] Refactor auth service key connector code

* [PM-2297] Remove reconciledOptions for deviceKey in state service

* [PM-2297] Remove unnecessary user id params
2023-07-27 16:55:06 +01:00
Jacob Fink
10574a7117 [PM-2713] remove broken casting 2023-07-20 16:17:30 -04:00
Jacob Fink
a2f1ca583a [PM-2713] remove extra comment 2023-07-20 16:14:40 -04:00
Jacob Fink
813ac841c6 [PM-2713] fix casting issues and pin 2023-07-20 13:57:32 -04:00
Jacob Fink
0da3d25955 [PM-2713] rename password hash to master key hash 2023-07-20 12:30:02 -04:00
Jacob Fink
f8c9cde2ed [PM-2713] optimize async code in crypto service 2023-07-20 11:17:57 -04:00
André Bispo
080aabfe82 [PM-1208] Fix merge 2023-07-20 15:39:03 +01:00
André Bispo
c0688c584e [PM-1208] Fix merge 2023-07-20 15:37:08 +01:00
André Bispo
c09672ff88 [PM-1208] Fix app resource file 2023-07-20 15:30:29 +01:00
André Bispo
635b6bc184 Merge branch 'feature/pm-1029-tde-login' into feature/pm-1208-f3-options
# Conflicts:
#	src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs
#	src/App/Resources/AppResources.resx
#	src/Core/Abstractions/IStateService.cs
2023-07-20 14:56:40 +01:00
Jacob Fink
da7a1964ef Update crypto service api call to fix build 2023-07-20 09:28:56 -04:00
Jacob Fink
73b8d8e6b8 fix bad merge 2023-07-20 09:20:31 -04:00
Jacob Fink
c61f9f0357 Merge branch 'auth/pm-2713/drop-master-key-dependency' into feature/pm-1029-tde-login 2023-07-20 09:19:48 -04:00
Jacob Fink
a3183857b9 [PM-2713] set decrypt and set user key in login helper 2023-07-19 21:23:21 -04:00
Jacob Fink
bedbca841d [PM-2713] remove unused cached values in crypto service 2023-07-19 21:23:21 -04:00
Jacob Fink
0ff314f076 [PM-2713] use new crypto service api in auth service 2023-07-19 21:23:21 -04:00
Jacob Fink
c9a7c29190 [PM-2713] More conversions to crypto api 2023-07-19 21:23:20 -04:00
Jacob Fink
546bf8dcb1 [PM-2713] convert cipher service and others to crypto service api 2023-07-19 21:23:20 -04:00
Jacob Fink
7fdc5597fc [PM-2713] more conversions to new crypto service api 2023-07-19 21:23:20 -04:00
Jacob Fink
7c664f58b3 [PM-2713] add migration for pin on lock screens 2023-07-19 21:23:20 -04:00
Jacob Fink
bdfe806846 [PM-2713] converting calls to new crypto service api 2023-07-19 21:23:19 -04:00
Jacob Fink
5ed567ab90 [PM-2713] add toggle method to crypto service for keys 2023-07-19 21:23:19 -04:00
Jacob Fink
cd4f44e6f6 [PM-2713] use new MakeMasterKey method 2023-07-19 21:23:19 -04:00
Jacob Fink
d58f0b281b [PM-2713] refresh pin key when setting user key 2023-07-19 21:23:19 -04:00
Jacob Fink
5ba3fac0c0 [PM-2713] add make user key method to crypto service 2023-07-19 21:23:19 -04:00
Jacob Fink
1e30524985 [PM-2713] fix signature of GetUserKeyPin 2023-07-19 21:23:18 -04:00
Jacob Fink
515decb4c9 [PM-2713] add new pin methods to state service 2023-07-19 21:23:18 -04:00
Jacob Fink
bf28d373e9 [PM-2713] more updates to crypto service 2023-07-19 21:23:18 -04:00
Jacob Fink
69d38d4d75 [PM-2713] continue organizing crypto service 2023-07-19 21:23:18 -04:00
Jacob Fink
c1619536aa [PM-2713] rename key hash to password hash & begin add methods to crypto service 2023-07-19 21:23:17 -04:00
Jacob Fink
079e02e4e5 [PM-271] add UserKey and MasterKey support to crypto service 2023-07-19 21:23:17 -04:00
Jacob Fink
15d3da607b [PM-2713] add new state for new keys and obsolete old ones
- UserKey
- MasterKey
- UserKeyMasterKey (enc UserKey from User Table)
2023-07-19 21:23:17 -04:00
Jacob Fink
b5cf9fd79d [PM-2731] add user key and master key types 2023-07-19 21:23:17 -04:00
André Bispo
b688b85d0f [PM-1201] Change timeout actions available based on hasMasterPassword (#2610)
* [PM-1201] Change timeout actions available based on hasMasterPassword
2023-07-12 20:42:21 +01:00
André Bispo
a5df6c0c65 [PM-2287][PM-2289][PM-2293] Approval Options (#2608)
* [PM-2293] Add AuthRequestType to PasswordlessLoginPage.

* [PM-2293] Add Actions to ApproveWithDevicePage

* [PM-2293] Change screen text based on AuthRequestType

* [PM-2293] Refactor AuthRequestType enum. Add label. Remove unnecessary actions.

* [PM-2293] Change boolean variable expression.

* [PM-2293] Trust device after admin request login.

* code format

* [PM-2287] Add trust device to master password unlock. Change trust device method. Remove email from SSO login page.

* [PM-2293] Fix state variable get set.

* [PM-2287][PM-2289][PM-2293] Rename method
2023-07-12 19:12:57 +01:00
André Bispo
c2d4fa4429 [PM-2583] Answer auth request with mp field as null if doesn't have it. (#2609) 2023-07-11 23:05:35 +01:00
André Bispo
548bd12a8e Merge branch 'feature/pm-1029-tde-login' into feature/pm-1208-f3-options 2023-07-10 12:37:13 +01:00
André Bispo
58542fd255 Merge branch 'master' into feature/pm-1029-tde-login 2023-07-10 12:36:59 +01:00
André Bispo
800b4c71de Merge branch 'feature/pm-1029-tde-login' into feature/pm-1208-f3-options
# Conflicts:
#	src/Core/Models/Response/DeviceResponse.cs
#	src/Core/Services/ApiService.cs
2023-07-10 12:32:27 +01:00
Jake Fink
3053eaa036 [PM-1379] add DeviceTrustCryptoService with establish trust logic (#2535)
* [PM-1379] add DeviceCryptoService with establish trust logic

* PM-1379 update api location and other minor refactors

* pm-1379 fix encoding

* update trusted device keys api call to Put

* [PM-1379] rename DeviceCryptoService to DeviceTrustCryptoService
- refactors to prevent side effects

* [PM-1379] rearrange methods in DeviceTrustCryptoService

* [PM-1379] rearrange methods in abstraction

* [PM-1379] deconstruct tuples

* [PM-1379] remove extra tasks
2023-07-05 16:13:20 -04:00
André Bispo
6268f0776b Merge branch 'feature/pm-1029-tde-login' into feature/pm-1208-f3-options 2023-07-03 10:34:53 +01:00
André Bispo
cbbc41be67 [PM-1208] Add continue button and not you option 2023-07-03 10:34:02 +01:00
André Bispo
e164fb9823 Merge branch 'feature/pm-1029-tde-login' into feature/pm-1208-f3-options
# Conflicts:
#	src/App/Resources/AppResources.resx
#	src/Core/Abstractions/IApiService.cs
#	src/Core/Services/StateService.cs
2023-06-29 14:36:46 +01:00
André Bispo
87866304a6 [PM-1208] Add device related api endpoint. Add AccoundDecryptOptions model and property to user Account. 2023-06-28 22:37:08 +01:00
André Bispo
84a82f0876 [PM-1208] Add Device approval options screen. View model waiting for additional logic to be added. 2023-05-17 17:46:45 +01:00
1201 changed files with 4009 additions and 265109 deletions

View File

@@ -1,5 +0,0 @@
{
"sdk": {
"version": "7.0.306"
}
}

View File

@@ -70,7 +70,8 @@ namespace Bit.Droid
var verificationActionsFlowHelper = new VerificationActionsFlowHelper( var verificationActionsFlowHelper = new VerificationActionsFlowHelper(
ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService"), ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService"),
ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService"), ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService"),
ServiceContainer.Resolve<ICryptoService>("cryptoService")); ServiceContainer.Resolve<ICryptoService>("cryptoService"),
ServiceContainer.Resolve<IUserVerificationService>());
ServiceContainer.Register<IVerificationActionsFlowHelper>("verificationActionsFlowHelper", verificationActionsFlowHelper); ServiceContainer.Register<IVerificationActionsFlowHelper>("verificationActionsFlowHelper", verificationActionsFlowHelper);
var accountsManager = new AccountsManager( var accountsManager = new AccountsManager(
@@ -156,9 +157,9 @@ namespace Bit.Droid
messagingService, broadcasterService); messagingService, broadcasterService);
var autofillHandler = new AutofillHandler(stateService, messagingService, clipboardService, var autofillHandler = new AutofillHandler(stateService, messagingService, clipboardService,
platformUtilsService, new LazyResolve<IEventService>()); platformUtilsService, new LazyResolve<IEventService>());
var biometricService = new BiometricService(stateService);
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService); var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
var cryptoService = new CryptoService(stateService, cryptoFunctionService); var cryptoService = new CryptoService(stateService, cryptoFunctionService);
var biometricService = new BiometricService(stateService, cryptoService);
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService); var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
ServiceContainer.Register<ISynchronousStorageService>(preferencesStorage); ServiceContainer.Register<ISynchronousStorageService>(preferencesStorage);

View File

@@ -2,6 +2,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Android.OS; using Android.OS;
using Android.Security.Keystore; using Android.Security.Keystore;
using Bit.App.Services;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Services; using Bit.Core.Services;
using Java.Security; using Java.Security;
@@ -9,10 +10,8 @@ using Javax.Crypto;
namespace Bit.Droid.Services namespace Bit.Droid.Services
{ {
public class BiometricService : IBiometricService public class BiometricService : BaseBiometricService
{ {
private readonly IStateService _stateService;
private const string KeyName = "com.8bit.bitwarden.biometric_integrity"; private const string KeyName = "com.8bit.bitwarden.biometric_integrity";
private const string KeyStoreName = "AndroidKeyStore"; private const string KeyStoreName = "AndroidKeyStore";
@@ -24,14 +23,14 @@ namespace Bit.Droid.Services
private readonly KeyStore _keystore; private readonly KeyStore _keystore;
public BiometricService(IStateService stateService) public BiometricService(IStateService stateService, ICryptoService cryptoService)
: base(stateService, cryptoService)
{ {
_stateService = stateService;
_keystore = KeyStore.GetInstance(KeyStoreName); _keystore = KeyStore.GetInstance(KeyStoreName);
_keystore.Load(null); _keystore.Load(null);
} }
public async Task<bool> SetupBiometricAsync(string bioIntegritySrcKey = null) public override async Task<bool> SetupBiometricAsync(string bioIntegritySrcKey = null)
{ {
if (Build.VERSION.SdkInt >= BuildVersionCodes.M) if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
{ {
@@ -41,7 +40,7 @@ namespace Bit.Droid.Services
return true; return true;
} }
public async Task<bool> IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null) public override async Task<bool> IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null)
{ {
if (Build.VERSION.SdkInt < BuildVersionCodes.M) if (Build.VERSION.SdkInt < BuildVersionCodes.M)
{ {

View File

@@ -9,7 +9,5 @@ namespace Bit.App.Abstractions
Task<bool> ShowPasswordPromptAsync(); Task<bool> ShowPasswordPromptAsync();
Task<(string password, bool valid)> ShowPasswordPromptAndGetItAsync(); Task<(string password, bool valid)> ShowPasswordPromptAndGetItAsync();
Task<bool> Enabled();
} }
} }

View File

@@ -1,28 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<RootNamespace>Bit.App</RootNamespace> <RootNamespace>Bit.App</RootNamespace>
<AssemblyName>BitwardenApp</AssemblyName> <AssemblyName>BitwardenApp</AssemblyName>
<Configurations>Debug;Release;FDroid</Configurations> <Configurations>Debug;Release;FDroid</Configurations>
<TargetFrameworks>net7.0-android;net7.0-ios</TargetFrameworks>
<UseMaui>True</UseMaui>
<OutputType>Library</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<SingleProject>true</SingleProject>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>pdbonly</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Plugin.Fingerprint" Version="2.1.5" /> <PackageReference Include="Plugin.Fingerprint" Version="2.1.5" />
<PackageReference Include="SkiaSharp.Views.Forms" Version="2.88.3" />
<PackageReference Include="Xamarin.CommunityToolkit" Version="2.0.6" />
<PackageReference Include="Xamarin.Essentials" Version="1.7.5" />
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" /> <PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" />
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2578" />
<PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" /> <PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" />
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" /> <PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" />
<PackageReference Include="MessagePack" Version="2.4.59" /> <PackageReference Include="MessagePack" Version="2.4.59" />
<PackageReference Include="SkiaSharp.Views.Maui.Core" Version="2.88.3" />
<PackageReference Include="SkiaSharp.Views.Maui.Controls" Version="2.88.3" />
<PackageReference Include="SkiaSharp.Views.Maui.Controls.Compatibility" Version="2.88.3" />
<PackageReference Include="CommunityToolkit.Maui" Version="5.2.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Core\Core.csproj" /> <ProjectReference Include="..\Core\Core.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="Pages\Accounts\EnvironmentPage.xaml.cs"> <Compile Update="Pages\Accounts\EnvironmentPage.xaml.cs">
<DependentUpon>EnvironmentPage.xaml</DependentUpon> <DependentUpon>EnvironmentPage.xaml</DependentUpon>
@@ -125,6 +130,7 @@
<DependentUpon>LoginPasswordlessRequestPage.xaml</DependentUpon> <DependentUpon>LoginPasswordlessRequestPage.xaml</DependentUpon>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Resources\" /> <Folder Include="Resources\" />
<Folder Include="Behaviors\" /> <Folder Include="Behaviors\" />
@@ -142,9 +148,11 @@
<Folder Include="Utilities\Automation\" /> <Folder Include="Utilities\Automation\" />
<Folder Include="Utilities\Prompts\" /> <Folder Include="Utilities\Prompts\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Remove="Pages\Accounts\AccountsPopupPage.xaml" /> <EmbeddedResource Remove="Pages\Accounts\AccountsPopupPage.xaml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="Styles\Black.xaml.cs"> <Compile Update="Styles\Black.xaml.cs">
<DependentUpon>Black.xaml</DependentUpon> <DependentUpon>Black.xaml</DependentUpon>
@@ -168,6 +176,7 @@
<DependentUpon>Android.xaml</DependentUpon> <DependentUpon>Android.xaml</DependentUpon>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="Resources\AppResources.cs.Designer.cs"> <Compile Update="Resources\AppResources.cs.Designer.cs">
<DependentUpon>AppResources.cs.resx</DependentUpon> <DependentUpon>AppResources.cs.resx</DependentUpon>
@@ -305,6 +314,7 @@
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Update="Resources\AppResources.cs.resx"> <EmbeddedResource Update="Resources\AppResources.cs.resx">
<LastGenOutput>AppResources.cs.Designer.cs</LastGenOutput> <LastGenOutput>AppResources.cs.Designer.cs</LastGenOutput>
@@ -415,6 +425,7 @@
<Generator>ResXFileCodeGenerator</Generator> <Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Remove="Behaviors\" /> <None Remove="Behaviors\" />
<None Remove="Xamarin.CommunityToolkit" /> <None Remove="Xamarin.CommunityToolkit" />
@@ -434,4 +445,4 @@
<None Remove="Utilities\Automation\" /> <None Remove="Utilities\Automation\" />
<None Remove="Utilities\Prompts\" /> <None Remove="Utilities\Prompts\" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui" <Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.App"> x:Class="Bit.App.App">
<Application.Resources> <Application.Resources>

View File

@@ -15,9 +15,8 @@ using Bit.Core.Models.Data;
using Bit.Core.Models.Response; using Bit.Core.Models.Response;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Microsoft.Maui.Controls.Xaml; using Xamarin.Forms;
using Microsoft.Maui.Controls; using Xamarin.Forms.Xaml;
using Microsoft.Maui;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)] [assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace Bit.App namespace Bit.App
@@ -94,7 +93,6 @@ namespace Bit.App
} }
else if (message.Command == "resumed") else if (message.Command == "resumed")
{ {
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.iOS) if (Device.RuntimePlatform == Device.iOS)
{ {
ResumedAsync().FireAndForget(); ResumedAsync().FireAndForget();
@@ -102,7 +100,6 @@ namespace Bit.App
} }
else if (message.Command == "slept") else if (message.Command == "slept")
{ {
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.iOS) if (Device.RuntimePlatform == Device.iOS)
{ {
await SleptAsync(); await SleptAsync();
@@ -124,7 +121,7 @@ namespace Bit.App
Options.OtpData = new OtpData((string)message.Data); Options.OtpData = new OtpData((string)message.Data);
} }
Device.InvokeOnMainThreadAsync(async () => await Device.InvokeOnMainThreadAsync(async () =>
{ {
if (Current.MainPage is TabsPage tabsPage) if (Current.MainPage is TabsPage tabsPage)
{ {
@@ -296,7 +293,6 @@ namespace Bit.App
{ {
_messagingService.Send(Constants.PasswordlessLoginRequestKey); _messagingService.Send(Constants.PasswordlessLoginRequestKey);
} }
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.Android) if (Device.RuntimePlatform == Device.Android)
{ {
await _vaultTimeoutService.CheckVaultTimeoutAsync(); await _vaultTimeoutService.CheckVaultTimeoutAsync();
@@ -312,7 +308,6 @@ namespace Bit.App
{ {
System.Diagnostics.Debug.WriteLine("XF App: OnSleep"); System.Diagnostics.Debug.WriteLine("XF App: OnSleep");
_isResumed = false; _isResumed = false;
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.Android) if (Device.RuntimePlatform == Device.Android)
{ {
var isLocked = await _vaultTimeoutService.IsLockedAsync(); var isLocked = await _vaultTimeoutService.IsLockedAsync();
@@ -336,7 +331,6 @@ namespace Bit.App
{ {
_messagingService.Send(Constants.PasswordlessLoginRequestKey); _messagingService.Send(Constants.PasswordlessLoginRequestKey);
} }
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.Android) if (Device.RuntimePlatform == Device.Android)
{ {
ResumedAsync().FireAndForget(); ResumedAsync().FireAndForget();
@@ -402,7 +396,6 @@ namespace Bit.App
private void ClearAutofillUri() private void ClearAutofillUri()
{ {
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(Options.Uri)) if (Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(Options.Uri))
{ {
Options.Uri = null; Options.Uri = null;
@@ -411,7 +404,6 @@ namespace Bit.App
private bool SetTabsPageFromAutofill(bool isLocked) private bool SetTabsPageFromAutofill(bool isLocked)
{ {
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(Options.Uri) && if (Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(Options.Uri) &&
!Options.FromAutofillFramework) !Options.FromAutofillFramework)
{ {
@@ -460,7 +452,7 @@ namespace Bit.App
private void SyncIfNeeded() private void SyncIfNeeded()
{ {
if (Microsoft.Maui.Networking.Connectivity.NetworkAccess == Microsoft.Maui.Networking.NetworkAccess.None) if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
{ {
return; return;
} }

View File

@@ -1,5 +1,5 @@
using Microsoft.Maui.Controls; using Xamarin.Essentials;
using Microsoft.Maui; using Xamarin.Forms;
namespace Bit.App.Behaviors namespace Bit.App.Behaviors
{ {

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<ContentView <ContentView
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="http://schemas.microsoft.com/dotnet/2022/maui/toolkit" xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:controls="clr-namespace:Bit.App.Controls" xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:effects="clr-namespace:Bit.App.Effects" xmlns:effects="clr-namespace:Bit.App.Effects"
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore" xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"

View File

@@ -4,8 +4,7 @@ using System.Windows.Input;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel; using Xamarin.CommunityToolkit.ObjectModel;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {
@@ -62,8 +61,7 @@ namespace Bit.App.Controls
public ICommand LongPressAccountCommand { get; } public ICommand LongPressAccountCommand { get; }
public int AccountListRowHeight => // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes public int AccountListRowHeight => Device.RuntimePlatform == Device.Android ? 74 : 70;
Device.RuntimePlatform == Device.Android ? 74 : 70;
public bool LongPressAccountEnabled { get; set; } = true; public bool LongPressAccountEnabled { get; set; } = true;

View File

@@ -7,8 +7,7 @@ using Bit.Core.Abstractions;
using Bit.Core.Models.View; using Bit.Core.Models.View;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel; using Xamarin.CommunityToolkit.ObjectModel;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://schemas.microsoft.com/dotnet/2021/maui" <ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="http://schemas.microsoft.com/dotnet/2022/maui/toolkit" xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
x:Class="Bit.App.Controls.AccountViewCell" x:Class="Bit.App.Controls.AccountViewCell"
xmlns:controls="clr-namespace:Bit.App.Controls" xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:u="clr-namespace:Bit.App.Utilities" xmlns:u="clr-namespace:Bit.App.Utilities"

View File

@@ -1,7 +1,6 @@
using System.Windows.Input; using System.Windows.Input;
using Bit.Core.Models.View; using Bit.Core.Models.View;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<controls:ExtendedGrid xmlns="http://schemas.microsoft.com/dotnet/2021/maui" <controls:ExtendedGrid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Controls.AuthenticatorViewCell" x:Class="Bit.App.Controls.AuthenticatorViewCell"
xmlns:controls="clr-namespace:Bit.App.Controls" xmlns:controls="clr-namespace:Bit.App.Controls"

View File

@@ -3,8 +3,7 @@ using Bit.App.Pages;
using Bit.App.Utilities; using Bit.App.Utilities;
using Bit.Core.Models.View; using Bit.Core.Models.View;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {

View File

@@ -5,8 +5,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using SkiaSharp; using SkiaSharp;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {
@@ -161,7 +160,7 @@ namespace Bit.App.Controls
{ {
if (str == null) if (str == null)
{ {
return Color.FromArgb("#33ffffff"); return Color.FromHex("#33ffffff");
} }
var hash = 0; var hash = 0;
for (var i = 0; i < str.Length; i++) for (var i = 0; i < str.Length; i++)
@@ -175,7 +174,7 @@ namespace Bit.App.Controls
var base16 = "00" + Convert.ToString(value, 16); var base16 = "00" + Convert.ToString(value, 16);
color += base16.Substring(base16.Length - 2); color += base16.Substring(base16.Length - 2);
} }
return Color.FromArgb(color); return Color.FromHex(color);
} }
} }
} }

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<controls:ExtendedGrid xmlns="http://schemas.microsoft.com/dotnet/2021/maui" <controls:ExtendedGrid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Controls.CipherViewCell" x:Class="Bit.App.Controls.CipherViewCell"
xmlns:controls="clr-namespace:Bit.App.Controls" xmlns:controls="clr-namespace:Bit.App.Controls"

View File

@@ -3,8 +3,7 @@ using System.Windows.Input;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.Core.Models.View; using Bit.Core.Models.View;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {

View File

@@ -1,11 +1,9 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SkiaSharp; using SkiaSharp;
using Microsoft.Maui.Graphics; using SkiaSharp.Views.Forms;
using Microsoft.Maui.Controls; using Xamarin.Essentials;
using Microsoft.Maui; using Xamarin.Forms;
using SkiaSharp.Views.Maui.Controls;
using SkiaSharp.Views.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {
@@ -23,13 +21,13 @@ namespace Bit.App.Controls
nameof(StrokeWidth), typeof(float), typeof(CircularProgressbarView), 3f); nameof(StrokeWidth), typeof(float), typeof(CircularProgressbarView), 3f);
public static readonly BindableProperty ProgressColorProperty = BindableProperty.Create( public static readonly BindableProperty ProgressColorProperty = BindableProperty.Create(
nameof(ProgressColor), typeof(Color), typeof(CircularProgressbarView), Color.FromArgb("175DDC")); nameof(ProgressColor), typeof(Color), typeof(CircularProgressbarView), Color.FromHex("175DDC"));
public static readonly BindableProperty EndingProgressColorProperty = BindableProperty.Create( public static readonly BindableProperty EndingProgressColorProperty = BindableProperty.Create(
nameof(EndingProgressColor), typeof(Color), typeof(CircularProgressbarView), Color.FromArgb("dd4b39")); nameof(EndingProgressColor), typeof(Color), typeof(CircularProgressbarView), Color.FromHex("dd4b39"));
public static readonly BindableProperty BackgroundProgressColorProperty = BindableProperty.Create( public static readonly BindableProperty BackgroundProgressColorProperty = BindableProperty.Create(
nameof(BackgroundProgressColor), typeof(Color), typeof(CircularProgressbarView), Colors.White); nameof(BackgroundProgressColor), typeof(Color), typeof(CircularProgressbarView), Color.White);
public double Progress public double Progress
{ {

View File

@@ -1,5 +1,4 @@
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<Grid <Grid
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:Bit.App.Controls" xmlns:controls="clr-namespace:Bit.App.Controls"
x:Class="Bit.App.Controls.DateTimePicker" x:Class="Bit.App.Controls.DateTimePicker"

View File

@@ -1,12 +1,6 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Microsoft.Maui.Controls; using Xamarin.CommunityToolkit.UI.Views;
using Microsoft.Maui; using Xamarin.Forms;
using CommunityToolkit.Maui.Converters;
using CommunityToolkit.Maui.ImageSources;
using CommunityToolkit.Maui;
using CommunityToolkit.Maui.Core;
using CommunityToolkit.Maui.Layouts;
using CommunityToolkit.Maui.Views;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {

View File

@@ -1,7 +1,6 @@
using System.Linq; using System.Linq;
using CommunityToolkit.Maui.Converters; using Xamarin.CommunityToolkit.Converters;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {

View File

@@ -1,6 +1,5 @@
using System; using System;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {

View File

@@ -1,5 +1,4 @@
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {

View File

@@ -1,7 +1,5 @@
using Bit.App.Utilities; using Bit.App.Utilities;
using Microsoft.Maui.Graphics; using Xamarin.Forms;
using Microsoft.Maui.Controls;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {
@@ -9,7 +7,6 @@ namespace Bit.App.Controls
{ {
public ExtendedSearchBar() public ExtendedSearchBar()
{ {
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.iOS) if (Device.RuntimePlatform == Device.iOS)
{ {
if (ThemeManager.UsingLightTheme) if (ThemeManager.UsingLightTheme)

View File

@@ -1,12 +1,11 @@
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {
public class ExtendedSlider : Slider public class ExtendedSlider : Slider
{ {
public static readonly BindableProperty ThumbBorderColorProperty = BindableProperty.Create( public static readonly BindableProperty ThumbBorderColorProperty = BindableProperty.Create(
nameof(ThumbBorderColor), typeof(Color), typeof(ExtendedSlider), Color.FromArgb("b5b5b5")); nameof(ThumbBorderColor), typeof(Color), typeof(ExtendedSlider), Color.FromHex("b5b5b5"));
public Color ThumbBorderColor public Color ThumbBorderColor
{ {

View File

@@ -1,5 +1,4 @@
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {

View File

@@ -1,16 +1,14 @@
using Microsoft.Maui.Graphics; using Xamarin.Forms;
using Microsoft.Maui.Controls;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {
public class ExtendedStepper : Stepper public class ExtendedStepper : Stepper
{ {
public static readonly BindableProperty StepperBackgroundColorProperty = BindableProperty.Create( public static readonly BindableProperty StepperBackgroundColorProperty = BindableProperty.Create(
nameof(StepperBackgroundColor), typeof(Color), typeof(ExtendedStepper), Colors.White); nameof(StepperBackgroundColor), typeof(Color), typeof(ExtendedStepper), Color.White);
public static readonly BindableProperty StepperForegroundColorProperty = BindableProperty.Create( public static readonly BindableProperty StepperForegroundColorProperty = BindableProperty.Create(
nameof(StepperForegroundColor), typeof(Color), typeof(ExtendedStepper), Colors.Black); nameof(StepperForegroundColor), typeof(Color), typeof(ExtendedStepper), Color.Black);
public Color StepperBackgroundColor public Color StepperBackgroundColor
{ {

View File

@@ -1,6 +1,5 @@
using System; using System;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {

View File

@@ -1,6 +1,5 @@
using System; using System;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {

View File

@@ -1,6 +1,5 @@
using System; using System;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {

View File

@@ -1,6 +1,5 @@
using Bit.App.Effects; using Bit.App.Effects;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {
@@ -9,7 +8,6 @@ namespace Bit.App.Controls
public IconButton() public IconButton()
{ {
Padding = 0; Padding = 0;
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
switch (Device.RuntimePlatform) switch (Device.RuntimePlatform)
{ {
case Device.iOS: case Device.iOS:

View File

@@ -1,6 +1,5 @@
using Bit.App.Effects; using Bit.App.Effects;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {
@@ -10,7 +9,6 @@ namespace Bit.App.Controls
public IconLabel() public IconLabel()
{ {
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
switch (Device.RuntimePlatform) switch (Device.RuntimePlatform)
{ {
case Device.iOS: case Device.iOS:

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Frame xmlns="http://schemas.microsoft.com/dotnet/2021/maui" <Frame xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Controls.IconLabelButton" x:Class="Bit.App.Controls.IconLabelButton"
xmlns:controls="clr-namespace:Bit.App.Controls" xmlns:controls="clr-namespace:Bit.App.Controls"

View File

@@ -5,10 +5,8 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
using Bit.Core.Models.Domain; using Bit.Core.Models.Domain;
using Microsoft.Maui.Controls.Xaml; using Xamarin.Forms;
using Microsoft.Maui.Graphics; using Xamarin.Forms.Xaml;
using Microsoft.Maui.Controls;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {
@@ -24,13 +22,13 @@ namespace Bit.App.Controls
nameof(ButtonCommand), typeof(ICommand), typeof(IconLabelButton)); nameof(ButtonCommand), typeof(ICommand), typeof(IconLabelButton));
public static readonly BindableProperty IconLabelColorProperty = BindableProperty.Create( public static readonly BindableProperty IconLabelColorProperty = BindableProperty.Create(
nameof(IconLabelColor), typeof(Color), typeof(IconLabelButton), Colors.White); nameof(IconLabelColor), typeof(Color), typeof(IconLabelButton), Color.White);
public static readonly BindableProperty IconLabelBackgroundColorProperty = BindableProperty.Create( public static readonly BindableProperty IconLabelBackgroundColorProperty = BindableProperty.Create(
nameof(IconLabelBackgroundColor), typeof(Color), typeof(IconLabelButton), Colors.White); nameof(IconLabelBackgroundColor), typeof(Color), typeof(IconLabelButton), Color.White);
public static readonly BindableProperty IconLabelBorderColorProperty = BindableProperty.Create( public static readonly BindableProperty IconLabelBorderColorProperty = BindableProperty.Create(
nameof(IconLabelBorderColor), typeof(Color), typeof(IconLabelButton), Colors.White); nameof(IconLabelBorderColor), typeof(Color), typeof(IconLabelButton), Color.White);
public IconLabelButton() public IconLabelButton()
{ {

View File

@@ -1,5 +1,4 @@
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {
@@ -8,7 +7,6 @@ namespace Bit.App.Controls
public MiButton() public MiButton()
{ {
Padding = 0; Padding = 0;
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
switch (Device.RuntimePlatform) switch (Device.RuntimePlatform)
{ {
case Device.iOS: case Device.iOS:

View File

@@ -1,5 +1,4 @@
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {
@@ -7,7 +6,6 @@ namespace Bit.App.Controls
{ {
public MiLabel() public MiLabel()
{ {
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
switch (Device.RuntimePlatform) switch (Device.RuntimePlatform)
{ {
case Device.iOS: case Device.iOS:

View File

@@ -1,5 +1,4 @@
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {
@@ -7,7 +6,6 @@ namespace Bit.App.Controls
{ {
public MonoEntry() public MonoEntry()
{ {
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
switch (Device.RuntimePlatform) switch (Device.RuntimePlatform)
{ {
case Device.iOS: case Device.iOS:

View File

@@ -1,5 +1,4 @@
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {
@@ -7,7 +6,6 @@ namespace Bit.App.Controls
{ {
public MonoLabel() public MonoLabel()
{ {
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
switch (Device.RuntimePlatform) switch (Device.RuntimePlatform)
{ {
case Device.iOS: case Device.iOS:

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<StackLayout <StackLayout
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:Bit.App.Controls" xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:u="clr-namespace:Bit.App.Utilities" xmlns:u="clr-namespace:Bit.App.Utilities"

View File

@@ -1,7 +1,5 @@
using System; using System;
using Microsoft.Maui.Graphics; using Xamarin.Forms;
using Microsoft.Maui.Controls;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {
@@ -102,7 +100,7 @@ namespace Bit.App.Controls
case Controls.PasswordStrengthLevel.Strong: case Controls.PasswordStrengthLevel.Strong:
return StrongColor; return StrongColor;
default: default:
return Colors.Transparent; return Color.Transparent;
} }
} }
} }

View File

@@ -1,8 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {

View File

@@ -1,8 +1,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Specialized; using System.Collections.Specialized;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {

View File

@@ -1,6 +1,5 @@
using System; using System;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<controls:ExtendedGrid xmlns="http://schemas.microsoft.com/dotnet/2021/maui" <controls:ExtendedGrid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Controls.SendViewCell" x:Class="Bit.App.Controls.SendViewCell"
xmlns:controls="clr-namespace:Bit.App.Controls" xmlns:controls="clr-namespace:Bit.App.Controls"

View File

@@ -2,8 +2,7 @@
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.Core.Models.View; using Bit.Core.Models.View;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {

View File

@@ -1,5 +1,4 @@
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Effects namespace Bit.App.Effects
{ {

View File

@@ -1,5 +1,4 @@
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Effects namespace Bit.App.Effects
{ {

View File

@@ -1,6 +1,5 @@
using System; using System;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Effects namespace Bit.App.Effects
{ {

View File

@@ -1,6 +1,5 @@
using System; using System;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Effects namespace Bit.App.Effects
{ {

View File

@@ -1,5 +1,4 @@
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Effects namespace Bit.App.Effects
{ {

View File

@@ -1,5 +1,4 @@
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Effects namespace Bit.App.Effects
{ {

View File

@@ -1,5 +1,4 @@
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Effects namespace Bit.App.Effects
{ {

View File

@@ -1,6 +1,5 @@
using Bit.App.Lists.ItemViewModels.CustomFields; using Bit.App.Lists.ItemViewModels.CustomFields;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Lists.DataTemplateSelectors namespace Bit.App.Lists.DataTemplateSelectors
{ {

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<StackLayout <StackLayout
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Lists.ItemLayouts.CustomFields.BooleanCustomFieldItemLayout" x:Class="Bit.App.Lists.ItemLayouts.CustomFields.BooleanCustomFieldItemLayout"
xmlns:u="clr-namespace:Bit.App.Utilities" xmlns:u="clr-namespace:Bit.App.Utilities"

View File

@@ -1,5 +1,4 @@
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Lists.ItemLayouts.CustomFields namespace Bit.App.Lists.ItemLayouts.CustomFields
{ {

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<StackLayout xmlns="http://schemas.microsoft.com/dotnet/2021/maui" <StackLayout xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Lists.ItemLayouts.CustomFields.HiddenCustomFieldItemLayout" x:Class="Bit.App.Lists.ItemLayouts.CustomFields.HiddenCustomFieldItemLayout"
xmlns:controls="clr-namespace:Bit.App.Controls" xmlns:controls="clr-namespace:Bit.App.Controls"

View File

@@ -1,5 +1,4 @@
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Lists.ItemLayouts.CustomFields namespace Bit.App.Lists.ItemLayouts.CustomFields
{ {

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<StackLayout xmlns="http://schemas.microsoft.com/dotnet/2021/maui" <StackLayout xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Lists.ItemLayouts.CustomFields.LinkedCustomFieldItemLayout" x:Class="Bit.App.Lists.ItemLayouts.CustomFields.LinkedCustomFieldItemLayout"
xmlns:controls="clr-namespace:Bit.App.Controls" xmlns:controls="clr-namespace:Bit.App.Controls"

View File

@@ -1,5 +1,4 @@
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Lists.ItemLayouts.CustomFields namespace Bit.App.Lists.ItemLayouts.CustomFields
{ {

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<StackLayout xmlns="http://schemas.microsoft.com/dotnet/2021/maui" <StackLayout xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Lists.ItemLayouts.CustomFields.TextCustomFieldItemLayout" x:Class="Bit.App.Lists.ItemLayouts.CustomFields.TextCustomFieldItemLayout"
xmlns:controls="clr-namespace:Bit.App.Controls" xmlns:controls="clr-namespace:Bit.App.Controls"

View File

@@ -1,5 +1,4 @@
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Lists.ItemLayouts.CustomFields namespace Bit.App.Lists.ItemLayouts.CustomFields
{ {

View File

@@ -1,8 +1,7 @@
using System.Windows.Input; using System.Windows.Input;
using Bit.Core.Models.View; using Bit.Core.Models.View;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Lists.ItemViewModels.CustomFields namespace Bit.App.Lists.ItemViewModels.CustomFields
{ {

View File

@@ -5,8 +5,7 @@ using Bit.App.Utilities;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Models.View; using Bit.Core.Models.View;
using Xamarin.CommunityToolkit.ObjectModel; using Xamarin.CommunityToolkit.ObjectModel;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Lists.ItemViewModels.CustomFields namespace Bit.App.Lists.ItemViewModels.CustomFields
{ {

View File

@@ -1,7 +1,6 @@
using System.Windows.Input; using System.Windows.Input;
using Bit.Core.Models.View; using Bit.Core.Models.View;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Lists.ItemViewModels.CustomFields namespace Bit.App.Lists.ItemViewModels.CustomFields
{ {

View File

@@ -6,6 +6,7 @@ using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Models.Domain; using Bit.Core.Models.Domain;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.Essentials;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<pages:BaseContentPage <pages:BaseContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.Accounts.DeleteAccountPage" x:Class="Bit.App.Pages.Accounts.DeleteAccountPage"
xmlns:pages="clr-namespace:Bit.App.Pages" xmlns:pages="clr-namespace:Bit.App.Pages"

View File

@@ -1,6 +1,5 @@
using System; using System;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Pages.Accounts namespace Bit.App.Pages.Accounts
{ {

View File

@@ -27,7 +27,7 @@ namespace Bit.App.Pages
{ {
try try
{ {
if (Microsoft.Maui.Networking.Connectivity.NetworkAccess == Microsoft.Maui.Networking.NetworkAccess.None) if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
{ {
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage, await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
AppResources.InternetConnectionRequiredTitle, AppResources.Ok); AppResources.InternetConnectionRequiredTitle, AppResources.Ok);

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<pages:BaseContentPage <pages:BaseContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.EnvironmentPage" x:Class="Bit.App.Pages.EnvironmentPage"
xmlns:pages="clr-namespace:Bit.App.Pages" xmlns:pages="clr-namespace:Bit.App.Pages"

View File

@@ -3,8 +3,7 @@ using System.Threading.Tasks;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
@@ -19,7 +18,6 @@ namespace Bit.App.Pages
InitializeComponent(); InitializeComponent();
_vm = BindingContext as EnvironmentPageViewModel; _vm = BindingContext as EnvironmentPageViewModel;
_vm.Page = this; _vm.Page = this;
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.Android) if (Device.RuntimePlatform == Device.Android)
{ {
ToolbarItems.RemoveAt(0); ToolbarItems.RemoveAt(0);

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<pages:BaseContentPage <pages:BaseContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.HintPage" x:Class="Bit.App.Pages.HintPage"
xmlns:pages="clr-namespace:Bit.App.Pages" xmlns:pages="clr-namespace:Bit.App.Pages"

View File

@@ -1,5 +1,4 @@
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
@@ -13,7 +12,6 @@ namespace Bit.App.Pages
_vm = BindingContext as HintPageViewModel; _vm = BindingContext as HintPageViewModel;
_vm.Page = this; _vm.Page = this;
_vm.Email = email; _vm.Email = email;
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.Android) if (Device.RuntimePlatform == Device.Android)
{ {
ToolbarItems.RemoveAt(0); ToolbarItems.RemoveAt(0);

View File

@@ -44,7 +44,7 @@ namespace Bit.App.Pages
public async Task SubmitAsync() public async Task SubmitAsync()
{ {
if (Microsoft.Maui.Networking.Connectivity.NetworkAccess == Microsoft.Maui.Networking.NetworkAccess.None) if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
{ {
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage, await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
AppResources.InternetConnectionRequiredTitle); AppResources.InternetConnectionRequiredTitle);

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<pages:BaseContentPage <pages:BaseContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.HomePage" x:Class="Bit.App.Pages.HomePage"
xmlns:pages="clr-namespace:Bit.App.Pages" xmlns:pages="clr-namespace:Bit.App.Pages"

View File

@@ -4,8 +4,7 @@ using Bit.App.Models;
using Bit.App.Utilities; using Bit.App.Utilities;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {

View File

@@ -7,17 +7,17 @@ using Bit.App.Utilities;
using Bit.Core; using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Models.Data; using Bit.Core.Models.Data;
using Bit.Core.Models.Response;
using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel; using Xamarin.CommunityToolkit.ObjectModel;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
public class HomeViewModel : BaseViewModel public class HomeViewModel : BaseViewModel
{ {
private const string LOGGING_IN_ON_US = "bitwarden.com";
private const string LOGGING_IN_ON_EU = "bitwarden.eu";
private readonly IStateService _stateService; private readonly IStateService _stateService;
private readonly IMessagingService _messagingService; private readonly IMessagingService _messagingService;
private readonly IPlatformUtilsService _platformUtilsService; private readonly IPlatformUtilsService _platformUtilsService;
@@ -30,8 +30,6 @@ namespace Bit.App.Pages
private bool _rememberEmail; private bool _rememberEmail;
private string _email; private string _email;
private string _selectedEnvironmentName; private string _selectedEnvironmentName;
private bool _isEmailEnabled;
private bool _canLogin;
private bool _displayEuEnvironment; private bool _displayEuEnvironment;
public HomeViewModel() public HomeViewModel()
@@ -52,9 +50,9 @@ namespace Bit.App.Pages
}; };
RememberEmailCommand = new Command(() => RememberEmail = !RememberEmail); RememberEmailCommand = new Command(() => RememberEmail = !RememberEmail);
ContinueCommand = new AsyncCommand(ContinueToLoginStepAsync, allowsMultipleExecutions: false); ContinueCommand = new AsyncCommand(ContinueToLoginStepAsync, allowsMultipleExecutions: false);
CreateAccountCommand = new AsyncCommand(async () => Device.InvokeOnMainThreadAsync(StartRegisterAction), CreateAccountCommand = new AsyncCommand(async () => await Device.InvokeOnMainThreadAsync(StartRegisterAction),
onException: _logger.Exception, allowsMultipleExecutions: false); onException: _logger.Exception, allowsMultipleExecutions: false);
CloseCommand = new AsyncCommand(async () => Device.InvokeOnMainThreadAsync(CloseAction), CloseCommand = new AsyncCommand(async () => await Device.InvokeOnMainThreadAsync(CloseAction),
onException: _logger.Exception, allowsMultipleExecutions: false); onException: _logger.Exception, allowsMultipleExecutions: false);
ShowEnvironmentPickerCommand = new AsyncCommand(ShowEnvironmentPickerAsync, ShowEnvironmentPickerCommand = new AsyncCommand(ShowEnvironmentPickerAsync,
onException: _logger.Exception, allowsMultipleExecutions: false); onException: _logger.Exception, allowsMultipleExecutions: false);
@@ -86,7 +84,7 @@ namespace Bit.App.Pages
set => SetProperty(ref _selectedEnvironmentName, value); set => SetProperty(ref _selectedEnvironmentName, value);
} }
public string RegionText => $"{AppResources.Region}:"; public string RegionText => $"{AppResources.LoggingInOn}:";
public bool CanContinue => !string.IsNullOrEmpty(Email); public bool CanContinue => !string.IsNullOrEmpty(Email);
public FormattedString CreateAccountText public FormattedString CreateAccountText
@@ -167,12 +165,12 @@ namespace Bit.App.Pages
{ {
_displayEuEnvironment = await _configService.GetFeatureFlagBoolAsync(Constants.DisplayEuEnvironmentFlag); _displayEuEnvironment = await _configService.GetFeatureFlagBoolAsync(Constants.DisplayEuEnvironmentFlag);
var options = _displayEuEnvironment var options = _displayEuEnvironment
? new string[] { AppResources.US, AppResources.EU, AppResources.SelfHosted } ? new string[] { LOGGING_IN_ON_US, LOGGING_IN_ON_EU, AppResources.SelfHosted }
: new string[] { AppResources.US, AppResources.SelfHosted }; : new string[] { LOGGING_IN_ON_US, AppResources.SelfHosted };
await Device.InvokeOnMainThreadAsync(async () => await Device.InvokeOnMainThreadAsync(async () =>
{ {
var result = await Page.DisplayActionSheet(AppResources.DataRegion, AppResources.Cancel, null, options); var result = await Page.DisplayActionSheet(AppResources.LoggingInOn, AppResources.Cancel, null, options);
if (result is null || result == AppResources.Cancel) if (result is null || result == AppResources.Cancel)
{ {
@@ -185,7 +183,7 @@ namespace Bit.App.Pages
return; return;
} }
await _environmentService.SetUrlsAsync(result == AppResources.EU ? EnvironmentUrlData.DefaultEU : EnvironmentUrlData.DefaultUS); await _environmentService.SetUrlsAsync(result == LOGGING_IN_ON_EU ? EnvironmentUrlData.DefaultEU : EnvironmentUrlData.DefaultUS);
await _configService.GetAsync(true); await _configService.GetAsync(true);
SelectedEnvironmentName = result; SelectedEnvironmentName = result;
}); });
@@ -198,17 +196,17 @@ namespace Bit.App.Pages
{ {
await _environmentService.SetUrlsAsync(EnvironmentUrlData.DefaultUS); await _environmentService.SetUrlsAsync(EnvironmentUrlData.DefaultUS);
environmentsSaved = EnvironmentUrlData.DefaultUS; environmentsSaved = EnvironmentUrlData.DefaultUS;
SelectedEnvironmentName = AppResources.US; SelectedEnvironmentName = LOGGING_IN_ON_US;
return; return;
} }
if (environmentsSaved.Base == EnvironmentUrlData.DefaultUS.Base) if (environmentsSaved.Base == EnvironmentUrlData.DefaultUS.Base)
{ {
SelectedEnvironmentName = AppResources.US; SelectedEnvironmentName = LOGGING_IN_ON_US;
} }
else if (environmentsSaved.Base == EnvironmentUrlData.DefaultEU.Base) else if (environmentsSaved.Base == EnvironmentUrlData.DefaultEU.Base)
{ {
SelectedEnvironmentName = AppResources.EU; SelectedEnvironmentName = LOGGING_IN_ON_EU;
} }
else else
{ {

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<pages:BaseContentPage <pages:BaseContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.LockPage" x:Class="Bit.App.Pages.LockPage"
xmlns:pages="clr-namespace:Bit.App.Pages" xmlns:pages="clr-namespace:Bit.App.Pages"
@@ -46,7 +46,7 @@
<StackLayout StyleClass="box"> <StackLayout StyleClass="box">
<Grid <Grid
StyleClass="box-row" StyleClass="box-row"
IsVisible="{Binding PinLock}" IsVisible="{Binding PinEnabled}"
Padding="0, 10, 0, 0"> Padding="0, 10, 0, 0">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@@ -89,7 +89,7 @@
<Grid <Grid
x:Name="_passwordGrid" x:Name="_passwordGrid"
StyleClass="box-row" StyleClass="box-row"
IsVisible="{Binding PinLock, Converter={StaticResource inverseBool}}" IsVisible="{Binding PinEnabled, Converter={StaticResource inverseBool}}"
Padding="0, 10, 0, 0"> Padding="0, 10, 0, 0">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />

View File

@@ -6,8 +6,7 @@ using Bit.App.Utilities;
using Bit.Core; using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
@@ -21,17 +20,17 @@ namespace Bit.App.Pages
private bool _promptedAfterResume; private bool _promptedAfterResume;
private bool _appeared; private bool _appeared;
public LockPage(AppOptions appOptions = null, bool autoPromptBiometric = true) public LockPage(AppOptions appOptions = null, bool autoPromptBiometric = true, bool checkPendingAuthRequests = true)
{ {
_appOptions = appOptions; _appOptions = appOptions;
_autoPromptBiometric = autoPromptBiometric; _autoPromptBiometric = autoPromptBiometric;
InitializeComponent(); InitializeComponent();
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>(); _broadcasterService = ServiceContainer.Resolve<IBroadcasterService>();
_vm = BindingContext as LockPageViewModel; _vm = BindingContext as LockPageViewModel;
_vm.CheckPendingAuthRequests = checkPendingAuthRequests;
_vm.Page = this; _vm.Page = this;
_vm.UnlockedAction = () => Device.BeginInvokeOnMainThread(async () => await UnlockedAsync()); _vm.UnlockedAction = () => Device.BeginInvokeOnMainThread(async () => await UnlockedAsync());
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.iOS) if (Device.RuntimePlatform == Device.iOS)
{ {
ToolbarItems.Add(_moreItem); ToolbarItems.Add(_moreItem);
@@ -46,7 +45,7 @@ namespace Bit.App.Pages
{ {
get get
{ {
if (_vm?.PinLock ?? false) if (_vm?.PinEnabled ?? false)
{ {
return _pin; return _pin;
} }
@@ -56,7 +55,7 @@ namespace Bit.App.Pages
public async Task PromptBiometricAfterResumeAsync() public async Task PromptBiometricAfterResumeAsync()
{ {
if (_vm.BiometricLock) if (_vm.BiometricEnabled)
{ {
await Task.Delay(500); await Task.Delay(500);
if (!_promptedAfterResume) if (!_promptedAfterResume)
@@ -93,13 +92,13 @@ namespace Bit.App.Pages
_vm.FocusSecretEntry += PerformFocusSecretEntry; _vm.FocusSecretEntry += PerformFocusSecretEntry;
if (!_vm.BiometricLock) if (!_vm.BiometricEnabled)
{ {
RequestFocus(SecretEntry); RequestFocus(SecretEntry);
} }
else else
{ {
if (_vm.UsingKeyConnector && !_vm.PinLock) if (!_vm.HasMasterPassword && !_vm.PinEnabled)
{ {
_passwordGrid.IsVisible = false; _passwordGrid.IsVisible = false;
_unlockButton.IsVisible = false; _unlockButton.IsVisible = false;

View File

@@ -11,9 +11,8 @@ using Bit.Core.Models.Domain;
using Bit.Core.Models.Request; using Bit.Core.Models.Request;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using CommunityToolkit.Maui.Converters; using Xamarin.CommunityToolkit.Helpers;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
@@ -28,27 +27,27 @@ namespace Bit.App.Pages
private readonly IEnvironmentService _environmentService; private readonly IEnvironmentService _environmentService;
private readonly IStateService _stateService; private readonly IStateService _stateService;
private readonly IBiometricService _biometricService; private readonly IBiometricService _biometricService;
private readonly IKeyConnectorService _keyConnectorService; private readonly IUserVerificationService _userVerificationService;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IWatchDeviceService _watchDeviceService; private readonly IWatchDeviceService _watchDeviceService;
private readonly WeakEventManager<int?> _secretEntryFocusWeakEventManager = new WeakEventManager<int?>(); private readonly WeakEventManager<int?> _secretEntryFocusWeakEventManager = new WeakEventManager<int?>();
private readonly IPolicyService _policyService; private readonly IPolicyService _policyService;
private readonly IPasswordGenerationService _passwordGenerationService; private readonly IPasswordGenerationService _passwordGenerationService;
private IDeviceTrustCryptoService _deviceTrustCryptoService;
private readonly ISyncService _syncService;
private string _email; private string _email;
private string _masterPassword; private string _masterPassword;
private string _pin; private string _pin;
private bool _showPassword; private bool _showPassword;
private bool _pinLock; private PinLockType _pinStatus;
private bool _biometricLock; private bool _pinEnabled;
private bool _biometricEnabled;
private bool _biometricIntegrityValid = true; private bool _biometricIntegrityValid = true;
private bool _biometricButtonVisible; private bool _biometricButtonVisible;
private bool _usingKeyConnector; private bool _hasMasterPassword;
private string _biometricButtonText; private string _biometricButtonText;
private string _loggedInAsText; private string _loggedInAsText;
private string _lockedVerifyText; private string _lockedVerifyText;
private bool _isPinProtected;
private bool _isPinProtectedWithKey;
public LockPageViewModel() public LockPageViewModel()
{ {
@@ -61,11 +60,13 @@ namespace Bit.App.Pages
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService"); _environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService"); _biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService"); _userVerificationService = ServiceContainer.Resolve<IUserVerificationService>();
_logger = ServiceContainer.Resolve<ILogger>("logger"); _logger = ServiceContainer.Resolve<ILogger>("logger");
_watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>(); _watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>();
_policyService = ServiceContainer.Resolve<IPolicyService>(); _policyService = ServiceContainer.Resolve<IPolicyService>();
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(); _passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>();
_deviceTrustCryptoService = ServiceContainer.Resolve<IDeviceTrustCryptoService>();
_syncService = ServiceContainer.Resolve<ISyncService>();
PageTitle = AppResources.VerifyMasterPassword; PageTitle = AppResources.VerifyMasterPassword;
TogglePasswordCommand = new Command(TogglePassword); TogglePasswordCommand = new Command(TogglePassword);
@@ -101,21 +102,21 @@ namespace Bit.App.Pages
}); });
} }
public bool PinLock public bool PinEnabled
{ {
get => _pinLock; get => _pinEnabled;
set => SetProperty(ref _pinLock, value); set => SetProperty(ref _pinEnabled, value);
} }
public bool UsingKeyConnector public bool HasMasterPassword
{ {
get => _usingKeyConnector; get => _hasMasterPassword;
} }
public bool BiometricLock public bool BiometricEnabled
{ {
get => _biometricLock; get => _biometricEnabled;
set => SetProperty(ref _biometricLock, value); set => SetProperty(ref _biometricEnabled, value);
} }
public bool BiometricIntegrityValid public bool BiometricIntegrityValid
@@ -148,6 +149,8 @@ namespace Bit.App.Pages
set => SetProperty(ref _lockedVerifyText, value); set => SetProperty(ref _lockedVerifyText, value);
} }
public bool CheckPendingAuthRequests { get; set; }
public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; } public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; }
public Command SubmitCommand { get; } public Command SubmitCommand { get; }
@@ -163,18 +166,32 @@ namespace Bit.App.Pages
public async Task InitAsync() public async Task InitAsync()
{ {
(_isPinProtected, _isPinProtectedWithKey) = await _vaultTimeoutService.IsPinLockSetAsync(); var pendingRequest = await _stateService.GetPendingAdminAuthRequestAsync();
PinLock = (_isPinProtected && await _stateService.GetPinProtectedKeyAsync() != null) || if (pendingRequest != null && CheckPendingAuthRequests)
_isPinProtectedWithKey;
BiometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasKeyAsync();
// Users with key connector and without biometric or pin has no MP to unlock with
_usingKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
if (_usingKeyConnector && !(BiometricLock || PinLock))
{ {
await _vaultTimeoutService.LogOutAsync(); await _vaultTimeoutService.LogOutAsync();
return; return;
} }
_pinStatus = await _vaultTimeoutService.GetPinLockTypeAsync();
var ephemeralPinSet = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync()
?? await _stateService.GetPinProtectedKeyAsync();
PinEnabled = (_pinStatus == PinLockType.Transient && ephemeralPinSet != null) ||
_pinStatus == PinLockType.Persistent;
BiometricEnabled = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _biometricService.CanUseBiometricsUnlockAsync();
// Users without MP and without biometric or pin has no MP to unlock with
_hasMasterPassword = await _userVerificationService.HasMasterPasswordAsync();
if (await _stateService.IsAuthenticatedAsync()
&& !_hasMasterPassword
&& !BiometricEnabled
&& !PinEnabled)
{
await _vaultTimeoutService.LogOutAsync();
return;
}
_email = await _stateService.GetEmailAsync(); _email = await _stateService.GetEmailAsync();
if (string.IsNullOrWhiteSpace(_email)) if (string.IsNullOrWhiteSpace(_email))
{ {
@@ -189,26 +206,18 @@ namespace Bit.App.Pages
} }
var webVaultHostname = CoreHelpers.GetHostname(webVault); var webVaultHostname = CoreHelpers.GetHostname(webVault);
LoggedInAsText = string.Format(AppResources.LoggedInAsOn, _email, webVaultHostname); LoggedInAsText = string.Format(AppResources.LoggedInAsOn, _email, webVaultHostname);
if (PinLock) if (PinEnabled)
{ {
PageTitle = AppResources.VerifyPIN; PageTitle = AppResources.VerifyPIN;
LockedVerifyText = AppResources.VaultLockedPIN; LockedVerifyText = AppResources.VaultLockedPIN;
} }
else else
{ {
if (_usingKeyConnector) PageTitle = _hasMasterPassword ? AppResources.VerifyMasterPassword : AppResources.UnlockVault;
{ LockedVerifyText = _hasMasterPassword ? AppResources.VaultLockedMasterPassword : AppResources.VaultLockedIdentity;
PageTitle = AppResources.UnlockVault;
LockedVerifyText = AppResources.VaultLockedIdentity;
}
else
{
PageTitle = AppResources.VerifyMasterPassword;
LockedVerifyText = AppResources.VaultLockedMasterPassword;
}
} }
if (BiometricLock) if (BiometricEnabled)
{ {
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync(); BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync();
if (!_biometricIntegrityValid) if (!_biometricIntegrityValid)
@@ -218,7 +227,6 @@ namespace Bit.App.Pages
} }
BiometricButtonVisible = true; BiometricButtonVisible = true;
BiometricButtonText = AppResources.UseBiometricsToUnlock; BiometricButtonText = AppResources.UseBiometricsToUnlock;
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.iOS) if (Device.RuntimePlatform == Device.iOS)
{ {
var supportsFace = await _deviceActionService.SupportsFaceBiometricAsync(); var supportsFace = await _deviceActionService.SupportsFaceBiometricAsync();
@@ -231,14 +239,14 @@ namespace Bit.App.Pages
public async Task SubmitAsync() public async Task SubmitAsync()
{ {
if (PinLock && string.IsNullOrWhiteSpace(Pin)) if (PinEnabled && string.IsNullOrWhiteSpace(Pin))
{ {
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
string.Format(AppResources.ValidationFieldRequired, AppResources.PIN), string.Format(AppResources.ValidationFieldRequired, AppResources.PIN),
AppResources.Ok); AppResources.Ok);
return; return;
} }
if (!PinLock && string.IsNullOrWhiteSpace(MasterPassword)) if (!PinEnabled && string.IsNullOrWhiteSpace(MasterPassword))
{ {
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword), string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword),
@@ -249,34 +257,54 @@ namespace Bit.App.Pages
ShowPassword = false; ShowPassword = false;
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile)); var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
if (PinLock) if (PinEnabled)
{ {
var failed = true; var failed = true;
try try
{ {
if (_isPinProtected) EncString userKeyPin = null;
EncString oldPinProtected = null;
if (_pinStatus == PinLockType.Persistent)
{ {
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email, userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyAsync();
var oldEncryptedKey = await _stateService.GetPinProtectedAsync();
oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null;
}
else if (_pinStatus == PinLockType.Transient)
{
userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync();
oldPinProtected = await _stateService.GetPinProtectedKeyAsync();
}
UserKey userKey;
if (oldPinProtected != null)
{
userKey = await _cryptoService.DecryptAndMigrateOldPinKeyAsync(
_pinStatus == PinLockType.Transient,
Pin,
_email,
kdfConfig, kdfConfig,
await _stateService.GetPinProtectedKeyAsync()); oldPinProtected
var encKey = await _cryptoService.GetEncKeyAsync(key); );
var protectedPin = await _stateService.GetProtectedPinAsync();
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
failed = decPin != Pin;
if (!failed)
{
Pin = string.Empty;
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
await SetKeyAndContinueAsync(key);
}
} }
else else
{ {
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email, kdfConfig); userKey = await _cryptoService.DecryptUserKeyWithPinAsync(
failed = false; Pin,
_email,
kdfConfig,
userKeyPin
);
}
var protectedPin = await _stateService.GetProtectedPinAsync();
var decryptedPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), userKey);
failed = decryptedPin != Pin;
if (!failed)
{
Pin = string.Empty; Pin = string.Empty;
await AppHelpers.ResetInvalidUnlockAttemptsAsync(); await AppHelpers.ResetInvalidUnlockAttemptsAsync();
await SetKeyAndContinueAsync(key); await SetUserKeyAndContinueAsync(userKey);
} }
} }
catch catch
@@ -297,19 +325,21 @@ namespace Bit.App.Pages
} }
else else
{ {
var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdfConfig); var masterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, _email, kdfConfig);
var storedKeyHash = await _cryptoService.GetKeyHashAsync(); var storedKeyHash = await _cryptoService.GetMasterKeyHashAsync();
var passwordValid = false; var passwordValid = false;
MasterPasswordPolicyOptions enforcedMasterPasswordOptions = null; MasterPasswordPolicyOptions enforcedMasterPasswordOptions = null;
if (storedKeyHash != null) if (storedKeyHash != null)
{ {
passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(MasterPassword, key); // Offline unlock possible
passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(MasterPassword, masterKey);
} }
else else
{ {
// Online unlock required
await _deviceActionService.ShowLoadingAsync(AppResources.Loading); await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization); var keyHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, masterKey, HashPurpose.ServerAuthorization);
var request = new PasswordVerificationRequest(); var request = new PasswordVerificationRequest();
request.MasterPasswordHash = keyHash; request.MasterPasswordHash = keyHash;
@@ -318,8 +348,8 @@ namespace Bit.App.Pages
var response = await _apiService.PostAccountVerifyPasswordAsync(request); var response = await _apiService.PostAccountVerifyPasswordAsync(request);
enforcedMasterPasswordOptions = response.MasterPasswordPolicy; enforcedMasterPasswordOptions = response.MasterPasswordPolicy;
passwordValid = true; passwordValid = true;
var localKeyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization); var localKeyHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, masterKey, HashPurpose.LocalAuthorization);
await _cryptoService.SetKeyHashAsync(localKeyHash); await _cryptoService.SetMasterKeyHashAsync(localKeyHash);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -329,15 +359,6 @@ namespace Bit.App.Pages
} }
if (passwordValid) if (passwordValid)
{ {
if (_isPinProtected)
{
var protectedPin = await _stateService.GetProtectedPinAsync();
var encKey = await _cryptoService.GetEncKeyAsync(key);
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email, kdfConfig);
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key.Key, pinKey));
}
if (await RequirePasswordChangeAsync(enforcedMasterPasswordOptions)) if (await RequirePasswordChangeAsync(enforcedMasterPasswordOptions))
{ {
// Save the ForcePasswordResetReason to force a password reset after unlock // Save the ForcePasswordResetReason to force a password reset after unlock
@@ -347,10 +368,13 @@ namespace Bit.App.Pages
MasterPassword = string.Empty; MasterPassword = string.Empty;
await AppHelpers.ResetInvalidUnlockAttemptsAsync(); await AppHelpers.ResetInvalidUnlockAttemptsAsync();
await SetKeyAndContinueAsync(key);
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey);
await _cryptoService.SetMasterKeyAsync(masterKey);
await SetUserKeyAndContinueAsync(userKey);
// Re-enable biometrics // Re-enable biometrics
if (BiometricLock & !BiometricIntegrityValid) if (BiometricEnabled & !BiometricIntegrityValid)
{ {
await _biometricService.SetupBiometricAsync(); await _biometricService.SetupBiometricAsync();
} }
@@ -427,7 +451,7 @@ namespace Bit.App.Pages
public void TogglePassword() public void TogglePassword()
{ {
ShowPassword = !ShowPassword; ShowPassword = !ShowPassword;
var secret = PinLock ? Pin : MasterPassword; var secret = PinEnabled ? Pin : MasterPassword;
_secretEntryFocusWeakEventManager.RaiseEvent(string.IsNullOrEmpty(secret) ? 0 : secret.Length, nameof(FocusSecretEntry)); _secretEntryFocusWeakEventManager.RaiseEvent(string.IsNullOrEmpty(secret) ? 0 : secret.Length, nameof(FocusSecretEntry));
} }
@@ -435,32 +459,35 @@ namespace Bit.App.Pages
{ {
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync(); BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync();
BiometricButtonVisible = BiometricIntegrityValid; BiometricButtonVisible = BiometricIntegrityValid;
if (!BiometricLock || !BiometricIntegrityValid) if (!BiometricEnabled || !BiometricIntegrityValid)
{ {
return; return;
} }
var success = await _platformUtilsService.AuthenticateBiometricAsync(null, var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
PinLock ? AppResources.PIN : AppResources.MasterPassword, PinEnabled ? AppResources.PIN : AppResources.MasterPassword,
() => _secretEntryFocusWeakEventManager.RaiseEvent((int?)null, nameof(FocusSecretEntry))); () => _secretEntryFocusWeakEventManager.RaiseEvent((int?)null, nameof(FocusSecretEntry)));
await _stateService.SetBiometricLockedAsync(!success); await _stateService.SetBiometricLockedAsync(!success);
if (success) if (success)
{ {
await DoContinueAsync(); var userKey = await _cryptoService.GetBiometricUnlockKeyAsync();
await SetUserKeyAndContinueAsync(userKey);
} }
} }
private async Task SetKeyAndContinueAsync(SymmetricCryptoKey key) private async Task SetUserKeyAndContinueAsync(UserKey key)
{ {
var hasKey = await _cryptoService.HasKeyAsync(); var hasKey = await _cryptoService.HasUserKeyAsync();
if (!hasKey) if (!hasKey)
{ {
await _cryptoService.SetKeyAsync(key); await _cryptoService.SetUserKeyAsync(key);
} }
await _deviceTrustCryptoService.TrustDeviceIfNeededAsync();
await DoContinueAsync(); await DoContinueAsync();
} }
private async Task DoContinueAsync() private async Task DoContinueAsync()
{ {
_syncService.FullSyncAsync(false).FireAndForget();
await _stateService.SetBiometricLockedAsync(false); await _stateService.SetBiometricLockedAsync(false);
_watchDeviceService.SyncDataToWatchAsync().FireAndForget(); _watchDeviceService.SyncDataToWatchAsync().FireAndForget();
_messagingService.Send("unlocked"); _messagingService.Send("unlocked");

View File

@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8" ?>
<pages:BaseContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.LoginApproveDevicePage"
xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:u="clr-namespace:Bit.App.Utilities"
x:DataType="pages:LoginApproveDeviceViewModel"
x:Name="_page"
Title="{Binding PageTitle}">
<ContentPage.BindingContext>
<pages:LoginApproveDeviceViewModel />
</ContentPage.BindingContext>
<StackLayout Padding="10, 10">
<StackLayout Padding="5, 10" Orientation="Horizontal">
<StackLayout HorizontalOptions="FillAndExpand">
<Label
StyleClass="text-md"
Text="{u:I18n RememberThisDevice}"/>
<Label
StyleClass="box-sub-label"
Text="{u:I18n TurnOffUsingPublicDevice}"/>
</StackLayout>
<Switch
Scale="0.8"
IsToggled="{Binding RememberThisDevice}"
VerticalOptions="Center"/>
</StackLayout>
<StackLayout Margin="0, 20, 0, 0">
<Button
x:Name="_continue"
Text="{u:I18n Continue}"
StyleClass="btn-primary"
Command="{Binding ContinueCommand}"
IsVisible="{Binding IsNewUser}"/>
<Button
x:Name="_approveWithMyOtherDevice"
Text="{u:I18n ApproveWithMyOtherDevice}"
StyleClass="btn-primary"
Command="{Binding ApproveWithMyOtherDeviceCommand}"
IsVisible="{Binding ApproveWithMyOtherDeviceEnabled}"/>
<Button
x:Name="_requestAdminApproval"
Text="{u:I18n RequestAdminApproval}"
StyleClass="box-button-row"
Command="{Binding RequestAdminApprovalCommand}"
IsVisible="{Binding RequestAdminApprovalEnabled}"/>
<Button
x:Name="_approveWithMasterPassword"
Text="{u:I18n ApproveWithMasterPassword}"
StyleClass="box-button-row"
Command="{Binding ApproveWithMasterPasswordCommand}"
IsVisible="{Binding ApproveWithMasterPasswordEnabled}"/>
<Label
Text="{Binding LoggingInAsText}"
StyleClass="text-sm"
Margin="0,40,0,0"
AutomationId="LoggingInAsLabel"
/>
<Label
Text="{u:I18n NotYou}"
StyleClass="text-md"
HorizontalOptions="Start"
TextColor="{DynamicResource HyperlinkColor}"
AutomationId="NotYouLabel">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding LogoutCommand}" />
</Label.GestureRecognizers>
</Label>
</StackLayout>
</StackLayout>
</pages:BaseContentPage>

View File

@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.App.Models;
using Bit.App.Utilities;
using Bit.Core.Enums;
using Bit.Core.Utilities;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public partial class LoginApproveDevicePage : BaseContentPage
{
private readonly LoginApproveDeviceViewModel _vm;
private readonly AppOptions _appOptions;
public LoginApproveDevicePage(AppOptions appOptions = null)
{
InitializeComponent();
_vm = BindingContext as LoginApproveDeviceViewModel;
_vm.LogInWithMasterPasswordAction = () => StartLogInWithMasterPasswordAsync().FireAndForget();
_vm.LogInWithDeviceAction = () => StartLoginWithDeviceAsync().FireAndForget();
_vm.RequestAdminApprovalAction = () => RequestAdminApprovalAsync().FireAndForget();
_vm.ContinueToVaultAction = () => ContinueToVaultAsync().FireAndForget();
_vm.Page = this;
_appOptions = appOptions;
}
protected override void OnAppearing()
{
_vm.InitAsync();
}
private async Task ContinueToVaultAsync()
{
if (AppHelpers.SetAlternateMainPage(_appOptions))
{
return;
}
var previousPage = await AppHelpers.ClearPreviousPage();
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
}
private async Task StartLogInWithMasterPasswordAsync()
{
var page = new LockPage(_appOptions, checkPendingAuthRequests: false);
await Navigation.PushModalAsync(new NavigationPage(page));
}
private async Task StartLoginWithDeviceAsync()
{
var page = new LoginPasswordlessRequestPage(_vm.Email, AuthRequestType.AuthenticateAndUnlock, _appOptions);
await Navigation.PushModalAsync(new NavigationPage(page));
}
private async Task RequestAdminApprovalAsync()
{
var page = new LoginPasswordlessRequestPage(_vm.Email, AuthRequestType.AdminApproval, _appOptions);
await Navigation.PushModalAsync(new NavigationPage(page));
}
}
}

View File

@@ -0,0 +1,150 @@
using System;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using System.Windows.Input;
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.App.Utilities.AccountManagement;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Domain;
using Bit.Core.Models.Request;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public class LoginApproveDeviceViewModel : BaseViewModel
{
private bool _rememberThisDevice;
private bool _approveWithMyOtherDeviceEnabled;
private bool _requestAdminApprovalEnabled;
private bool _approveWithMasterPasswordEnabled;
private string _email;
private readonly IStateService _stateService;
private readonly IApiService _apiService;
private IDeviceTrustCryptoService _deviceTrustCryptoService;
private readonly IAuthService _authService;
private readonly ISyncService _syncService;
private readonly IMessagingService _messagingService;
public ICommand ApproveWithMyOtherDeviceCommand { get; }
public ICommand RequestAdminApprovalCommand { get; }
public ICommand ApproveWithMasterPasswordCommand { get; }
public ICommand ContinueCommand { get; }
public ICommand LogoutCommand { get; }
public Action LogInWithMasterPasswordAction { get; set; }
public Action LogInWithDeviceAction { get; set; }
public Action RequestAdminApprovalAction { get; set; }
public Action ContinueToVaultAction { get; set; }
public LoginApproveDeviceViewModel()
{
_stateService = ServiceContainer.Resolve<IStateService>();
_apiService = ServiceContainer.Resolve<IApiService>();
_deviceTrustCryptoService = ServiceContainer.Resolve<IDeviceTrustCryptoService>();
_authService = ServiceContainer.Resolve<IAuthService>();
_syncService = ServiceContainer.Resolve<ISyncService>();
_messagingService = ServiceContainer.Resolve<IMessagingService>();
PageTitle = AppResources.LogInInitiated;
RememberThisDevice = true;
ApproveWithMyOtherDeviceCommand = new AsyncCommand(() => SetDeviceTrustAndInvokeAsync(LogInWithDeviceAction),
onException: ex => HandleException(ex),
allowsMultipleExecutions: false);
RequestAdminApprovalCommand = new AsyncCommand(() => SetDeviceTrustAndInvokeAsync(RequestAdminApprovalAction),
onException: ex => HandleException(ex),
allowsMultipleExecutions: false);
ApproveWithMasterPasswordCommand = new AsyncCommand(() => SetDeviceTrustAndInvokeAsync(LogInWithMasterPasswordAction),
onException: ex => HandleException(ex),
allowsMultipleExecutions: false);
ContinueCommand = new AsyncCommand(CreateNewSsoUserAsync,
onException: ex => HandleException(ex),
allowsMultipleExecutions: false);
LogoutCommand = new Command(() => _messagingService.Send(AccountsManagerMessageCommands.LOGOUT));
}
public string LoggingInAsText => string.Format(AppResources.LoggingInAsX, Email);
public bool RememberThisDevice
{
get => _rememberThisDevice;
set => SetProperty(ref _rememberThisDevice, value);
}
public bool ApproveWithMyOtherDeviceEnabled
{
get => _approveWithMyOtherDeviceEnabled;
set => SetProperty(ref _approveWithMyOtherDeviceEnabled, value);
}
public bool RequestAdminApprovalEnabled
{
get => _requestAdminApprovalEnabled;
set => SetProperty(ref _requestAdminApprovalEnabled, value,
additionalPropertyNames: new[] { nameof(IsNewUser) });
}
public bool ApproveWithMasterPasswordEnabled
{
get => _approveWithMasterPasswordEnabled;
set => SetProperty(ref _approveWithMasterPasswordEnabled, value,
additionalPropertyNames: new[] { nameof(IsNewUser) });
}
public bool IsNewUser => !RequestAdminApprovalEnabled && !ApproveWithMasterPasswordEnabled;
public string Email
{
get => _email;
set => SetProperty(ref _email, value, additionalPropertyNames:
new string[] {
nameof(LoggingInAsText)
});
}
public async Task InitAsync()
{
try
{
Email = await _stateService.GetActiveUserEmailAsync();
var decryptOptions = await _stateService.GetAccountDecryptionOptions();
RequestAdminApprovalEnabled = decryptOptions?.TrustedDeviceOption?.HasAdminApproval ?? false;
ApproveWithMasterPasswordEnabled = decryptOptions?.HasMasterPassword ?? false;
ApproveWithMyOtherDeviceEnabled = decryptOptions?.TrustedDeviceOption?.HasLoginApprovingDevice ?? false;
}
catch (Exception ex)
{
HandleException(ex);
}
}
public async Task CreateNewSsoUserAsync()
{
await _authService.CreateNewSsoUserAsync(await _stateService.GetRememberedOrgIdentifierAsync());
if (RememberThisDevice)
{
await _deviceTrustCryptoService.TrustDeviceAsync();
}
_syncService.FullSyncAsync(true).FireAndForget();
await Device.InvokeOnMainThreadAsync(ContinueToVaultAction);
}
private async Task SetDeviceTrustAndInvokeAsync(Action action)
{
await _deviceTrustCryptoService.SetShouldTrustDeviceAsync(RememberThisDevice);
await Device.InvokeOnMainThreadAsync(action);
}
}
}

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<pages:BaseContentPage <pages:BaseContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.LoginPage" x:Class="Bit.App.Pages.LoginPage"
xmlns:pages="clr-namespace:Bit.App.Pages" xmlns:pages="clr-namespace:Bit.App.Pages"

View File

@@ -4,11 +4,11 @@ using Bit.App.Models;
using Bit.App.Utilities; using Bit.App.Utilities;
using Bit.Core; using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel; using Xamarin.CommunityToolkit.ObjectModel;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
@@ -49,7 +49,6 @@ namespace Bit.App.Pages
_vm.Email = email; _vm.Email = email;
MasterPasswordEntry = _masterPassword; MasterPasswordEntry = _masterPassword;
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.iOS) if (Device.RuntimePlatform == Device.iOS)
{ {
ToolbarItems.Add(_moreItem); ToolbarItems.Add(_moreItem);
@@ -95,7 +94,6 @@ namespace Bit.App.Pages
RequestFocus(_masterPassword); RequestFocus(_masterPassword);
_inputFocused = true; _inputFocused = true;
} }
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.Android && !_vm.CanRemoveAccount) if (Device.RuntimePlatform == Device.Android && !_vm.CanRemoveAccount)
{ {
ToolbarItems.Add(_removeAccount); ToolbarItems.Add(_removeAccount);
@@ -138,7 +136,7 @@ namespace Bit.App.Pages
private async Task StartLoginWithDeviceAsync() private async Task StartLoginWithDeviceAsync()
{ {
var page = new LoginPasswordlessRequestPage(_vm.Email, _appOptions); var page = new LoginPasswordlessRequestPage(_vm.Email, AuthRequestType.AuthenticateAndUnlock, _appOptions);
await Navigation.PushModalAsync(new NavigationPage(page)); await Navigation.PushModalAsync(new NavigationPage(page));
} }

View File

@@ -16,8 +16,7 @@ using Bit.Core.Models.View;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel; using Xamarin.CommunityToolkit.ObjectModel;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
@@ -182,7 +181,7 @@ namespace Bit.App.Pages
public async Task LogInAsync(bool showLoading = true, bool checkForExistingAccount = false) public async Task LogInAsync(bool showLoading = true, bool checkForExistingAccount = false)
{ {
if (Microsoft.Maui.Networking.Connectivity.NetworkAccess == Microsoft.Maui.Networking.NetworkAccess.None) if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
{ {
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage, await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
AppResources.InternetConnectionRequiredTitle, AppResources.Ok); AppResources.InternetConnectionRequiredTitle, AppResources.Ok);

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<pages:BaseContentPage <pages:BaseContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.LoginPasswordlessPage" x:Class="Bit.App.Pages.LoginPasswordlessPage"
xmlns:pages="clr-namespace:Bit.App.Pages" xmlns:pages="clr-namespace:Bit.App.Pages"

View File

@@ -1,5 +1,4 @@
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<pages:BaseContentPage <pages:BaseContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.LoginPasswordlessRequestPage" x:Class="Bit.App.Pages.LoginPasswordlessRequestPage"
xmlns:pages="clr-namespace:Bit.App.Pages" xmlns:pages="clr-namespace:Bit.App.Pages"
@@ -21,17 +21,17 @@
<StackLayout <StackLayout
Padding="7, 0, 7, 20"> Padding="7, 0, 7, 20">
<Label <Label
Text="{u:I18n LogInInitiated}" Text="{Binding Title}"
FontSize="Title" FontSize="Title"
FontAttributes="Bold" FontAttributes="Bold"
Margin="0,14,0,21" Margin="0,14,0,21"
AutomationId="LogInInitiatedLabel" /> AutomationId="LogInInitiatedLabel" />
<Label <Label
Text="{u:I18n ANotificationHasBeenSentToYourDevice}" Text="{Binding SubTitle}"
FontSize="Small" FontSize="Small"
Margin="0,0,0,10"/> Margin="0,0,0,10"/>
<Label <Label
Text="{u:I18n PleaseMakeSureYourVaultIsUnlockedAndTheFingerprintPhraseMatchesOnTheOtherDevice}" Text="{Binding Description}"
FontSize="Small" FontSize="Small"
Margin="0,0,0,24"/> Margin="0,0,0,24"/>
<Label <Label
@@ -40,41 +40,39 @@
FontAttributes="Bold"/> FontAttributes="Bold"/>
<controls:MonoLabel <controls:MonoLabel
FormattedText="{Binding FingerprintPhrase}" FormattedText="{Binding FingerprintPhrase}"
FontSize="Medium" FontSize="Small"
TextColor="{DynamicResource FingerprintPhrase}" TextColor="{DynamicResource FingerprintPhrase}"
AutomationId="FingerprintPhraseValue" /> AutomationId="FingerprintPhraseValue" />
<Label <Label
Text="{u:I18n ResendNotification}" Text="{u:I18n ResendNotification}"
StyleClass="text-md" IsVisible="{Binding ResendNotificationVisible}"
StyleClass="text-sm"
FontAttributes="Bold"
HorizontalOptions="Start" HorizontalOptions="Start"
Margin="0,40,0,0" Margin="0,24,0,0"
TextColor="{DynamicResource HyperlinkColor}" TextColor="{DynamicResource HyperlinkColor}"
AutomationId="ResendNotificationButton"> AutomationId="ResendNotificationButton">
<Label.GestureRecognizers> <Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding CreatePasswordlessLoginCommand}" /> <TapGestureRecognizer Command="{Binding CreatePasswordlessLoginCommand}" />
</Label.GestureRecognizers> </Label.GestureRecognizers>
</Label> </Label>
<StackLayout <BoxView
Orientation="Horizontal" HeightRequest="1"
Margin="0,30,0,0"> Margin="0,24,0,24"
<Label Color="{DynamicResource DisabledIconColor}" />
Text="{u:I18n NeedAnotherOption}" <Label
FontSize="Small" Text="{Binding OtherOptions}"
VerticalTextAlignment="End"/> FontSize="Small"/>
<Label <Label
Text="{u:I18n ViewAllLoginOptions}" Text="{u:I18n ViewAllLoginOptions}"
StyleClass="text-md" StyleClass="text-sm"
VerticalTextAlignment="End" FontAttributes="Bold"
VerticalOptions="CenterAndExpand" TextColor="{DynamicResource HyperlinkColor}"
Margin="5, 0" AutomationId="ViewAllLoginOptionsButton">
TextColor="{DynamicResource HyperlinkColor}" <Label.GestureRecognizers>
AutomationId="ViewAllLoginOptionsButton"> <TapGestureRecognizer Command="{Binding CloseCommand}" />
<Label.GestureRecognizers> </Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding CloseCommand}" /> </Label>
</Label.GestureRecognizers>
</Label>
</StackLayout>
</StackLayout> </StackLayout>
</ScrollView> </ScrollView>
</pages:BaseContentPage> </pages:BaseContentPage>

View File

@@ -3,8 +3,8 @@ using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Models; using Bit.App.Models;
using Bit.App.Utilities; using Bit.App.Utilities;
using Microsoft.Maui.Controls; using Bit.Core.Enums;
using Microsoft.Maui; using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
@@ -13,13 +13,14 @@ namespace Bit.App.Pages
private LoginPasswordlessRequestViewModel _vm; private LoginPasswordlessRequestViewModel _vm;
private readonly AppOptions _appOptions; private readonly AppOptions _appOptions;
public LoginPasswordlessRequestPage(string email, AppOptions appOptions = null) public LoginPasswordlessRequestPage(string email, AuthRequestType authRequestType, AppOptions appOptions = null)
{ {
InitializeComponent(); InitializeComponent();
_appOptions = appOptions; _appOptions = appOptions;
_vm = BindingContext as LoginPasswordlessRequestViewModel; _vm = BindingContext as LoginPasswordlessRequestViewModel;
_vm.Page = this; _vm.Page = this;
_vm.Email = email; _vm.Email = email;
_vm.AuthRequestType = authRequestType;
_vm.StartTwoFactorAction = () => Device.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync()); _vm.StartTwoFactorAction = () => Device.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
_vm.LogInSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await LogInSuccessAsync()); _vm.LogInSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await LogInSuccessAsync());
_vm.UpdateTempPasswordAction = () => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync()); _vm.UpdateTempPasswordAction = () => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -12,11 +13,12 @@ using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models.Domain; using Bit.Core.Models.Domain;
using Bit.Core.Models.Response;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel; using Xamarin.CommunityToolkit.ObjectModel;
using Microsoft.Maui.Controls; using Xamarin.Essentials;
using Microsoft.Maui; using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
@@ -32,6 +34,9 @@ namespace Bit.App.Pages
private IPlatformUtilsService _platformUtilsService; private IPlatformUtilsService _platformUtilsService;
private IEnvironmentService _environmentService; private IEnvironmentService _environmentService;
private ILogger _logger; private ILogger _logger;
private IDeviceTrustCryptoService _deviceTrustCryptoService;
private readonly ICryptoFunctionService _cryptoFunctionService;
private readonly ICryptoService _cryptoService;
protected override II18nService i18nService => _i18nService; protected override II18nService i18nService => _i18nService;
protected override IEnvironmentService environmentService => _environmentService; protected override IEnvironmentService environmentService => _environmentService;
@@ -44,6 +49,7 @@ namespace Bit.App.Pages
private string _email; private string _email;
private string _requestId; private string _requestId;
private string _requestAccessCode; private string _requestAccessCode;
private AuthRequestType _authRequestType;
// Item1 publicKey, Item2 privateKey // Item1 publicKey, Item2 privateKey
private Tuple<byte[], byte[]> _requestKeyPair; private Tuple<byte[], byte[]> _requestKeyPair;
@@ -57,8 +63,9 @@ namespace Bit.App.Pages
_i18nService = ServiceContainer.Resolve<II18nService>(); _i18nService = ServiceContainer.Resolve<II18nService>();
_stateService = ServiceContainer.Resolve<IStateService>(); _stateService = ServiceContainer.Resolve<IStateService>();
_logger = ServiceContainer.Resolve<ILogger>(); _logger = ServiceContainer.Resolve<ILogger>();
_deviceTrustCryptoService = ServiceContainer.Resolve<IDeviceTrustCryptoService>();
PageTitle = AppResources.LogInWithAnotherDevice; _cryptoFunctionService = ServiceContainer.Resolve<ICryptoFunctionService>();
_cryptoService = ServiceContainer.Resolve<ICryptoService>();
CreatePasswordlessLoginCommand = new AsyncCommand(CreatePasswordlessLoginAsync, CreatePasswordlessLoginCommand = new AsyncCommand(CreatePasswordlessLoginAsync,
onException: ex => HandleException(ex), onException: ex => HandleException(ex),
@@ -77,6 +84,86 @@ namespace Bit.App.Pages
public ICommand CreatePasswordlessLoginCommand { get; } public ICommand CreatePasswordlessLoginCommand { get; }
public ICommand CloseCommand { get; } public ICommand CloseCommand { get; }
public string HeaderTitle
{
get
{
switch (_authRequestType)
{
case AuthRequestType.AuthenticateAndUnlock:
return AppResources.LogInWithDevice;
case AuthRequestType.AdminApproval:
return AppResources.LogInInitiated;
default:
return string.Empty;
};
}
}
public string Title
{
get
{
switch (_authRequestType)
{
case AuthRequestType.AuthenticateAndUnlock:
return AppResources.LogInInitiated;
case AuthRequestType.AdminApproval:
return AppResources.AdminApprovalRequested;
default:
return string.Empty;
};
}
}
public string SubTitle
{
get
{
switch (_authRequestType)
{
case AuthRequestType.AuthenticateAndUnlock:
return AppResources.ANotificationHasBeenSentToYourDevice;
case AuthRequestType.AdminApproval:
return AppResources.YourRequestHasBeenSentToYourAdmin;
default:
return string.Empty;
};
}
}
public string Description
{
get
{
switch (_authRequestType)
{
case AuthRequestType.AuthenticateAndUnlock:
return AppResources.PleaseMakeSureYourVaultIsUnlockedAndTheFingerprintPhraseMatchesOnTheOtherDevice;
case AuthRequestType.AdminApproval:
return AppResources.YouWillBeNotifiedOnceApproved;
default:
return string.Empty;
};
}
}
public string OtherOptions
{
get
{
switch (_authRequestType)
{
case AuthRequestType.AuthenticateAndUnlock:
return AppResources.LogInWithDeviceMustBeSetUpInTheSettingsOfTheBitwardenAppNeedAnotherOption;
case AuthRequestType.AdminApproval:
return AppResources.TroubleLoggingIn;
default:
return string.Empty;
};
}
}
public string FingerprintPhrase public string FingerprintPhrase
{ {
get => _fingerprintPhrase; get => _fingerprintPhrase;
@@ -89,6 +176,25 @@ namespace Bit.App.Pages
set => SetProperty(ref _email, value); set => SetProperty(ref _email, value);
} }
public AuthRequestType AuthRequestType
{
get => _authRequestType;
set
{
SetProperty(ref _authRequestType, value, additionalPropertyNames: new string[]
{
nameof(Title),
nameof(SubTitle),
nameof(Description),
nameof(OtherOptions),
nameof(ResendNotificationVisible)
});
PageTitle = HeaderTitle;
}
}
public bool ResendNotificationVisible => AuthRequestType == AuthRequestType.AuthenticateAndUnlock;
public void StartCheckLoginRequestStatus() public void StartCheckLoginRequestStatus()
{ {
try try
@@ -119,14 +225,22 @@ namespace Bit.App.Pages
private async Task CheckLoginRequestStatus() private async Task CheckLoginRequestStatus()
{ {
if (string.IsNullOrEmpty(_requestId) || string.IsNullOrEmpty(_requestAccessCode)) if (string.IsNullOrEmpty(_requestId))
{ {
return; return;
} }
try try
{ {
var response = await _authService.GetPasswordlessLoginResponseAsync(_requestId, _requestAccessCode); PasswordlessLoginResponse response = null;
if (await _stateService.IsAuthenticatedAsync())
{
response = await _authService.GetPasswordlessLoginRequestByIdAsync(_requestId);
}
else
{
response = await _authService.GetPasswordlessLoginResquestAsync(_requestId, _requestAccessCode);
}
if (response.RequestApproved == null || !response.RequestApproved.Value) if (response.RequestApproved == null || !response.RequestApproved.Value)
{ {
@@ -138,6 +252,12 @@ namespace Bit.App.Pages
var authResult = await _authService.LogInPasswordlessAsync(Email, _requestAccessCode, _requestId, _requestKeyPair.Item2, response.Key, response.MasterPasswordHash); var authResult = await _authService.LogInPasswordlessAsync(Email, _requestAccessCode, _requestId, _requestKeyPair.Item2, response.Key, response.MasterPasswordHash);
await AppHelpers.ResetInvalidUnlockAttemptsAsync(); await AppHelpers.ResetInvalidUnlockAttemptsAsync();
if (authResult == null && await _stateService.IsAuthenticatedAsync())
{
await HandleLoginCompleteAsync();
return;
}
if (await HandleCaptchaAsync(authResult.CaptchaSiteKey, authResult.CaptchaNeeded, CheckLoginRequestStatus)) if (await HandleCaptchaAsync(authResult.CaptchaSiteKey, authResult.CaptchaNeeded, CheckLoginRequestStatus))
{ {
return; return;
@@ -153,8 +273,7 @@ namespace Bit.App.Pages
} }
else else
{ {
_syncService.FullSyncAsync(true).FireAndForget(); await HandleLoginCompleteAsync();
LogInSuccessAction?.Invoke();
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -164,25 +283,70 @@ namespace Bit.App.Pages
} }
} }
private async Task HandleLoginCompleteAsync()
{
await _stateService.SetPendingAdminAuthRequestAsync(null);
_syncService.FullSyncAsync(true).FireAndForget();
LogInSuccessAction?.Invoke();
}
private async Task CreatePasswordlessLoginAsync() private async Task CreatePasswordlessLoginAsync()
{ {
await Device.InvokeOnMainThreadAsync(() => _deviceActionService.ShowLoadingAsync(AppResources.Loading)); await Device.InvokeOnMainThreadAsync(() => _deviceActionService.ShowLoadingAsync(AppResources.Loading));
var response = await _authService.PasswordlessCreateLoginRequestAsync(_email); PasswordlessLoginResponse response = null;
if (response != null) var pendingRequest = await _stateService.GetPendingAdminAuthRequestAsync();
if (pendingRequest != null && _authRequestType == AuthRequestType.AdminApproval)
{ {
FingerprintPhrase = response.FingerprintPhrase; response = await _authService.GetPasswordlessLoginRequestByIdAsync(pendingRequest.Id);
_requestId = response.Id; if (response == null || (response.IsAnswered && !response.RequestApproved.Value))
_requestAccessCode = response.RequestAccessCode; {
_requestKeyPair = response.RequestKeyPair; // handle pending auth request not valid remove it from state
await _stateService.SetPendingAdminAuthRequestAsync(null);
pendingRequest = null;
response = null;
}
else
{
// Derive pubKey from privKey in state to avoid MITM attacks
// Also generate FingerprintPhrase locally for the same reason
var derivedPublicKey = await _cryptoFunctionService.RsaExtractPublicKeyAsync(pendingRequest.PrivateKey);
response.FingerprintPhrase = string.Join("-", await _cryptoService.GetFingerprintAsync(Email, derivedPublicKey));
response.RequestKeyPair = new Tuple<byte[], byte[]>(derivedPublicKey, pendingRequest.PrivateKey);
}
} }
if (response == null)
{
response = await _authService.PasswordlessCreateLoginRequestAsync(_email, AuthRequestType);
}
await HandlePasswordlessLoginAsync(response, pendingRequest == null && _authRequestType == AuthRequestType.AdminApproval);
await _deviceActionService.HideLoadingAsync(); await _deviceActionService.HideLoadingAsync();
} }
private async Task HandlePasswordlessLoginAsync(PasswordlessLoginResponse response, bool createPendingAdminRequest)
{
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
if (createPendingAdminRequest)
{
var pendingAuthRequest = new PendingAdminAuthRequest { Id = response.Id, PrivateKey = response.RequestKeyPair.Item2 };
await _stateService.SetPendingAdminAuthRequestAsync(pendingAuthRequest);
}
FingerprintPhrase = response.FingerprintPhrase;
_requestId = response.Id;
_requestAccessCode = response.RequestAccessCode;
_requestKeyPair = response.RequestKeyPair;
}
private void HandleException(Exception ex) private void HandleException(Exception ex)
{ {
Microsoft.Maui.ApplicationModel.MainThread.InvokeOnMainThreadAsync(async () => Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
{ {
await _deviceActionService.HideLoadingAsync(); await _deviceActionService.HideLoadingAsync();
await _platformUtilsService.ShowDialogAsync(AppResources.GenericErrorMessage); await _platformUtilsService.ShowDialogAsync(AppResources.GenericErrorMessage);

View File

@@ -13,8 +13,7 @@ using Bit.Core.Enums;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel; using Xamarin.CommunityToolkit.ObjectModel;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<pages:BaseContentPage <pages:BaseContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.LoginSsoPage" x:Class="Bit.App.Pages.LoginSsoPage"
xmlns:pages="clr-namespace:Bit.App.Pages" xmlns:pages="clr-namespace:Bit.App.Pages"

View File

@@ -4,8 +4,7 @@ using Bit.App.Models;
using Bit.App.Utilities; using Bit.App.Utilities;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
@@ -30,11 +29,12 @@ namespace Bit.App.Pages
_vm.SsoAuthSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await SsoAuthSuccessAsync()); _vm.SsoAuthSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await SsoAuthSuccessAsync());
_vm.UpdateTempPasswordAction = _vm.UpdateTempPasswordAction =
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync()); () => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
_vm.StartDeviceApprovalOptionsAction =
() => Device.BeginInvokeOnMainThread(async () => await StartDeviceApprovalOptionsAsync());
_vm.CloseAction = async () => _vm.CloseAction = async () =>
{ {
await Navigation.PopModalAsync(); await Navigation.PopModalAsync();
}; };
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.Android) if (Device.RuntimePlatform == Device.Android)
{ {
ToolbarItems.RemoveAt(0); ToolbarItems.RemoveAt(0);
@@ -108,10 +108,17 @@ namespace Bit.App.Pages
await Navigation.PushModalAsync(new NavigationPage(page)); await Navigation.PushModalAsync(new NavigationPage(page));
} }
private async Task StartDeviceApprovalOptionsAsync()
{
var page = new LoginApproveDevicePage();
await Navigation.PushModalAsync(new NavigationPage(page));
}
private async Task SsoAuthSuccessAsync() private async Task SsoAuthSuccessAsync()
{ {
RestoreAppOptionsFromCopy(); RestoreAppOptionsFromCopy();
await AppHelpers.ClearPreviousPage(); await AppHelpers.ClearPreviousPage();
if (await _vaultTimeoutService.IsLockedAsync()) if (await _vaultTimeoutService.IsLockedAsync())
{ {
Application.Current.MainPage = new NavigationPage(new LockPage(_appOptions)); Application.Current.MainPage = new NavigationPage(new LockPage(_appOptions));

View File

@@ -9,8 +9,10 @@ using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Models.Domain; using Bit.Core.Models.Domain;
using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel; using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Essentials;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
@@ -28,6 +30,8 @@ namespace Bit.App.Pages
private readonly IStateService _stateService; private readonly IStateService _stateService;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IOrganizationService _organizationService; private readonly IOrganizationService _organizationService;
private readonly IDeviceTrustCryptoService _deviceTrustCryptoService;
private readonly ICryptoService _cryptoService;
private string _orgIdentifier; private string _orgIdentifier;
@@ -44,7 +48,8 @@ namespace Bit.App.Pages
_stateService = ServiceContainer.Resolve<IStateService>("stateService"); _stateService = ServiceContainer.Resolve<IStateService>("stateService");
_logger = ServiceContainer.Resolve<ILogger>("logger"); _logger = ServiceContainer.Resolve<ILogger>("logger");
_organizationService = ServiceContainer.Resolve<IOrganizationService>(); _organizationService = ServiceContainer.Resolve<IOrganizationService>();
_deviceTrustCryptoService = ServiceContainer.Resolve<IDeviceTrustCryptoService>();
_cryptoService = ServiceContainer.Resolve<ICryptoService>();
PageTitle = AppResources.Bitwarden; PageTitle = AppResources.Bitwarden;
LogInCommand = new AsyncCommand(LogInAsync, allowsMultipleExecutions: false); LogInCommand = new AsyncCommand(LogInAsync, allowsMultipleExecutions: false);
@@ -60,6 +65,7 @@ namespace Bit.App.Pages
public Action StartTwoFactorAction { get; set; } public Action StartTwoFactorAction { get; set; }
public Action StartSetPasswordAction { get; set; } public Action StartSetPasswordAction { get; set; }
public Action SsoAuthSuccessAction { get; set; } public Action SsoAuthSuccessAction { get; set; }
public Action StartDeviceApprovalOptionsAction { get; set; }
public Action CloseAction { get; set; } public Action CloseAction { get; set; }
public Action UpdateTempPasswordAction { get; set; } public Action UpdateTempPasswordAction { get; set; }
@@ -143,7 +149,6 @@ namespace Bit.App.Pages
authResult = await WebAuthenticator.AuthenticateAsync(new Uri(url), authResult = await WebAuthenticator.AuthenticateAsync(new Uri(url),
new Uri(REDIRECT_URI)); new Uri(REDIRECT_URI));
var code = GetResultCode(authResult, state); var code = GetResultCode(authResult, state);
if (!string.IsNullOrEmpty(code)) if (!string.IsNullOrEmpty(code))
{ {
@@ -196,28 +201,89 @@ namespace Bit.App.Pages
try try
{ {
var response = await _authService.LogInSsoAsync(code, codeVerifier, REDIRECT_URI, orgId); var response = await _authService.LogInSsoAsync(code, codeVerifier, REDIRECT_URI, orgId);
var decryptOptions = await _stateService.GetAccountDecryptionOptions();
await AppHelpers.ResetInvalidUnlockAttemptsAsync(); await AppHelpers.ResetInvalidUnlockAttemptsAsync();
await _stateService.SetRememberedOrgIdentifierAsync(OrgIdentifier); await _stateService.SetRememberedOrgIdentifierAsync(OrgIdentifier);
await _deviceActionService.HideLoadingAsync(); await _deviceActionService.HideLoadingAsync();
if (response.TwoFactor) if (response.TwoFactor)
{ {
StartTwoFactorAction?.Invoke(); StartTwoFactorAction?.Invoke();
return;
} }
else if (response.ResetMasterPassword)
if (decryptOptions?.TrustedDeviceOption != null)
{
var pendingRequest = await _stateService.GetPendingAdminAuthRequestAsync();
// If user doesn't have a MP, but has reset password permission, they must set a MP
if (!decryptOptions.HasMasterPassword &&
decryptOptions.TrustedDeviceOption.HasManageResetPasswordPermission)
{
StartSetPasswordAction?.Invoke();
}
else if (response.ForcePasswordReset)
{
UpdateTempPasswordAction?.Invoke();
}
else if (await _deviceTrustCryptoService.IsDeviceTrustedAsync())
{
if (decryptOptions.TrustedDeviceOption.EncryptedPrivateKey == null && decryptOptions.TrustedDeviceOption.EncryptedUserKey == null)
{
await _deviceTrustCryptoService.RemoveTrustedDeviceAsync();
StartDeviceApprovalOptionsAction?.Invoke();
}
else
{
_syncService.FullSyncAsync(true).FireAndForget();
SsoAuthSuccessAction?.Invoke();
}
}
else if (pendingRequest != null)
{
var authRequest = await _authService.GetPasswordlessLoginRequestByIdAsync(pendingRequest.Id);
if (authRequest != null && authRequest.RequestApproved != null && authRequest.RequestApproved.Value)
{
var authResult = await _authService.LogInPasswordlessAsync(await _stateService.GetActiveUserEmailAsync(), authRequest.RequestAccessCode, pendingRequest.Id, pendingRequest.PrivateKey, authRequest.Key, authRequest.MasterPasswordHash);
if (authResult == null && await _stateService.IsAuthenticatedAsync())
{
await Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(
() => _platformUtilsService.ShowToast("info", null, AppResources.LoginApproved));
await _stateService.SetPendingAdminAuthRequestAsync(null);
_syncService.FullSyncAsync(true).FireAndForget();
SsoAuthSuccessAction?.Invoke();
}
}
else
{
await _stateService.SetPendingAdminAuthRequestAsync(null);
StartDeviceApprovalOptionsAction?.Invoke();
}
}
else
{
StartDeviceApprovalOptionsAction?.Invoke();
}
return;
}
// In the standard, non TDE case, a user must set password if they don't
// have one and they aren't using key connector.
// Note: TDE & Key connector are mutually exclusive org config options.
if (response.ResetMasterPassword || (decryptOptions?.RequireSetPassword ?? false))
{ {
StartSetPasswordAction?.Invoke(); StartSetPasswordAction?.Invoke();
return;
} }
else if (response.ForcePasswordReset)
if (response.ForcePasswordReset)
{ {
UpdateTempPasswordAction?.Invoke(); UpdateTempPasswordAction?.Invoke();
return;
} }
else
{ _syncService.FullSyncAsync(true).FireAndForget();
var task = Task.Run(async () => await _syncService.FullSyncAsync(true)); SsoAuthSuccessAction?.Invoke();
SsoAuthSuccessAction?.Invoke();
}
} }
catch (Exception e) catch (Exception)
{ {
await _deviceActionService.HideLoadingAsync(); await _deviceActionService.HideLoadingAsync();
await _platformUtilsService.ShowDialogAsync(AppResources.LoginSsoError, await _platformUtilsService.ShowDialogAsync(AppResources.LoginSsoError,

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<pages:BaseContentPage <pages:BaseContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.RegisterPage" x:Class="Bit.App.Pages.RegisterPage"
xmlns:pages="clr-namespace:Bit.App.Pages" xmlns:pages="clr-namespace:Bit.App.Pages"

View File

@@ -1,7 +1,6 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
@@ -23,7 +22,6 @@ namespace Bit.App.Pages
}; };
MasterPasswordEntry = _masterPassword; MasterPasswordEntry = _masterPassword;
ConfirmMasterPasswordEntry = _confirmMasterPassword; ConfirmMasterPasswordEntry = _confirmMasterPassword;
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.Android) if (Device.RuntimePlatform == Device.Android)
{ {
ToolbarItems.RemoveAt(0); ToolbarItems.RemoveAt(0);

View File

@@ -13,8 +13,7 @@ using Bit.Core.Exceptions;
using Bit.Core.Models.Request; using Bit.Core.Models.Request;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Microsoft.Maui.Controls; using Xamarin.Forms;
using Microsoft.Maui;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
@@ -119,7 +118,7 @@ namespace Bit.App.Pages
public async Task SubmitAsync(bool showLoading = true) public async Task SubmitAsync(bool showLoading = true)
{ {
if (Microsoft.Maui.Networking.Connectivity.NetworkAccess == Microsoft.Maui.Networking.NetworkAccess.None) if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
{ {
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage, await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
AppResources.InternetConnectionRequiredTitle, AppResources.Ok); AppResources.InternetConnectionRequiredTitle, AppResources.Ok);
@@ -178,25 +177,25 @@ namespace Bit.App.Pages
Name = string.IsNullOrWhiteSpace(Name) ? null : Name; Name = string.IsNullOrWhiteSpace(Name) ? null : Name;
Email = Email.Trim().ToLower(); Email = Email.Trim().ToLower();
var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null); var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null);
var key = await _cryptoService.MakeKeyAsync(MasterPassword, Email, kdfConfig); var newMasterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, Email, kdfConfig);
var encKey = await _cryptoService.MakeEncKeyAsync(key); var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey);
var hashedPassword = await _cryptoService.HashPasswordAsync(MasterPassword, key); var hashedPassword = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey);
var keys = await _cryptoService.MakeKeyPairAsync(encKey.Item1); var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey);
var request = new RegisterRequest var request = new RegisterRequest
{ {
Email = Email, Email = Email,
Name = Name, Name = Name,
MasterPasswordHash = hashedPassword, MasterPasswordHash = hashedPassword,
MasterPasswordHint = Hint, MasterPasswordHint = Hint,
Key = encKey.Item2.EncryptedString, Key = newProtectedUserKey.EncryptedString,
Kdf = kdfConfig.Type, Kdf = kdfConfig.Type,
KdfIterations = kdfConfig.Iterations, KdfIterations = kdfConfig.Iterations,
KdfMemory = kdfConfig.Memory, KdfMemory = kdfConfig.Memory,
KdfParallelism = kdfConfig.Parallelism, KdfParallelism = kdfConfig.Parallelism,
Keys = new KeysRequest Keys = new KeysRequest
{ {
PublicKey = keys.Item1, PublicKey = newPublicKey,
EncryptedPrivateKey = keys.Item2.EncryptedString EncryptedPrivateKey = newProtectedPrivateKey.EncryptedString
}, },
CaptchaResponse = _captchaToken, CaptchaResponse = _captchaToken,
}; };

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<pages:BaseContentPage <pages:BaseContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.RemoveMasterPasswordPage" x:Class="Bit.App.Pages.RemoveMasterPasswordPage"
xmlns:pages="clr-namespace:Bit.App.Pages" xmlns:pages="clr-namespace:Bit.App.Pages"

Some files were not shown because too many files have changed in this diff Show More