Skip to content

Commit e08e34d

Browse files
test: update test coverage
1 parent 4efa84b commit e08e34d

2 files changed

Lines changed: 159 additions & 0 deletions

File tree

test/Api.Test/Auth/Controllers/WebAuthnControllerTests.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,25 @@ public async Task AssertionOptions_UserVerificationSuccess_ReturnsAssertionOptio
177177
Assert.NotNull(result);
178178
Assert.IsType<WebAuthnLoginAssertionOptionsResponseModel>(result);
179179
}
180+
181+
[Theory, BitAutoData]
182+
public async Task AssertionOptions_Success_ProtectsTokenWithUpdateKeySetScope(SecretVerificationRequestModel requestModel, User user, SutProvider<WebAuthnController> sutProvider)
183+
{
184+
// Arrange
185+
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user);
186+
sutProvider.GetDependency<IUserService>().VerifySecretAsync(user, requestModel.Secret).Returns(true);
187+
sutProvider.GetDependency<IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable>>()
188+
.Protect(Arg.Any<WebAuthnLoginAssertionOptionsTokenable>()).Returns("token");
189+
190+
// Act
191+
await sutProvider.Sut.AssertionOptions(requestModel);
192+
193+
// Assert
194+
sutProvider.GetDependency<IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable>>()
195+
.Received(1)
196+
.Protect(Arg.Is<WebAuthnLoginAssertionOptionsTokenable>(t =>
197+
t.Scope == Core.Auth.Enums.WebAuthnLoginAssertionOptionsScope.UpdateKeySet));
198+
}
180199
#endregion
181200

182201
[Theory, BitAutoData]
@@ -419,6 +438,30 @@ public async Task Put_TokenVerificationFailed_ThrowsBadRequestException(Assertio
419438
Assert.Equal(expectedMessage, exception.Message);
420439
}
421440

