mirror of
https://github.com/bitwarden/android.git
synced 2024-12-25 02:18:27 +03:00
[SG-816] Get all login requests and pick the most recent (#2191)
* [SG-816] Get all login requests anfd pick the most recent * [SG-816] Add check if active user has approve login with device active * [SG-816] Build fix. Fix response model. * [SG-816] Move code to sync service
This commit is contained in:
parent
0992a989d4
commit
693a4ef776
9 changed files with 75 additions and 5 deletions
|
@ -147,7 +147,6 @@ namespace Bit.App
|
|||
}
|
||||
else if (message.Command == Constants.PasswordlessLoginRequestKey
|
||||
|| message.Command == "unlocked"
|
||||
|| message.Command == "syncCompleted"
|
||||
|| message.Command == AccountsManagerMessageCommands.ACCOUNT_SWITCH_COMPLETED)
|
||||
{
|
||||
lock (_processingLoginRequestLock)
|
||||
|
@ -209,7 +208,7 @@ namespace Bit.App
|
|||
});
|
||||
await _stateService.SetPasswordlessLoginNotificationAsync(null);
|
||||
_pushNotificationService.DismissLocalNotification(Constants.PasswordlessNotificationId);
|
||||
if (loginRequestData.CreationDate.ToUniversalTime().AddMinutes(Constants.PasswordlessNotificationTimeoutInMinutes) > DateTime.UtcNow)
|
||||
if (!loginRequestData.IsExpired)
|
||||
{
|
||||
await Device.InvokeOnMainThreadAsync(() => Application.Current.MainPage.Navigation.PushModalAsync(new NavigationPage(page)));
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ namespace Bit.App.Pages
|
|||
private async Task UpdateRequestTime()
|
||||
{
|
||||
TriggerPropertyChanged(nameof(TimeOfRequestText));
|
||||
if (DateTime.UtcNow > LoginRequest?.RequestDate.ToUniversalTime().AddMinutes(Constants.PasswordlessNotificationTimeoutInMinutes))
|
||||
if (LoginRequest?.IsExpired ?? false)
|
||||
{
|
||||
StopRequestTimeUpdater();
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.LoginRequestHasAlreadyExpired);
|
||||
|
@ -110,7 +110,7 @@ namespace Bit.App.Pages
|
|||
|
||||
private async Task PasswordlessLoginAsync(bool approveRequest)
|
||||
{
|
||||
if (LoginRequest.RequestDate.ToUniversalTime().AddMinutes(Constants.PasswordlessNotificationTimeoutInMinutes) <= DateTime.UtcNow)
|
||||
if (LoginRequest.IsExpired)
|
||||
{
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.LoginRequestHasAlreadyExpired);
|
||||
await Page.Navigation.PopModalAsync();
|
||||
|
@ -179,5 +179,7 @@ namespace Bit.App.Pages
|
|||
public string DeviceType { get; set; }
|
||||
|
||||
public string IpAddress { get; set; }
|
||||
|
||||
public bool IsExpired => RequestDate.ToUniversalTime().AddMinutes(Constants.PasswordlessNotificationTimeoutInMinutes) < DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,6 +83,7 @@ namespace Bit.Core.Abstractions
|
|||
Task<SendResponse> PutSendAsync(string id, SendRequest request);
|
||||
Task<SendResponse> PutSendRemovePasswordAsync(string id);
|
||||
Task DeleteSendAsync(string id);
|
||||
Task<List<PasswordlessLoginResponse>> GetAuthRequestAsync();
|
||||
Task<PasswordlessLoginResponse> GetAuthRequestAsync(string id);
|
||||
Task<PasswordlessLoginResponse> GetAuthResponseAsync(string id, string accessCode);
|
||||
Task<PasswordlessLoginResponse> PutAuthRequestAsync(string id, string key, string masterPasswordHash, string deviceIdentifier, bool requestApproved);
|
||||
|
|
|
@ -29,6 +29,7 @@ namespace Bit.Core.Abstractions
|
|||
Task<AuthResult> LogInTwoFactorAsync(TwoFactorProviderType twoFactorProvider, string twoFactorToken, string captchaToken, bool? remember = null);
|
||||
Task<AuthResult> LogInPasswordlessAsync(string email, string accessCode, string authRequestId, byte[] decryptionKey, string userKeyCiphered, string localHashedPasswordCiphered);
|
||||
|
||||
Task<List<PasswordlessLoginResponse>> GetPasswordlessLoginRequestsAsync();
|
||||
Task<PasswordlessLoginResponse> GetPasswordlessLoginRequestByIdAsync(string id);
|
||||
Task<PasswordlessLoginResponse> GetPasswordlessLoginResponseAsync(string id, string accessCode);
|
||||
Task<PasswordlessLoginResponse> PasswordlessLoginAsync(string id, string pubKey, bool requestApproved);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Core.Models.Response
|
||||
{
|
||||
|
@ -18,5 +20,14 @@ namespace Bit.Core.Models.Response
|
|||
public string Origin { get; set; }
|
||||
public string RequestAccessCode { get; set; }
|
||||
public Tuple<byte[], byte[]> RequestKeyPair { get; set; }
|
||||
|
||||
public bool IsAnswered => RequestApproved != null && ResponseDate != null;
|
||||
|
||||
public bool IsExpired => CreationDate.ToUniversalTime().AddMinutes(Constants.PasswordlessNotificationTimeoutInMinutes) < DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public class PasswordlessLoginsResponse
|
||||
{
|
||||
public List<PasswordlessLoginResponse> Data { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -536,6 +536,12 @@ namespace Bit.Core.Services
|
|||
|
||||
#region PasswordlessLogin
|
||||
|
||||
public async Task<List<PasswordlessLoginResponse>> GetAuthRequestAsync()
|
||||
{
|
||||
var response = await SendAsync<object, PasswordlessLoginsResponse>(HttpMethod.Get, $"/auth-requests/", null, true, true);
|
||||
return response.Data;
|
||||
}
|
||||
|
||||
public Task<PasswordlessLoginResponse> GetAuthRequestAsync(string id)
|
||||
{
|
||||
return SendAsync<object, PasswordlessLoginResponse>(HttpMethod.Get, $"/auth-requests/{id}", null, true, true);
|
||||
|
|
|
@ -485,6 +485,11 @@ namespace Bit.Core.Services
|
|||
SelectedTwoFactorProviderType = null;
|
||||
}
|
||||
|
||||
public async Task<List<PasswordlessLoginResponse>> GetPasswordlessLoginRequestsAsync()
|
||||
{
|
||||
return await _apiService.GetAuthRequestAsync();
|
||||
}
|
||||
|
||||
public async Task<PasswordlessLoginResponse> GetPasswordlessLoginRequestByIdAsync(string id)
|
||||
{
|
||||
return await _apiService.GetAuthRequestAsync(id);
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace Bit.Core.Services
|
|||
private readonly IPolicyService _policyService;
|
||||
private readonly ISendService _sendService;
|
||||
private readonly IKeyConnectorService _keyConnectorService;
|
||||
private readonly ILogger _logger;
|
||||
private readonly Func<Tuple<string, bool, bool>, Task> _logoutCallbackAsync;
|
||||
|
||||
public SyncService(
|
||||
|
@ -39,6 +40,7 @@ namespace Bit.Core.Services
|
|||
IPolicyService policyService,
|
||||
ISendService sendService,
|
||||
IKeyConnectorService keyConnectorService,
|
||||
ILogger logger,
|
||||
Func<Tuple<string, bool, bool>, Task> logoutCallbackAsync)
|
||||
{
|
||||
_stateService = stateService;
|
||||
|
@ -53,6 +55,7 @@ namespace Bit.Core.Services
|
|||
_policyService = policyService;
|
||||
_sendService = sendService;
|
||||
_keyConnectorService = keyConnectorService;
|
||||
_logger = logger;
|
||||
_logoutCallbackAsync = logoutCallbackAsync;
|
||||
}
|
||||
|
||||
|
@ -108,6 +111,7 @@ namespace Bit.Core.Services
|
|||
await SyncSettingsAsync(userId, response.Domains);
|
||||
await SyncPoliciesAsync(response.Policies);
|
||||
await SyncSendsAsync(userId, response.Sends);
|
||||
await SyncPasswordlessLoginRequestsAsync(userId);
|
||||
await SetLastSyncAsync(now);
|
||||
return SyncCompleted(true);
|
||||
}
|
||||
|
@ -382,5 +386,44 @@ namespace Bit.Core.Services
|
|||
new Dictionary<string, SendData>();
|
||||
await _sendService.ReplaceAsync(sends);
|
||||
}
|
||||
|
||||
private async Task SyncPasswordlessLoginRequestsAsync(string userId)
|
||||
{
|
||||
try
|
||||
{
|
||||
// if the user has not enabled passwordless logins ignore requests
|
||||
if (!await _stateService.GetApprovePasswordlessLoginsAsync(userId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var loginRequests = await _apiService.GetAuthRequestAsync();
|
||||
if (loginRequests == null || !loginRequests.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var validLoginRequest = loginRequests.Where(l => !l.IsAnswered && !l.IsExpired)
|
||||
.OrderByDescending(x => x.CreationDate)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (validLoginRequest is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _stateService.SetPasswordlessLoginNotificationAsync(new PasswordlessRequestNotification()
|
||||
{
|
||||
Id = validLoginRequest.Id,
|
||||
UserId = userId
|
||||
});
|
||||
|
||||
_messagingService.Send(Constants.PasswordlessLoginRequestKey);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Exception(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ namespace Bit.Core.Utilities
|
|||
var messagingService = Resolve<IMessagingService>("messagingService");
|
||||
var cryptoFunctionService = Resolve<ICryptoFunctionService>("cryptoFunctionService");
|
||||
var cryptoService = Resolve<ICryptoService>("cryptoService");
|
||||
var logger = Resolve<ILogger>();
|
||||
|
||||
SearchService searchService = null;
|
||||
|
||||
var tokenService = new TokenService(stateService);
|
||||
|
@ -67,7 +69,7 @@ namespace Bit.Core.Utilities
|
|||
});
|
||||
var syncService = new SyncService(stateService, apiService, settingsService, folderService, cipherService,
|
||||
cryptoService, collectionService, organizationService, messagingService, policyService, sendService,
|
||||
keyConnectorService, (extras) =>
|
||||
keyConnectorService, logger, (extras) =>
|
||||
{
|
||||
messagingService.Send("logout", extras);
|
||||
return Task.CompletedTask;
|
||||
|
|
Loading…
Reference in a new issue