[PM-31394] use email address hash for send access email verification (#6921)

* [PM-31394] use email address hash for send access email verification

* [PM-31394] fixing identity server tests for send access

* [PM-31394] fixing more identity server tests for send access
This commit is contained in:
Alex Dragovich
2026-01-29 11:48:12 -08:00
committed by GitHub
parent 7855c4ee6e
commit 0544ec41d5
6 changed files with 41 additions and 13 deletions

View File

@@ -0,0 +1,17 @@
using System.Security.Cryptography;
using System.Text;
namespace Bit.Test.Common.Helpers;
public class CryptographyHelper
{
/// <summary>
/// Returns a hex-encoded, SHA256 hash for the given string
/// </summary>
public static string HashAndEncode(string text)
{
var hashBytes = SHA256.HashData(Encoding.UTF8.GetBytes(text));
var hashEncoded = Convert.ToHexString(hashBytes).ToUpperInvariant();
return hashEncoded;
}
}

View File

@@ -56,7 +56,7 @@ public class SendAuthenticationQueryTests
// Assert
var emailOtp = Assert.IsType<EmailOtp>(result);
Assert.Equal(expectedEmailHashes, emailOtp.Emails);
Assert.Equal(expectedEmailHashes, emailOtp.EmailHashes);
}
[Fact]

View File

@@ -3,6 +3,7 @@ using Bit.Core.Services;
using Bit.Core.Tools.Models.Data;
using Bit.Core.Tools.SendFeatures.Queries.Interfaces;
using Bit.IntegrationTestCommon.Factories;
using Bit.Test.Common.Helpers;
using Duende.IdentityModel;
using NSubstitute;
using Xunit;
@@ -60,7 +61,7 @@ public class SendEmailOtpRequestValidatorIntegrationTests(IdentityApplicationFac
var sendAuthQuery = Substitute.For<ISendAuthenticationQuery>();
sendAuthQuery.GetAuthenticationMethod(sendId)
.Returns(new EmailOtp([email]));
.Returns(new EmailOtp([CryptographyHelper.HashAndEncode(email)]));
services.AddSingleton(sendAuthQuery);
// Mock OTP token provider
@@ -75,6 +76,7 @@ public class SendEmailOtpRequestValidatorIntegrationTests(IdentityApplicationFac
});
}).CreateClient();
var requestBody = SendAccessTestUtilities.CreateTokenRequestBody(sendId, email: email); // Email but no OTP
// Act
@@ -104,7 +106,7 @@ public class SendEmailOtpRequestValidatorIntegrationTests(IdentityApplicationFac
var sendAuthQuery = Substitute.For<ISendAuthenticationQuery>();
sendAuthQuery.GetAuthenticationMethod(sendId)
.Returns(new EmailOtp(new[] { email }));
.Returns(new EmailOtp(new[] { CryptographyHelper.HashAndEncode(email) }));
services.AddSingleton(sendAuthQuery);
// Mock OTP token provider to validate successfully
@@ -148,7 +150,7 @@ public class SendEmailOtpRequestValidatorIntegrationTests(IdentityApplicationFac
var sendAuthQuery = Substitute.For<ISendAuthenticationQuery>();
sendAuthQuery.GetAuthenticationMethod(sendId)
.Returns(new EmailOtp(new[] { email }));
.Returns(new EmailOtp(new[] { CryptographyHelper.HashAndEncode(email) }));
services.AddSingleton(sendAuthQuery);
// Mock OTP token provider to validate as false
@@ -190,7 +192,7 @@ public class SendEmailOtpRequestValidatorIntegrationTests(IdentityApplicationFac
var sendAuthQuery = Substitute.For<ISendAuthenticationQuery>();
sendAuthQuery.GetAuthenticationMethod(sendId)
.Returns(new EmailOtp(new[] { email }));
.Returns(new EmailOtp(new[] { CryptographyHelper.HashAndEncode(email) }));
services.AddSingleton(sendAuthQuery);
// Mock OTP token provider to fail generation

View File

@@ -5,6 +5,7 @@ using Bit.Core.Tools.Models.Data;
using Bit.Identity.IdentityServer.RequestValidators.SendAccess;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Bit.Test.Common.Helpers;
using Duende.IdentityModel;
using Duende.IdentityServer.Validation;
using NSubstitute;
@@ -105,7 +106,8 @@ public class SendEmailOtpRequestValidatorTests
expectedUniqueId)
.Returns(generatedToken);
emailOtp = emailOtp with { Emails = [email] };
var emailHash = CryptographyHelper.HashAndEncode(email);
emailOtp = emailOtp with { EmailHashes = [emailHash] };
// Act
var result = await sutProvider.Sut.ValidateRequestAsync(context, emailOtp, sendId);
@@ -144,7 +146,8 @@ public class SendEmailOtpRequestValidatorTests
Request = tokenRequest
};
emailOtp = emailOtp with { Emails = [email] };
var emailHash = CryptographyHelper.HashAndEncode(email);
emailOtp = emailOtp with { EmailHashes = [emailHash] };
sutProvider.GetDependency<IOtpTokenProvider<DefaultOtpTokenProviderOptions>>()
.GenerateTokenAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>())
@@ -179,7 +182,8 @@ public class SendEmailOtpRequestValidatorTests
Request = tokenRequest
};
emailOtp = emailOtp with { Emails = [email] };
var emailHash = CryptographyHelper.HashAndEncode(email);
emailOtp = emailOtp with { EmailHashes = [emailHash] };
var expectedUniqueId = string.Format(SendAccessConstants.OtpToken.TokenUniqueIdentifier, sendId, email);
@@ -231,7 +235,8 @@ public class SendEmailOtpRequestValidatorTests
Request = tokenRequest
};
emailOtp = emailOtp with { Emails = [email] };
var emailHash = CryptographyHelper.HashAndEncode(email);
emailOtp = emailOtp with { EmailHashes = [emailHash] };
var expectedUniqueId = string.Format(SendAccessConstants.OtpToken.TokenUniqueIdentifier, sendId, email);