441+
[Theory, BitAutoData]
442+
public async Task Put_TokenWithNullOptions_ThrowsBadRequestException(WebAuthnLoginCredentialUpdateRequestModel requestModel, SutProvider<WebAuthnController> sutProvider)
443+
{
444+
// Arrange - tokenable deserialized with correct scope but Options == null
445+
var expectedMessage = "The token associated with your request is invalid or has expired. A valid token is required to continue.";
446+
var token = new WebAuthnLoginAssertionOptionsTokenable
447+
{
448+
Scope = Core.Auth.Enums.WebAuthnLoginAssertionOptionsScope.UpdateKeySet,
449+
Options = null,
450+
};
451+
sutProvider.GetDependency<IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable>>()
452+
.Unprotect(requestModel.Token)
453+
.Returns(token);
454+
455+
// Act
456+
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateCredential(requestModel));
457+
458+
// Assert
459+
Assert.Equal(expectedMessage, exception.Message);
460+
await sutProvider.GetDependency<IAssertWebAuthnLoginCredentialCommand>()
461+
.DidNotReceive()
462+
.AssertWebAuthnLoginCredential(Arg.Any<AssertionOptions>(), Arg.Any<AuthenticatorAssertionRawResponse>());
463+
}
464+
422465
[Theory, BitAutoData]
423466
public async Task Put_CredentialNotFound_ThrowsBadRequestException(AssertionOptions assertionOptions, WebAuthnLoginCredentialUpdateRequestModel requestModel, SutProvider<WebAuthnController> sutProvider)
424467
{

test/Identity.Test/IdentityServer/RequestValidators/WebAuthnGrantValidatorTests.cs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,122 @@ public async Task ValidateAsync_InvalidToken_RejectsWithInvalidRequest(
6868
Assert.Equal("invalid_request", context.Result.Error);
6969
}
7070

71+
[Theory]
72+
[BitAutoData("", "{}")]
73+
[BitAutoData(" ", "{}")]
74+
[BitAutoData("test-token", "")]
75+
[BitAutoData("test-token", " ")]
76+
public async Task ValidateAsync_EmptyOrWhitespaceParameter_RejectsWithInvalidGrant(
77+
string token,
78+
string deviceResponse,
79+
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest tokenRequest,
80+
SutProvider<WebAuthnGrantValidator> sutProvider)
81+
{
82+
// Arrange - one of token / deviceResponse is empty or whitespace
83+
var context = CreateContext(tokenRequest, token, deviceResponse);
84+
85+
// Act
86+
await sutProvider.Sut.ValidateAsync(context);
87+
88+
// Assert
89+
Assert.Equal("invalid_grant", context.Result.Error);
90+
sutProvider.GetDependency<IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable>>()
91+
.DidNotReceive()
92+
.TryUnprotect(Arg.Any<string>(), out Arg.Any<WebAuthnLoginAssertionOptionsTokenable>());
93+
}
94+
95+
[Theory, BitAutoData]
96+
public async Task ValidateAsync_DeviceResponseDeserializesToNull_RejectsWithInvalidRequest(
97+
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest tokenRequest,
98+
SutProvider<WebAuthnGrantValidator> sutProvider)
99+
{
100+
// Arrange
101+
var options = new AssertionOptions { Challenge = [1, 2, 3, 4, 5, 6, 7, 8] };
102+
var tokenable = new WebAuthnLoginAssertionOptionsTokenable(
103+
WebAuthnLoginAssertionOptionsScope.Authentication, options);
104+
105+
var context = CreateContext(tokenRequest, deviceResponse: "null");
106+
107+
sutProvider.GetDependency<IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable>>()
108+
.TryUnprotect(Arg.Any<string>(), out Arg.Any<WebAuthnLoginAssertionOptionsTokenable>())
109+
.Returns(x =>
110+
{
111+
x[1] = tokenable;
112+
return true;
113+
});
114+
115+
// Act
116+
await sutProvider.Sut.ValidateAsync(context);
117+
118+
// Assert
119+
Assert.Equal("invalid_request", context.Result.Error);
120+
await sutProvider.GetDependency<IAssertWebAuthnLoginCredentialCommand>()
121+
.DidNotReceive()
122+
.AssertWebAuthnLoginCredential(Arg.Any<AssertionOptions>(), Arg.Any<AuthenticatorAssertionRawResponse>());
123+
}
124+
125+
[Theory, BitAutoData]
126+
public async Task ValidateAsync_TokenWithWrongScope_RejectsWithInvalidRequest(
127+
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest tokenRequest,
128+
SutProvider<WebAuthnGrantValidator> sutProvider)
129+
{
130+
// Arrange
131+
var options = new AssertionOptions { Challenge = [1, 2, 3, 4, 5, 6, 7, 8] };
132+
var tokenable = new WebAuthnLoginAssertionOptionsTokenable(
133+
WebAuthnLoginAssertionOptionsScope.PrfRegistration, options);
134+
135+
var context = CreateContext(tokenRequest);
136+
137+
sutProvider.GetDependency<IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable>>()
138+
.TryUnprotect(Arg.Any<string>(), out Arg.Any<WebAuthnLoginAssertionOptionsTokenable>())
139+
.Returns(x =>
140+
{
141+
x[1] = tokenable;
142+
return true;
143+
});
144+
145+
// Act
146+
await sutProvider.Sut.ValidateAsync(context);
147+
148+
// Assert
149+
Assert.Equal("invalid_request", context.Result.Error);
150+
await sutProvider.GetDependency<IAssertWebAuthnLoginCredentialCommand>()
151+
.DidNotReceive()
152+
.AssertWebAuthnLoginCredential(Arg.Any<AssertionOptions>(), Arg.Any<AuthenticatorAssertionRawResponse>());
153+
}
154+
155+
[Theory, BitAutoData]
156+
public async Task ValidateAsync_TokenWithNullOptions_RejectsWithInvalidRequest(
157+
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest tokenRequest,
158+
SutProvider<WebAuthnGrantValidator> sutProvider)
159+
{
160+
// Arrange
161+
var tokenable = new WebAuthnLoginAssertionOptionsTokenable
162+
{
163+
Scope = WebAuthnLoginAssertionOptionsScope.Authentication,
164+
Options = null,
165+
};
166+
167+
var context = CreateContext(tokenRequest);
168+
169+
sutProvider.GetDependency<IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable>>()
170+
.TryUnprotect(Arg.Any<string>(), out Arg.Any<WebAuthnLoginAssertionOptionsTokenable>())
171+
.Returns(x =>
172+
{
173+
x[1] = tokenable;
174+
return true;
175+
});
176+
177+
// Act
178+
await sutProvider.Sut.ValidateAsync(context);
179+
180+
// Assert
181+
Assert.Equal("invalid_request", context.Result.Error);
182+
await sutProvider.GetDependency<IAssertWebAuthnLoginCredentialCommand>()
183+
.DidNotReceive()
184+
.AssertWebAuthnLoginCredential(Arg.Any<AssertionOptions>(), Arg.Any<AuthenticatorAssertionRawResponse>());
185+
}
186+
71187
[Theory, BitAutoData]
72188
public async Task ValidateAsync_ValidToken_CallsAssertCommand(
73189
[AuthFixtures.ValidatedTokenRequest] ValidatedTokenRequest tokenRequest,

0 commit comments

Comments
 (0)