Skip to content

Commit

Permalink
Introduce wrapping objects for Options-methods (#562)
Browse files Browse the repository at this point in the history
* RequestNewCredentialParams

* Create wrapper objects for GetAssertionOptinos

* format

* remove empty comment
  • Loading branch information
abergs authored Nov 18, 2024
1 parent 3c6808e commit 1c003bf
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 71 deletions.
25 changes: 14 additions & 11 deletions BlazorWasmDemo/Server/Controllers/UserController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,18 +103,19 @@ public CredentialCreateOptions GetCredentialOptions(
}

// 4. Create options
var options = _fido2.RequestNewCredential(
user,
existingKeys,
authenticatorSelection,
attestationType ?? AttestationConveyancePreference.None,
new AuthenticationExtensionsClientInputs
var options = _fido2.RequestNewCredential(new RequestNewCredentialParams
{
User = user,
ExcludeCredentials = existingKeys,
AuthenticatorSelection = authenticatorSelection,
AttestationPreference = attestationType ?? AttestationConveyancePreference.None,
Extensions = new AuthenticationExtensionsClientInputs
{
Extensions = true,
UserVerificationMethod = true,
CredProps = true
}
);
});

// 5. Temporarily store options, session/in-memory cache/redis/db
_pendingCredentials[key] = options;
Expand Down Expand Up @@ -212,10 +213,12 @@ public AssertionOptions MakeAssertionOptions([FromRoute] string? username, [From
};

// 2. Create options (usernameless users will be prompted by their device to select a credential from their own list)
var options = _fido2.GetAssertionOptions(
existingKeys,
userVerification ?? UserVerificationRequirement.Discouraged,
exts);
var options = _fido2.GetAssertionOptions(new GetAssertionOptionsParams
{
AllowedCredentials = existingKeys,
UserVerification = userVerification ?? UserVerificationRequirement.Discouraged,
Extensions = exts
});

// 4. Temporarily store options, session/in-memory cache/redis/db
_pendingAssertions[new string(options.Challenge.Select(b => (char)b).ToArray())] = options;
Expand Down
13 changes: 7 additions & 6 deletions Demo/Controller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public JsonResult MakeCredentialOptions([FromForm] string username,
CredProps = true
};

var options = _fido2.RequestNewCredential(user, existingKeys, authenticatorSelection, attType.ToEnum<AttestationConveyancePreference>(), exts);
var options = _fido2.RequestNewCredential(new RequestNewCredentialParams { User = user, ExcludeCredentials = existingKeys, AuthenticatorSelection = authenticatorSelection, AttestationPreference = attType.ToEnum<AttestationConveyancePreference>(), Extensions = exts });

// 4. Temporarily store options, session/in-memory cache/redis/db
HttpContext.Session.SetString("fido2.attestationOptions", options.ToJson());
Expand Down Expand Up @@ -163,11 +163,12 @@ public ActionResult AssertionOptionsPost([FromForm] string username, [FromForm]

// 3. Create options
var uv = string.IsNullOrEmpty(userVerification) ? UserVerificationRequirement.Discouraged : userVerification.ToEnum<UserVerificationRequirement>();
var options = _fido2.GetAssertionOptions(
existingCredentials,
uv,
exts
);
var options = _fido2.GetAssertionOptions(new GetAssertionOptionsParams()
{
AllowedCredentials = existingCredentials,
UserVerification = uv,
Extensions = exts
});

// 4. Temporarily store options, session/in-memory cache/redis/db
HttpContext.Session.SetString("fido2.assertionOptions", options.ToJson());
Expand Down
24 changes: 16 additions & 8 deletions Demo/TestController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,16 @@ public OkObjectResult MakeCredentialOptionsTest([FromBody] TEST_MakeCredentialPa
exts.Example = opts.Extensions.Example;

// 3. Create options
var options = _fido2.RequestNewCredential(user, existingKeys, opts.AuthenticatorSelection, opts.Attestation, exts);

// 4. Temporarily store options, session/in-memory cache/redis/db
var options = _fido2.RequestNewCredential(new RequestNewCredentialParams
{
User = user,
ExcludeCredentials = existingKeys,
AuthenticatorSelection = opts.AuthenticatorSelection,
AttestationPreference = opts.Attestation,
Extensions = exts
});

// 4. Temporarily store options, session/in-memory cache/redis/db
HttpContext.Session.SetString("fido2.attestationOptions", options.ToJson());

// 5. return options to client
Expand Down Expand Up @@ -146,11 +153,12 @@ public IActionResult AssertionOptionsTest([FromBody] TEST_AssertionClientParams
exts.Example = assertionClientParams.Extensions.Example;

// 3. Create options
var options = _fido2.GetAssertionOptions(
existingCredentials,
uv,
exts
);
var options = _fido2.GetAssertionOptions(new GetAssertionOptionsParams
{
AllowedCredentials = existingCredentials,
UserVerification = uv,
Extensions = exts
});

// 4. Temporarily store options, session/in-memory cache/redis/db
HttpContext.Session.SetString("fido2.assertionOptions", options.ToJson());
Expand Down
43 changes: 12 additions & 31 deletions Src/Fido2/Fido2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,37 +26,13 @@ public Fido2(
/// <summary>
/// Returns CredentialCreateOptions including a challenge to be sent to the browser/authenticator to create new credentials.
/// </summary>
/// <param name="user"></param>
/// <param name="excludeCredentials">Recommended. This member is intended for use by Relying Parties that wish to limit the creation of multiple credentials for the same account on a single authenticator. The client is requested to return an error if the new credential would be created on an authenticator that also contains one of the credentials enumerated in this parameter.</param>
/// <param name="extensions"></param>
/// <param name="requestNewCredentialParams">The input arguments for generating CredentialCreateOptions</param>
/// <returns></returns>
public CredentialCreateOptions RequestNewCredential(
Fido2User user,
IReadOnlyList<PublicKeyCredentialDescriptor> excludeCredentials,
AuthenticationExtensionsClientInputs? extensions = null)
public CredentialCreateOptions RequestNewCredential(RequestNewCredentialParams requestNewCredentialParams)
{
return RequestNewCredential(user, excludeCredentials, AuthenticatorSelection.Default, AttestationConveyancePreference.None, extensions);
}
var challenge = RandomNumberGenerator.GetBytes(_config.ChallengeSize);
return CredentialCreateOptions.Create(_config, challenge, requestNewCredentialParams.User, requestNewCredentialParams.AuthenticatorSelection, requestNewCredentialParams.AttestationPreference, requestNewCredentialParams.ExcludeCredentials, requestNewCredentialParams.Extensions);

/// <summary>
/// Returns CredentialCreateOptions including a challenge to be sent to the browser/authenticator to create new credentials.
/// </summary>
/// <param name="user"></param>
/// <param name="excludeCredentials">Recommended. This member is intended for use by Relying Parties that wish to limit the creation of multiple credentials for the same account on a single authenticator. The client is requested to return an error if the new credential would be created on an authenticator that also contains one of the credentials enumerated in this parameter.</param>
/// <param name="authenticatorSelection"></param>
/// <param name="attestationPreference">This member is intended for use by Relying Parties that wish to express their preference for attestation conveyance. The default is none.</param>
/// <param name="extensions"></param>
/// <returns></returns>
public CredentialCreateOptions RequestNewCredential(
Fido2User user,
IReadOnlyList<PublicKeyCredentialDescriptor> excludeCredentials,
AuthenticatorSelection authenticatorSelection,
AttestationConveyancePreference attestationPreference,
AuthenticationExtensionsClientInputs? extensions = null)
{
byte[] challenge = RandomNumberGenerator.GetBytes(_config.ChallengeSize);

return CredentialCreateOptions.Create(_config, challenge, user, authenticatorSelection, attestationPreference, excludeCredentials, extensions);
}

/// <summary>
Expand All @@ -77,10 +53,15 @@ public async Task<RegisteredPublicKeyCredential> MakeNewCredentialAsync(MakeNewC
/// <summary>
/// Returns AssertionOptions including a challenge to the browser/authenticator to assert existing credentials and authenticate a user.
/// </summary>
/// <param name="allowedCredentials"></param>
/// <param name="userVerification"></param>
/// <param name="extensions"></param>
/// <param name="getAssertionOptionsParams">The input arguments for generating AssertionOptions</param>
/// <returns></returns>
public AssertionOptions GetAssertionOptions(GetAssertionOptionsParams getAssertionOptionsParams)
{
byte[] challenge = RandomNumberGenerator.GetBytes(_config.ChallengeSize);

return AssertionOptions.Create(_config, challenge, getAssertionOptionsParams.AllowedCredentials, getAssertionOptionsParams.UserVerification, getAssertionOptionsParams.Extensions);
}

public AssertionOptions GetAssertionOptions(
IReadOnlyList<PublicKeyCredentialDescriptor> allowedCredentials,
UserVerificationRequirement? userVerification,
Expand Down
35 changes: 35 additions & 0 deletions Src/Fido2/GetAssertionOptionsParams.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using Fido2NetLib.Objects;

namespace Fido2NetLib;

/// <summary>
/// The input arguments for generating AssertionOptions
/// </summary>
public sealed class GetAssertionOptionsParams
{
/// <summary>
/// This OPTIONAL member is used by the client to find authenticators eligible for this authentication ceremony. It can be used in two ways:
///
/// * If the user account to authenticate is already identified (e.g., if the user has entered a username), then the Relying Party SHOULD use this member to list credential descriptors for credential records in the user account. This SHOULD usually include all credential records in the user account.
/// The items SHOULD specify transports whenever possible. This helps the client optimize the user experience for any given situation. Also note that the Relying Party does not need to filter the list when requesting user verification — the client will automatically ignore non-eligible credentials if userVerification is set to required.
/// See also the § 14.6.3 Privacy leak via credential IDs privacy consideration.
/// * If the user account to authenticate is not already identified, then the Relying Party MAY leave this member empty or unspecified. In this case, only discoverable credentials will be utilized in this authentication ceremony, and the user account MAY be identified by the userHandle of the resulting AuthenticatorAssertionResponse. If the available authenticators contain more than one discoverable credential scoped to the Relying Party, the credentials are displayed by the client platform or authenticator for the user to select from (see step 7 of § 6.3.3 The authenticatorGetAssertion Operation).
///
/// If not empty, the client MUST return an error if none of the listed credentials can be used.
///
/// The list is ordered in descending order of preference: the first item in the list is the most preferred credential, and the last is the least preferred.
/// </summary>
public IReadOnlyList<PublicKeyCredentialDescriptor> AllowedCredentials { get; init; } = Array.Empty<PublicKeyCredentialDescriptor>();

/// <summary>
/// This OPTIONAL member specifies the Relying Party's requirements regarding user verification for the get() operation. The value SHOULD be a member of UserVerificationRequirement but client platforms MUST ignore unknown values, treating an unknown value as if the member does not exist. Eligible authenticators are filtered to only those capable of satisfying this requirement.
/// </summary>
public UserVerificationRequirement? UserVerification { get; init; }

/// <summary>
/// The Relying Party MAY use this OPTIONAL member to provide client extension inputs requesting additional processing by the client and authenticator.
/// </summary>
public AuthenticationExtensionsClientInputs? Extensions { get; init; }
}
17 changes: 2 additions & 15 deletions Src/Fido2/IFido2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,13 @@ namespace Fido2NetLib;

public interface IFido2
{
AssertionOptions GetAssertionOptions(
IReadOnlyList<PublicKeyCredentialDescriptor> allowedCredentials,
UserVerificationRequirement? userVerification,
AuthenticationExtensionsClientInputs? extensions = null);
AssertionOptions GetAssertionOptions(GetAssertionOptionsParams getAssertionOptionsParams);

Task<VerifyAssertionResult> MakeAssertionAsync(MakeAssertionParams makeAssertionParams,
CancellationToken cancellationToken = default);

Task<RegisteredPublicKeyCredential> MakeNewCredentialAsync(MakeNewCredentialParams makeNewCredentialParams,
CancellationToken cancellationToken = default);

CredentialCreateOptions RequestNewCredential(
Fido2User user,
IReadOnlyList<PublicKeyCredentialDescriptor> excludeCredentials,
AuthenticationExtensionsClientInputs? extensions = null);

CredentialCreateOptions RequestNewCredential(
Fido2User user,
IReadOnlyList<PublicKeyCredentialDescriptor> excludeCredentials,
AuthenticatorSelection authenticatorSelection,
AttestationConveyancePreference attestationPreference,
AuthenticationExtensionsClientInputs? extensions = null);
CredentialCreateOptions RequestNewCredential(RequestNewCredentialParams requestNewCredentialParams);
}
37 changes: 37 additions & 0 deletions Src/Fido2/RequestNewCredentialParams.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using Fido2NetLib.Objects;

namespace Fido2NetLib;

/// <summary>
/// The input arguments for generating CredentialCreateOptions
/// </summary>
public sealed class RequestNewCredentialParams
{
/// <summary>
/// This member contains names and an identifier for the user account performing the registration. Its value’s name, displayName and id members are REQUIRED. id can be returned as the userHandle in some future authentication ceremonies, and is used to overwrite existing discoverable credentials that have the same rp.id and user.id on the same authenticator. name and displayName MAY be used by the authenticator and client in future authentication ceremonies to help the user select a credential, but are not returned to the Relying Party as a result of future authentication ceremonies
/// </summary>
public required Fido2User User { get; init; }

/// <summary>
/// The Relying Party SHOULD use this OPTIONAL member to list any existing credentials mapped to this user account (as identified by user.id). This ensures that the new credential is not created on an authenticator that already contains a credential mapped to this user account. If it would be, the client is requested to instead guide the user to use a different authenticator, or return an error if that fails.
/// </summary>
public IReadOnlyList<PublicKeyCredentialDescriptor> ExcludeCredentials { get; init; } =
Array.Empty<PublicKeyCredentialDescriptor>();

/// <summary>
/// The Relying Party MAY use this OPTIONAL member to specify capabilities and settings that the authenticator MUST or SHOULD satisfy to participate in the create() operation. See § 5.4.4 Authenticator Selection Criteria (dictionary AuthenticatorSelectionCriteria).
/// </summary>
public AuthenticatorSelection AuthenticatorSelection { get; init; } = AuthenticatorSelection.Default;

/// <summary>
/// The Relying Party MAY use this OPTIONAL member to specify a preference regarding attestation conveyance. Its value SHOULD be a member of AttestationConveyancePreference. Client platforms MUST ignore unknown values, treating an unknown value as if the member does not exist.
/// </summary>
public AttestationConveyancePreference AttestationPreference { get; init; } = AttestationConveyancePreference.None;

/// <summary>
/// The Relying Party MAY use this OPTIONAL member to provide client extension inputs requesting additional processing by the client and authenticator. For example, the Relying Party may request that the client returns additional information about the credential that was created.
/// </summary>
public AuthenticationExtensionsClientInputs? Extensions { get; init; }
}

0 comments on commit 1c003bf

Please sign in to comment.