diff --git a/apps/server/AliasVault.Api/Controllers/AuthController.cs b/apps/server/AliasVault.Api/Controllers/AuthController.cs
index 0132fa9d5..7f6a6ae23 100644
--- a/apps/server/AliasVault.Api/Controllers/AuthController.cs
+++ b/apps/server/AliasVault.Api/Controllers/AuthController.cs
@@ -582,25 +582,6 @@ public class AuthController(IAliasServerDbContextFactory dbContextFactory, UserM
return username.ToLowerInvariant().Trim();
}
- ///
- /// Generate a device identifier based on request headers. This is used to associate refresh tokens
- /// with a specific device for a specific user.
- ///
- /// NOTE: current implementation means that only one refresh token can be valid for a
- /// specific user/device combo at a time. The identifier generation could be made more unique in the future
- /// to prevent any unwanted conflicts.
- ///
- /// The HttpRequest instance for the request that the client used.
- /// Unique device identifier as string.
- private static string GenerateDeviceIdentifier(HttpRequest request)
- {
- var userAgent = request.Headers.UserAgent.ToString();
- var acceptLanguage = request.Headers.AcceptLanguage.ToString();
-
- var rawIdentifier = $"{userAgent}|{acceptLanguage}";
- return rawIdentifier;
- }
-
///
/// Generate a refresh token for a user. This token is used to request a new access token when the current
/// access token expires. The refresh token is long-lived by design.
@@ -888,7 +869,7 @@ public class AuthController(IAliasServerDbContextFactory dbContextFactory, UserM
// Generate device identifier
var accessToken = GenerateJwtToken(user);
var refreshToken = GenerateRefreshToken();
- var deviceIdentifier = GenerateDeviceIdentifier(Request);
+ var deviceIdentifier = AuthHelper.GenerateDeviceIdentifier(Request);
// Add new refresh token.
context.AliasVaultUserRefreshTokens.Add(new AliasVaultUserRefreshToken
diff --git a/apps/server/AliasVault.Api/Controllers/VaultController.cs b/apps/server/AliasVault.Api/Controllers/VaultController.cs
index ed7320946..a89ccc7b9 100644
--- a/apps/server/AliasVault.Api/Controllers/VaultController.cs
+++ b/apps/server/AliasVault.Api/Controllers/VaultController.cs
@@ -362,6 +362,12 @@ public class VaultController(ILogger logger, IAliasServerDbCont
await authLoggingService.LogAuthEventSuccessAsync(user.UserName!, AuthEventType.PasswordChange);
+ // Force revoke all user logged in sessions except current one.
+ // This means that other clients which have not already updated to the new password will be logged out.
+ // This ensures that all clients login again with the new password to refresh their encryption keys for future vault mutations.
+ var deviceIdentifier = AuthHelper.GenerateDeviceIdentifier(Request);
+ await context.AliasVaultUserRefreshTokens.Where(x => x.UserId == user.Id && x.DeviceIdentifier != deviceIdentifier).ExecuteDeleteAsync();
+
return Ok(new VaultUpdateResponse { Status = VaultStatus.Ok, NewRevisionNumber = newRevisionNumber });
}
diff --git a/apps/server/AliasVault.Api/Helpers/AuthHelper.cs b/apps/server/AliasVault.Api/Helpers/AuthHelper.cs
index 6c4fb15df..1e6016ee1 100644
--- a/apps/server/AliasVault.Api/Helpers/AuthHelper.cs
+++ b/apps/server/AliasVault.Api/Helpers/AuthHelper.cs
@@ -72,4 +72,23 @@ public static class AuthHelper
var latestVault = user.Vaults.OrderByDescending(x => x.RevisionNumber).Select(x => new { x.Salt, x.Verifier, x.EncryptionType, x.EncryptionSettings }).First();
return (latestVault.Salt, latestVault.Verifier, latestVault.EncryptionType, latestVault.EncryptionSettings);
}
+
+ ///
+ /// Generate a device identifier based on request headers. This is used to associate refresh tokens
+ /// with a specific device for a specific user.
+ ///
+ /// NOTE: current implementation means that only one refresh token can be valid for a
+ /// specific user/device combo at a time. The identifier generation could be made more unique in the future
+ /// to prevent any unwanted conflicts.
+ ///
+ /// The HttpRequest instance for the request that the client used.
+ /// Unique device identifier as string.
+ public static string GenerateDeviceIdentifier(HttpRequest request)
+ {
+ var userAgent = request.Headers.UserAgent.ToString();
+ var acceptLanguage = request.Headers.AcceptLanguage.ToString();
+
+ var rawIdentifier = $"{userAgent}|{acceptLanguage}";
+ return rawIdentifier;
+ }
}