-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Expand file tree
/
Copy pathAssertWebAuthnLoginCredentialCommand.cs
More file actions
74 lines (63 loc) · 2.95 KB
/
AssertWebAuthnLoginCredentialCommand.cs
File metadata and controls
74 lines (63 loc) · 2.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
using Bit.Core.Auth.Entities;
using Bit.Core.Auth.Repositories;
using Bit.Core.Auth.Utilities;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Core.Utilities;
using Fido2NetLib;
namespace Bit.Core.Auth.UserFeatures.WebAuthnLogin.Implementations;
internal class AssertWebAuthnLoginCredentialCommand : IAssertWebAuthnLoginCredentialCommand
{
private readonly IFido2 _fido2;
private readonly IWebAuthnCredentialRepository _webAuthnCredentialRepository;
private readonly IUserRepository _userRepository;
private readonly IWebAuthnChallengeCacheProvider _webAuthnChallengeCache;
public AssertWebAuthnLoginCredentialCommand(
IFido2 fido2,
IWebAuthnCredentialRepository webAuthnCredentialRepository,
IUserRepository userRepository,
IWebAuthnChallengeCacheProvider webAuthnChallengeCache)
{
_fido2 = fido2;
_webAuthnCredentialRepository = webAuthnCredentialRepository;
_userRepository = userRepository;
_webAuthnChallengeCache = webAuthnChallengeCache;
}
public async Task<(User, WebAuthnCredential)> AssertWebAuthnLoginCredential(AssertionOptions options, AuthenticatorAssertionRawResponse assertionResponse)
{
if (!await _webAuthnChallengeCache.TryMarkChallengeAsUsedAsync(options.Challenge))
{
throw new BadRequestException("Invalid credential.");
}
if (!GuidUtilities.TryParseBytes(assertionResponse.Response.UserHandle, out var userId))
{
throw new BadRequestException("Invalid credential.");
}
var user = await _userRepository.GetByIdAsync(userId);
if (user == null)
{
throw new BadRequestException("Invalid credential.");
}
var userCredentials = await _webAuthnCredentialRepository.GetManyByUserIdAsync(user.Id);
var assertedCredentialId = CoreHelpers.Base64UrlEncode(assertionResponse.Id);
var credential = userCredentials.FirstOrDefault(c => c.CredentialId == assertedCredentialId);
if (credential == null)
{
throw new BadRequestException("Invalid credential.");
}
// Always return true, since we've already filtered the credentials after user id
IsUserHandleOwnerOfCredentialIdAsync callback = (args, cancellationToken) => Task.FromResult(true);
var credentialPublicKey = CoreHelpers.Base64UrlDecode(credential.PublicKey);
var assertionVerificationResult = await _fido2.MakeAssertionAsync(
assertionResponse, options, credentialPublicKey, (uint)credential.Counter, callback);
// Update SignatureCounter
credential.Counter = (int)assertionVerificationResult.Counter;
await _webAuthnCredentialRepository.ReplaceAsync(credential);
if (assertionVerificationResult.Status != "ok")
{
throw new BadRequestException("Invalid credential.");
}
return (user, credential);
}
}