diff --git a/src/Core/CurrentContext.cs b/src/Core/CurrentContext.cs index e9da8ebdb4..2ec7847acc 100644 --- a/src/Core/CurrentContext.cs +++ b/src/Core/CurrentContext.cs @@ -9,5 +9,6 @@ namespace Bit.Core public class CurrentContext { public virtual User User { get; set; } + public virtual string DeviceIdentifier { get; set; } } } diff --git a/src/Core/Identity/JwtBearerEventImplementations.cs b/src/Core/Identity/JwtBearerEventImplementations.cs index 54ba96c6df..f57e14c67c 100644 --- a/src/Core/Identity/JwtBearerEventImplementations.cs +++ b/src/Core/Identity/JwtBearerEventImplementations.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Http.Authentication; using Microsoft.IdentityModel.Tokens; using Microsoft.AspNetCore.Identity; using Bit.Core.Domains; +using System.Linq; namespace Bit.Core.Identity { @@ -37,6 +38,11 @@ namespace Bit.Core.Identity // register the current context user var currentContext = context.HttpContext.RequestServices.GetRequiredService(); currentContext.User = user; + var deviceIdentifierClaim = context.Ticket.Principal.Claims.SingleOrDefault(c => c.Type == "DeviceIdentifier"); + if(deviceIdentifierClaim != null) + { + currentContext.DeviceIdentifier = deviceIdentifierClaim.Value; + } } public static Task AuthenticationFailedAsync(AuthenticationFailedContext context) diff --git a/src/Core/Identity/JwtBearerSignInManager.cs b/src/Core/Identity/JwtBearerSignInManager.cs index 0e86717c40..15490262f9 100644 --- a/src/Core/Identity/JwtBearerSignInManager.cs +++ b/src/Core/Identity/JwtBearerSignInManager.cs @@ -68,7 +68,7 @@ namespace Bit.Core.Identity if(await UserManager.CheckPasswordAsync(user, password)) { - var result = await SignInOrTwoFactorAsync(user); + var result = await SignInOrTwoFactorAsync(user, device); if(result.Succeeded && device != null) { var existingDevice = await _deviceRepository.GetByIdentifierAsync(device.Identifier, user.Id); @@ -105,7 +105,7 @@ namespace Bit.Core.Identity if(await UserManager.VerifyTwoFactorTokenAsync(user, provider, code)) { - var token = await SignInAsync(user, false); + var token = await SignInAsync(user, false, device); var success = JwtBearerSignInResult.Success; success.Token = token; @@ -127,7 +127,7 @@ namespace Bit.Core.Identity return JwtBearerSignInResult.Failed; } - private async Task SignInAsync(User user, bool twoFactor) + private async Task SignInAsync(User user, bool twoFactor, Device device) { var handler = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler(); @@ -150,6 +150,11 @@ namespace Bit.Core.Identity } } + if(device != null && !string.IsNullOrWhiteSpace(device.Identifier)) + { + userPrincipal.Identities.First().AddClaim(new Claim("DeviceIdentifier", device.Identifier)); + } + var descriptor = new SecurityTokenDescriptor { Issuer = JwtIdentityOptions.Issuer, @@ -164,13 +169,13 @@ namespace Bit.Core.Identity return handler.WriteToken(securityToken); } - private async Task SignInOrTwoFactorAsync(User user) + private async Task SignInOrTwoFactorAsync(User user, Device device) { if(UserManager.SupportsUserTwoFactor && await UserManager.GetTwoFactorEnabledAsync(user) && (await UserManager.GetValidTwoFactorProvidersAsync(user)).Count > 0) { - var twoFactorToken = await SignInAsync(user, true); + var twoFactorToken = await SignInAsync(user, true, device); var twoFactorResult = JwtBearerSignInResult.TwoFactorRequired; twoFactorResult.Token = twoFactorToken; @@ -179,7 +184,7 @@ namespace Bit.Core.Identity return twoFactorResult; } - var token = await SignInAsync(user, false); + var token = await SignInAsync(user, false, device); var result = JwtBearerSignInResult.Success; result.Token = token; diff --git a/src/Core/Services/PushService.cs b/src/Core/Services/PushService.cs index 8f67f3ee97..136cc4db3f 100644 --- a/src/Core/Services/PushService.cs +++ b/src/Core/Services/PushService.cs @@ -22,17 +22,20 @@ namespace Bit.Core.Services { private readonly IDeviceRepository _deviceRepository; private readonly ILogger _logger; + private readonly CurrentContext _currentContext; private GcmServiceBroker _gcmBroker; private ApnsServiceBroker _apnsBroker; public PushService( IDeviceRepository deviceRepository, ILogger logger, + CurrentContext currentContext, IHostingEnvironment hostingEnvironment, GlobalSettings globalSettings) { _deviceRepository = deviceRepository; _logger = logger; + _currentContext = currentContext; InitGcmBroker(globalSettings); InitApnsBroker(globalSettings, hostingEnvironment); @@ -74,7 +77,13 @@ namespace Bit.Core.Services Aps = new PushNotification.AppleData { ContentAvailable = 1 } }; - await PushToAllUserDevicesAsync(cipher.UserId, JObject.FromObject(message)); + var excludedTokens = new List(); + if(!string.IsNullOrWhiteSpace(_currentContext.DeviceIdentifier)) + { + excludedTokens.Add(_currentContext.DeviceIdentifier); + } + + await PushToAllUserDevicesAsync(cipher.UserId, JObject.FromObject(message), excludedTokens); } public async Task PushSyncCiphersAsync(Guid userId) @@ -87,7 +96,7 @@ namespace Bit.Core.Services Aps = new PushNotification.AppleData { ContentAvailable = 1 } }; - await PushToAllUserDevicesAsync(userId, JObject.FromObject(message)); + await PushToAllUserDevicesAsync(userId, JObject.FromObject(message), null); } private void InitGcmBroker(GlobalSettings globalSettings) @@ -255,9 +264,10 @@ namespace Bit.Core.Services // timestamp is the time the token was reported as expired } - private async Task PushToAllUserDevicesAsync(Guid userId, JObject message) + private async Task PushToAllUserDevicesAsync(Guid userId, JObject message, IEnumerable tokensToSkip) { - var devices = (await _deviceRepository.GetManyByUserIdAsync(userId)).Where(d => !string.IsNullOrWhiteSpace(d.PushToken)); + var devices = (await _deviceRepository.GetManyByUserIdAsync(userId)) + .Where(d => !string.IsNullOrWhiteSpace(d.PushToken) && (!tokensToSkip?.Contains(d.PushToken) ?? true)); if(devices.Count() == 0) { return;