Skip to content

Commit

Permalink
Wrap arguments into classes (#556)
Browse files Browse the repository at this point in the history
* Wrap method arguments into classes

* format

* format

* Seal the classes

* Adress feedback
  • Loading branch information
abergs authored Oct 28, 2024
1 parent 72f5a36 commit b2e7c15
Show file tree
Hide file tree
Showing 11 changed files with 1,522 additions and 1,217 deletions.
24 changes: 15 additions & 9 deletions BlazorWasmDemo/Server/Controllers/UserController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,12 @@ public async Task<string> CreateCredentialAsync([FromRoute] string username, [Fr
// 2. Create callback so that lib can verify credential id is unique to this user

// 3. Verify and make the credentials
var credential = await _fido2.MakeNewCredentialAsync(attestationResponse, options, CredentialIdUniqueToUserAsync, cancellationToken: cancellationToken);
var credential = await _fido2.MakeNewCredentialAsync(new MakeNewCredentialParams
{
AttestationResponse = attestationResponse,
OriginalOptions = options,
IsCredentialIdUniqueToUserCallback = CredentialIdUniqueToUserAsync
}, cancellationToken: cancellationToken);

// 4. Store the credentials in db
_demoStorage.AddCredentialToUser(options.User, new StoredCredential
Expand Down Expand Up @@ -266,14 +271,15 @@ public async Task<string> MakeAssertionAsync([FromBody] AuthenticatorAssertionRa
var creds = _demoStorage.GetCredentialById(clientResponse.Id) ?? throw new Exception("Unknown credentials");

// 3. Make the assertion
var res = await _fido2.MakeAssertionAsync(
clientResponse,
options,
creds.PublicKey,
creds.DevicePublicKeys,
creds.SignCount,
UserHandleOwnerOfCredentialIdAsync,
cancellationToken: cancellationToken);
var res = await _fido2.MakeAssertionAsync(new MakeAssertionParams
{
AssertionResponse = clientResponse,
OriginalOptions = options,
StoredPublicKey = creds.PublicKey,
StoredSignatureCounter = creds.SignCount,
IsUserHandleOwnerOfCredentialIdCallback = UserHandleOwnerOfCredentialIdAsync,
StoredDevicePublicKeys = creds.DevicePublicKeys
}, cancellationToken: cancellationToken);

// 4. Store the updated counter
_demoStorage.UpdateCounter(res.CredentialId, res.SignCount);
Expand Down
17 changes: 15 additions & 2 deletions Demo/Controller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,12 @@ public async Task<JsonResult> MakeCredential([FromBody] AuthenticatorAttestation
};

// 2. Verify and make the credentials
var credential = await _fido2.MakeNewCredentialAsync(attestationResponse, options, callback, cancellationToken: cancellationToken);
var credential = await _fido2.MakeNewCredentialAsync(new MakeNewCredentialParams
{
AttestationResponse = attestationResponse,
OriginalOptions = options,
IsCredentialIdUniqueToUserCallback = callback
}, cancellationToken: cancellationToken);

// 3. Store the credentials in db
DemoStorage.AddCredentialToUser(options.User, new StoredCredential
Expand Down Expand Up @@ -204,7 +209,15 @@ public async Task<JsonResult> MakeAssertion([FromBody] AuthenticatorAssertionRaw
};

// 5. Make the assertion
var res = await _fido2.MakeAssertionAsync(clientResponse, options, creds.PublicKey, creds.DevicePublicKeys, storedCounter, callback, cancellationToken: cancellationToken);
var res = await _fido2.MakeAssertionAsync(new MakeAssertionParams
{
AssertionResponse = clientResponse,
OriginalOptions = options,
StoredPublicKey = creds.PublicKey,
StoredSignatureCounter = storedCounter,
IsUserHandleOwnerOfCredentialIdCallback = callback,
StoredDevicePublicKeys = creds.DevicePublicKeys
}, cancellationToken: cancellationToken);

// 6. Store the updated counter
DemoStorage.UpdateCounter(res.CredentialId, res.SignCount);
Expand Down
17 changes: 15 additions & 2 deletions Demo/TestController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,12 @@ public async Task<OkObjectResult> MakeCredentialResultTestAsync([FromBody] Authe
};

// 2. Verify and make the credentials
var credential = await _fido2.MakeNewCredentialAsync(attestationResponse, options, callback, cancellationToken: cancellationToken);
var credential = await _fido2.MakeNewCredentialAsync(new MakeNewCredentialParams
{
AttestationResponse = attestationResponse,
OriginalOptions = options,
IsCredentialIdUniqueToUserCallback = callback
}, cancellationToken: cancellationToken);

// 3. Store the credentials in db
_demoStorage.AddCredentialToUser(options.User, new StoredCredential
Expand Down Expand Up @@ -177,7 +182,15 @@ public async Task<JsonResult> MakeAssertionTestAsync([FromBody] AuthenticatorAss
};

// 5. Make the assertion
var res = await _fido2.MakeAssertionAsync(clientResponse, options, creds.PublicKey, creds.DevicePublicKeys, storedCounter, callback, cancellationToken: cancellationToken);
var res = await _fido2.MakeAssertionAsync(new MakeAssertionParams
{
AssertionResponse = clientResponse,
OriginalOptions = options,
StoredPublicKey = creds.PublicKey,
StoredSignatureCounter = storedCounter,
IsUserHandleOwnerOfCredentialIdCallback = callback,
StoredDevicePublicKeys = creds.DevicePublicKeys
}, cancellationToken: cancellationToken);

// 6. Store the updated counter
_demoStorage.UpdateCounter(res.CredentialId, res.SignCount);
Expand Down
46 changes: 13 additions & 33 deletions Src/Fido2/Fido2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,14 @@ public CredentialCreateOptions RequestNewCredential(
/// <summary>
/// Verifies the response from the browser/authenticator after creating new credentials.
/// </summary>
/// <param name="attestationResponse">The attestation response from the authenticator.</param>
/// <param name="originalOptions">The original options that was sent to the client.</param>
/// <param name="isCredentialIdUniqueToUser">The delegate used to validate that the CredentialID is unique to this user.</param>
/// <param name="requestTokenBindingId">DO NOT USE - Deprecated, but kept in code due to conformance testing tool</param>
/// <param name="makeNewCredentialParams">The input arguments for creating a passkey</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns></returns>
public async Task<RegisteredPublicKeyCredential> MakeNewCredentialAsync(
AuthenticatorAttestationRawResponse attestationResponse,
CredentialCreateOptions originalOptions,
IsCredentialIdUniqueToUserAsyncDelegate isCredentialIdUniqueToUser,
byte[]? requestTokenBindingId = null,
public async Task<RegisteredPublicKeyCredential> MakeNewCredentialAsync(MakeNewCredentialParams makeNewCredentialParams,
CancellationToken cancellationToken = default)
{
var parsedResponse = AuthenticatorAttestationResponse.Parse(attestationResponse);
var credential = await parsedResponse.VerifyAsync(originalOptions, _config, isCredentialIdUniqueToUser, _metadataService, requestTokenBindingId, cancellationToken);
var parsedResponse = AuthenticatorAttestationResponse.Parse(makeNewCredentialParams.AttestationResponse);
var credential = await parsedResponse.VerifyAsync(makeNewCredentialParams.OriginalOptions, _config, makeNewCredentialParams.IsCredentialIdUniqueToUserCallback, _metadataService, makeNewCredentialParams.RequestTokenBindingId, cancellationToken);

return credential;
}
Expand All @@ -101,35 +94,22 @@ public AssertionOptions GetAssertionOptions(
/// <summary>
/// Verifies the assertion response from the browser/authenticator to assert existing credentials and authenticate a user.
/// </summary>
/// <param name="assertionResponse">The assertion response from the authenticator.</param>
/// <param name="originalOptions">The original options that was sent to the client.</param>
/// <param name="storedPublicKey">The stored credential public key.</param>
/// <param name="storedDevicePublicKeys">The stored device public keys.</param>
/// <param name="storedSignatureCounter">The stored value of the signature counter.</param>
/// <param name="isUserHandleOwnerOfCredentialIdCallback">The delegate used to validate that the user handle is indeed owned of the CredentialId.</param>
/// <param name="requestTokenBindingId">DO NOT USE - Deprecated, but kept in code due to conformance testing tool</param>
/// <param name="makeAssertionParams">The input arguments for asserting a passkey</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns></returns>
public async Task<VerifyAssertionResult> MakeAssertionAsync(
AuthenticatorAssertionRawResponse assertionResponse,
AssertionOptions originalOptions,
byte[] storedPublicKey,
IReadOnlyList<byte[]> storedDevicePublicKeys,
uint storedSignatureCounter,
IsUserHandleOwnerOfCredentialIdAsync isUserHandleOwnerOfCredentialIdCallback,
byte[]? requestTokenBindingId = null,
public async Task<VerifyAssertionResult> MakeAssertionAsync(MakeAssertionParams makeAssertionParams,
CancellationToken cancellationToken = default)
{
var parsedResponse = AuthenticatorAssertionResponse.Parse(assertionResponse);
var parsedResponse = AuthenticatorAssertionResponse.Parse(makeAssertionParams.AssertionResponse);

var result = await parsedResponse.VerifyAsync(originalOptions,
var result = await parsedResponse.VerifyAsync(makeAssertionParams.OriginalOptions,
_config,
storedPublicKey,
storedDevicePublicKeys,
storedSignatureCounter,
isUserHandleOwnerOfCredentialIdCallback,
makeAssertionParams.StoredPublicKey,
makeAssertionParams.StoredDevicePublicKeys,
makeAssertionParams.StoredSignatureCounter,
makeAssertionParams.IsUserHandleOwnerOfCredentialIdCallback,
_metadataService,
requestTokenBindingId,
makeAssertionParams.RequestTokenBindingId,
cancellationToken);

return result;
Expand Down
15 changes: 2 additions & 13 deletions Src/Fido2/IFido2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,10 @@ AssertionOptions GetAssertionOptions(
UserVerificationRequirement? userVerification,
AuthenticationExtensionsClientInputs? extensions = null);

Task<VerifyAssertionResult> MakeAssertionAsync(
AuthenticatorAssertionRawResponse assertionResponse,
AssertionOptions originalOptions,
byte[] storedPublicKey,
IReadOnlyList<byte[]> storedDevicePublicKeys,
uint storedSignatureCounter,
IsUserHandleOwnerOfCredentialIdAsync isUserHandleOwnerOfCredentialIdCallback,
byte[]? requestTokenBindingId = null,
Task<VerifyAssertionResult> MakeAssertionAsync(MakeAssertionParams makeAssertionParams,
CancellationToken cancellationToken = default);

Task<RegisteredPublicKeyCredential> MakeNewCredentialAsync(
AuthenticatorAttestationRawResponse attestationResponse,
CredentialCreateOptions originalOptions,
IsCredentialIdUniqueToUserAsyncDelegate isCredentialIdUniqueToUser,
byte[]? requestTokenBindingId = null,
Task<RegisteredPublicKeyCredential> MakeNewCredentialAsync(MakeNewCredentialParams makeNewCredentialParams,
CancellationToken cancellationToken = default);

CredentialCreateOptions RequestNewCredential(
Expand Down
47 changes: 47 additions & 0 deletions Src/Fido2/MakeAssertionParams.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace Fido2NetLib;

/// <summary>
/// Wraps the input for the MakeAssertion function
/// </summary>
public sealed class MakeAssertionParams
{
/// <summary>
/// The assertion response from the authenticator.
/// </summary>
public required AuthenticatorAssertionRawResponse AssertionResponse { get; init; }

/// <summary>
/// The original options that was sent to the client.
/// </summary>
public required AssertionOptions OriginalOptions { get; init; }

/// <summary>
/// The stored credential public key.
/// </summary>
public required byte[] StoredPublicKey { get; init; }

/// <summary>
/// The stored value of the signature counter.
/// </summary>
public required uint StoredSignatureCounter { get; init; }

/// <summary>
/// The delegate used to validate that the user handle is indeed owned of the CredentialId.
/// </summary>
public required IsUserHandleOwnerOfCredentialIdAsync IsUserHandleOwnerOfCredentialIdCallback { get; init; }

/// <summary>
/// The stored device public keys.
/// </summary>
public IReadOnlyList<byte[]> StoredDevicePublicKeys { get; init; } = Array.Empty<byte[]>();

/// <summary>
/// DO NOT USE - Deprecated, but kept in code due to conformance testing tool.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public byte[]? RequestTokenBindingId { get; init; }
}
30 changes: 30 additions & 0 deletions Src/Fido2/MakeNewCredentialParams.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.ComponentModel;

namespace Fido2NetLib;

/// <summary>
/// Wraps the input for the MakeNewCredential function
/// </summary>
public sealed class MakeNewCredentialParams
{
/// <summary>
/// The attestation response from the authenticator.
/// </summary>
public required AuthenticatorAttestationRawResponse AttestationResponse { get; init; }

/// <summary>
/// The original options that was sent to the client.
/// </summary>
public required CredentialCreateOptions OriginalOptions { get; init; }

/// <summary>
/// The delegate used to validate that the CredentialID is unique to this user.
/// </summary>
public required IsCredentialIdUniqueToUserAsyncDelegate IsCredentialIdUniqueToUserCallback { get; init; }

/// <summary>
/// DO NOT USE - Deprecated, but kept in code due to conformance testing tool
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public byte[]? RequestTokenBindingId { get; init; }
}
7 changes: 6 additions & 1 deletion Tests/Fido2.Tests/Attestation/Apple.cs
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,12 @@ public async Task TestApplePublicKeyMismatch()
Origins = new HashSet<string> { "https://www.passwordless.dev" },
});

var credentialMakeResult = await lib.MakeNewCredentialAsync(attestationResponse, originalOptions, callback);
var credentialMakeResult = await lib.MakeNewCredentialAsync(new MakeNewCredentialParams
{
AttestationResponse = attestationResponse,
OriginalOptions = originalOptions,
IsCredentialIdUniqueToUserCallback = callback
});
}

private string[] StackAllocSha256(ReadOnlySpan<byte> authData, ReadOnlySpan<byte> clientDataJson)
Expand Down
Loading

0 comments on commit b2e7c15

Please sign in to comment.