1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-15 07:43:37 +00:00

[KeyConnector] Add support for key connector OTP (#1633)

* initial commit
- add UsesKeyConnector to UserService
- add models
- begin work on authentication

* finish auth workflow for key connector sso login
- finish api call for get user key
- start api calls for posts to key connector

* Bypass lock page if already unlocked

* Move logic to KeyConnectorService, log out if no pin or biometric is set

* Disable password reprompt when using key connector

* hide password reprompt checkbox when editing or adding cipher

* add PostUserKey and PostSetKeyConnector calls

* add ConvertMasterPasswordPage

* add functionality to RemoveMasterPasswordPage
- rename Convert to Remove

* Hide Change Master Password button if using key connector

* Add OTP verification for export component

* Update src/App/Pages/Vault/AddEditPage.xaml.cs

Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>

* remove toolbar item "close"

* Update src/Core/Models/Request/KeyConnectorUserKeyRequest.cs

Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>

* remove new line in resource string
- format warning as two labels
- set label in code behind for loading simultaneously

* implement GetAndSetKey in KeyConnectorService
- ignore EnvironmentService call

* remove unnecesary orgIdentifier

* move RemoveMasterPasswordPage call to LockPage

* add spacing to export vault page

* log out if no PIN or bio on lock page with key connector

* Delete excessive whitespace

* Delete excessive whitespace

* Change capitalisation of OTP

* add default value to models for backwards compatibility

* remove this keyword

* actually handle exceptions

* move RemoveMasterPasswordPage to TabPage using messaging service

* add minor improvements

* remove 'this.'

Co-authored-by: Hinton <oscar@oscarhinton.com>
Co-authored-by: Thomas Rittson <trittson@bitwarden.com>
Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
This commit is contained in:
Jake Fink
2021-11-10 20:46:48 -05:00
committed by GitHub
parent 90b62d61ae
commit 13869b5a1b
40 changed files with 869 additions and 74 deletions

View File

@@ -12,6 +12,7 @@ namespace Bit.Core.Services
public class AuthService : IAuthService
{
private readonly ICryptoService _cryptoService;
private readonly ICryptoFunctionService _cryptoFunctionService;
private readonly IApiService _apiService;
private readonly IUserService _userService;
private readonly ITokenService _tokenService;
@@ -20,12 +21,14 @@ namespace Bit.Core.Services
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IMessagingService _messagingService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly IKeyConnectorService _keyConnectorService;
private readonly bool _setCryptoKeys;
private SymmetricCryptoKey _key;
public AuthService(
ICryptoService cryptoService,
ICryptoFunctionService cryptoFunctionService,
IApiService apiService,
IUserService userService,
ITokenService tokenService,
@@ -34,9 +37,11 @@ namespace Bit.Core.Services
IPlatformUtilsService platformUtilsService,
IMessagingService messagingService,
IVaultTimeoutService vaultTimeoutService,
IKeyConnectorService keyConnectorService,
bool setCryptoKeys = true)
{
_cryptoService = cryptoService;
_cryptoFunctionService = cryptoFunctionService;
_apiService = apiService;
_userService = userService;
_tokenService = tokenService;
@@ -45,6 +50,7 @@ namespace Bit.Core.Services
_platformUtilsService = platformUtilsService;
_messagingService = messagingService;
_vaultTimeoutService = vaultTimeoutService;
_keyConnectorService = keyConnectorService;
_setCryptoKeys = setCryptoKeys;
TwoFactorProviders = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
@@ -275,7 +281,7 @@ namespace Bit.Core.Services
private async Task<AuthResult> LogInHelperAsync(string email, string hashedPassword, string localHashedPassword,
string code, string codeVerifier, string redirectUrl, SymmetricCryptoKey key,
TwoFactorProviderType? twoFactorProvider = null, string twoFactorToken = null, bool? remember = null,
string captchaToken = null)
string captchaToken = null, string orgId = null)
{
var storedTwoFactorToken = await _tokenService.GetTwoFactorTokenAsync(email);
var appId = await _appIdService.GetAppIdAsync();
@@ -353,27 +359,75 @@ namespace Bit.Core.Services
tokenResponse.Kdf, tokenResponse.KdfIterations);
if (_setCryptoKeys)
{
await _cryptoService.SetKeyAsync(key);
await _cryptoService.SetKeyHashAsync(localHashedPassword);
await _cryptoService.SetEncKeyAsync(tokenResponse.Key);
// User doesn't have a key pair yet (old account), let's generate one for them.
if (tokenResponse.PrivateKey == null)
if (key != null)
{
try
{
var keyPair = await _cryptoService.MakeKeyPairAsync();
await _apiService.PostAccountKeysAsync(new KeysRequest
{
PublicKey = keyPair.Item1,
EncryptedPrivateKey = keyPair.Item2.EncryptedString
});
tokenResponse.PrivateKey = keyPair.Item2.EncryptedString;
}
catch { }
await _cryptoService.SetKeyAsync(key);
}
if (localHashedPassword != null)
{
await _cryptoService.SetKeyHashAsync(localHashedPassword);
}
if (code == null || tokenResponse.Key != null)
{
if (tokenResponse.KeyConnectorUrl != null)
{
await _keyConnectorService.GetAndSetKey(tokenResponse.KeyConnectorUrl);
}
await _cryptoService.SetEncKeyAsync(tokenResponse.Key);
// User doesn't have a key pair yet (old account), let's generate one for them.
if (tokenResponse.PrivateKey == null)
{
try
{
var keyPair = await _cryptoService.MakeKeyPairAsync();
await _apiService.PostAccountKeysAsync(new KeysRequest
{
PublicKey = keyPair.Item1,
EncryptedPrivateKey = keyPair.Item2.EncryptedString
});
tokenResponse.PrivateKey = keyPair.Item2.EncryptedString;
}
catch { }
}
await _cryptoService.SetEncPrivateKeyAsync(tokenResponse.PrivateKey);
}
else if (tokenResponse.KeyConnectorUrl != null)
{
// SSO Key Connector Onboarding
var password = await _cryptoFunctionService.RandomBytesAsync(64);
var k = await _cryptoService.MakeKeyAsync(Convert.ToBase64String(password), _tokenService.GetEmail(), tokenResponse.Kdf, tokenResponse.KdfIterations);
var keyConnectorRequest = new KeyConnectorUserKeyRequest(k.EncKeyB64);
await _cryptoService.SetKeyAsync(k);
var encKey = await _cryptoService.MakeEncKeyAsync(k);
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
var keyPair = await _cryptoService.MakeKeyPairAsync();
try
{
await _apiService.PostUserKeyToKeyConnector(tokenResponse.KeyConnectorUrl, keyConnectorRequest);
}
catch (Exception e)
{
throw new Exception("Unable to reach Key Connector", e);
}
var keys = new KeysRequest
{
PublicKey = keyPair.Item1,
EncryptedPrivateKey = keyPair.Item2.EncryptedString
};
var setPasswordRequest = new SetKeyConnectorKeyRequest(
encKey.Item2.EncryptedString, keys, tokenResponse.Kdf, tokenResponse.KdfIterations, orgId
);
await _apiService.PostSetKeyConnectorKey(setPasswordRequest);
}
await _cryptoService.SetEncPrivateKeyAsync(tokenResponse.PrivateKey);
}
_vaultTimeoutService.BiometricLocked = false;