From 1c003bfd60751d0e6863965428cd6c1899222d0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20=C3=85berg?= Date: Mon, 18 Nov 2024 17:14:25 +0100 Subject: [PATCH] Introduce wrapping objects for Options-methods (#562) * RequestNewCredentialParams * Create wrapper objects for GetAssertionOptinos * format * remove empty comment --- .../Server/Controllers/UserController.cs | 25 ++++++----- Demo/Controller.cs | 13 +++--- Demo/TestController.cs | 24 +++++++---- Src/Fido2/Fido2.cs | 43 ++++++------------- Src/Fido2/GetAssertionOptionsParams.cs | 35 +++++++++++++++ Src/Fido2/IFido2.cs | 17 +------- Src/Fido2/RequestNewCredentialParams.cs | 37 ++++++++++++++++ 7 files changed, 123 insertions(+), 71 deletions(-) create mode 100644 Src/Fido2/GetAssertionOptionsParams.cs create mode 100644 Src/Fido2/RequestNewCredentialParams.cs diff --git a/BlazorWasmDemo/Server/Controllers/UserController.cs b/BlazorWasmDemo/Server/Controllers/UserController.cs index 437e9ed8..5b648d00 100644 --- a/BlazorWasmDemo/Server/Controllers/UserController.cs +++ b/BlazorWasmDemo/Server/Controllers/UserController.cs @@ -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; @@ -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; diff --git a/Demo/Controller.cs b/Demo/Controller.cs index d2e64357..367a30c1 100644 --- a/Demo/Controller.cs +++ b/Demo/Controller.cs @@ -70,7 +70,7 @@ public JsonResult MakeCredentialOptions([FromForm] string username, CredProps = true }; - var options = _fido2.RequestNewCredential(user, existingKeys, authenticatorSelection, attType.ToEnum(), exts); + var options = _fido2.RequestNewCredential(new RequestNewCredentialParams { User = user, ExcludeCredentials = existingKeys, AuthenticatorSelection = authenticatorSelection, AttestationPreference = attType.ToEnum(), Extensions = exts }); // 4. Temporarily store options, session/in-memory cache/redis/db HttpContext.Session.SetString("fido2.attestationOptions", options.ToJson()); @@ -163,11 +163,12 @@ public ActionResult AssertionOptionsPost([FromForm] string username, [FromForm] // 3. Create options var uv = string.IsNullOrEmpty(userVerification) ? UserVerificationRequirement.Discouraged : userVerification.ToEnum(); - 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()); diff --git a/Demo/TestController.cs b/Demo/TestController.cs index 045962bb..ca916982 100644 --- a/Demo/TestController.cs +++ b/Demo/TestController.cs @@ -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 @@ -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()); diff --git a/Src/Fido2/Fido2.cs b/Src/Fido2/Fido2.cs index d2081ff5..a35fa853 100644 --- a/Src/Fido2/Fido2.cs +++ b/Src/Fido2/Fido2.cs @@ -26,37 +26,13 @@ public Fido2( /// /// Returns CredentialCreateOptions including a challenge to be sent to the browser/authenticator to create new credentials. /// - /// - /// 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. - /// + /// The input arguments for generating CredentialCreateOptions /// - public CredentialCreateOptions RequestNewCredential( - Fido2User user, - IReadOnlyList 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); - /// - /// Returns CredentialCreateOptions including a challenge to be sent to the browser/authenticator to create new credentials. - /// - /// - /// 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. - /// - /// This member is intended for use by Relying Parties that wish to express their preference for attestation conveyance. The default is none. - /// - /// - public CredentialCreateOptions RequestNewCredential( - Fido2User user, - IReadOnlyList excludeCredentials, - AuthenticatorSelection authenticatorSelection, - AttestationConveyancePreference attestationPreference, - AuthenticationExtensionsClientInputs? extensions = null) - { - byte[] challenge = RandomNumberGenerator.GetBytes(_config.ChallengeSize); - - return CredentialCreateOptions.Create(_config, challenge, user, authenticatorSelection, attestationPreference, excludeCredentials, extensions); } /// @@ -77,10 +53,15 @@ public async Task MakeNewCredentialAsync(MakeNewC /// /// Returns AssertionOptions including a challenge to the browser/authenticator to assert existing credentials and authenticate a user. /// - /// - /// - /// + /// The input arguments for generating AssertionOptions /// + 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 allowedCredentials, UserVerificationRequirement? userVerification, diff --git a/Src/Fido2/GetAssertionOptionsParams.cs b/Src/Fido2/GetAssertionOptionsParams.cs new file mode 100644 index 00000000..67ab69fe --- /dev/null +++ b/Src/Fido2/GetAssertionOptionsParams.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using Fido2NetLib.Objects; + +namespace Fido2NetLib; + +/// +/// The input arguments for generating AssertionOptions +/// +public sealed class GetAssertionOptionsParams +{ + /// + /// 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. + /// + public IReadOnlyList AllowedCredentials { get; init; } = Array.Empty(); + + /// + /// 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. + /// + public UserVerificationRequirement? UserVerification { get; init; } + + /// + /// The Relying Party MAY use this OPTIONAL member to provide client extension inputs requesting additional processing by the client and authenticator. + /// + public AuthenticationExtensionsClientInputs? Extensions { get; init; } +} diff --git a/Src/Fido2/IFido2.cs b/Src/Fido2/IFido2.cs index e22035c4..8d18ce4c 100644 --- a/Src/Fido2/IFido2.cs +++ b/Src/Fido2/IFido2.cs @@ -8,10 +8,7 @@ namespace Fido2NetLib; public interface IFido2 { - AssertionOptions GetAssertionOptions( - IReadOnlyList allowedCredentials, - UserVerificationRequirement? userVerification, - AuthenticationExtensionsClientInputs? extensions = null); + AssertionOptions GetAssertionOptions(GetAssertionOptionsParams getAssertionOptionsParams); Task MakeAssertionAsync(MakeAssertionParams makeAssertionParams, CancellationToken cancellationToken = default); @@ -19,15 +16,5 @@ Task MakeAssertionAsync(MakeAssertionParams makeAssertion Task MakeNewCredentialAsync(MakeNewCredentialParams makeNewCredentialParams, CancellationToken cancellationToken = default); - CredentialCreateOptions RequestNewCredential( - Fido2User user, - IReadOnlyList excludeCredentials, - AuthenticationExtensionsClientInputs? extensions = null); - - CredentialCreateOptions RequestNewCredential( - Fido2User user, - IReadOnlyList excludeCredentials, - AuthenticatorSelection authenticatorSelection, - AttestationConveyancePreference attestationPreference, - AuthenticationExtensionsClientInputs? extensions = null); + CredentialCreateOptions RequestNewCredential(RequestNewCredentialParams requestNewCredentialParams); } diff --git a/Src/Fido2/RequestNewCredentialParams.cs b/Src/Fido2/RequestNewCredentialParams.cs new file mode 100644 index 00000000..13877c75 --- /dev/null +++ b/Src/Fido2/RequestNewCredentialParams.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using Fido2NetLib.Objects; + +namespace Fido2NetLib; + +/// +/// The input arguments for generating CredentialCreateOptions +/// +public sealed class RequestNewCredentialParams +{ + /// + /// 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 + /// + public required Fido2User User { get; init; } + + /// + /// 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. + /// + public IReadOnlyList ExcludeCredentials { get; init; } = + Array.Empty(); + + /// + /// 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). + /// + public AuthenticatorSelection AuthenticatorSelection { get; init; } = AuthenticatorSelection.Default; + + /// + /// 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. + /// + public AttestationConveyancePreference AttestationPreference { get; init; } = AttestationConveyancePreference.None; + + /// + /// 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. + /// + public AuthenticationExtensionsClientInputs? Extensions { get; init; } +}