From 39ef9132582cab66d26aac62273b5ebf32c23ae2 Mon Sep 17 00:00:00 2001 From: czf Date: Thu, 28 Dec 2023 14:45:28 -0800 Subject: [PATCH 1/4] Adding authenticator enrollment and verification to authentication client --- .../AuthenticationApiClient.cs | 49 +++++++++++ .../IAuthenticationApiClient.cs | 23 +++++ .../AssociateNewAuthenticatorRequest.cs | 88 +++++++++++++++++++ .../AssociateNewAuthenticatorResponse.cs | 29 ++++++ .../Models/MfaOobTokenRequest.cs | 32 +++++++ .../Models/MfaOobTokenResponse.cs | 19 ++++ .../MfaTests.cs | 60 +++++++++++++ 7 files changed, 300 insertions(+) create mode 100644 src/Auth0.AuthenticationApi/Models/AssociateNewAuthenticatorRequest.cs create mode 100644 src/Auth0.AuthenticationApi/Models/AssociateNewAuthenticatorResponse.cs create mode 100644 src/Auth0.AuthenticationApi/Models/MfaOobTokenRequest.cs create mode 100644 src/Auth0.AuthenticationApi/Models/MfaOobTokenResponse.cs create mode 100644 tests/Auth0.AuthenticationApi.IntegrationTests/MfaTests.cs diff --git a/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs b/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs index 2d3c32970..fb12a3293 100644 --- a/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs +++ b/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Dynamic; +using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -350,7 +351,30 @@ await AssertIdTokenValidIfExisting(response.IdToken, request.ClientId, request.S } /// + public async Task GetTokenAsync(MfaOobTokenRequest request, CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException(nameof(request)); + } + + var body = new Dictionary() + { + { "grant_type", "http://auth0.com/oauth/grant-type/mfa-oob" }, + { "client_id", request.ClientId }, + { "client_secret", request.ClientSecret }, + { "mfa_token", request.MfaToken}, + { "oob_code", request.OobCode}, + { "binding_code", request.BindingCode} + }; + return await connection.SendAsync( + HttpMethod.Post, + tokenUri, + body, + cancellationToken: cancellationToken); + } + /// public Task RevokeRefreshTokenAsync(RevokeRefreshTokenRequest request, CancellationToken cancellationToken = default) { if (request == null) @@ -494,6 +518,31 @@ public Task PushedAuthorizationRequestAsync( cancellationToken: cancellationToken ); } + + /// + public Task AssociateNewAuthenticatorAsync(AssociateNewAuthenticatorRequest request, CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException(nameof(request)); + } + if (!request.IsValid(out List validationErrors)) + { + if (validationErrors.Count == 1) + { + throw new InvalidOperationException(validationErrors.First()); + } + + throw new InvalidOperationException(validationErrors.Aggregate((x, y) => x + "\n" + y)); + } + + return connection.SendAsync( + HttpMethod.Post, + BuildUri("mfa/associate"), + request, + BuildHeaders(request.Token), + cancellationToken); + } /// /// Disposes of any owned disposable resources such as a . diff --git a/src/Auth0.AuthenticationApi/IAuthenticationApiClient.cs b/src/Auth0.AuthenticationApi/IAuthenticationApiClient.cs index 268ad63eb..440654058 100644 --- a/src/Auth0.AuthenticationApi/IAuthenticationApiClient.cs +++ b/src/Auth0.AuthenticationApi/IAuthenticationApiClient.cs @@ -125,6 +125,15 @@ public interface IAuthenticationApiClient : IDisposable /// Task GetTokenAsync(DeviceCodeTokenRequest request, CancellationToken cancellationToken = default); + /// + /// Requests an Access Token using Oob MFA verification. + /// + /// containing request details to verify oob. + /// The cancellation token to cancel operation. + /// representing the async operation containing + /// a with the requested tokens. + Task GetTokenAsync(MfaOobTokenRequest request, CancellationToken cancellationToken = default); + /// /// Revokes refresh token provided in request. /// @@ -180,5 +189,19 @@ public interface IAuthenticationApiClient : IDisposable /// a with the details of the response. Task PushedAuthorizationRequestAsync(PushedAuthorizationRequest request, CancellationToken cancellationToken = default); + + /// + /// Sends a Mfa enrollment request + /// + /// containing information to enroll a new Authenticator. + /// The cancellation token to cancel operation. + /// representing the async operation containing + /// a with the details of the response. + /// + Task AssociateNewAuthenticatorAsync( + AssociateNewAuthenticatorRequest request, + CancellationToken cancellationToken = default); + + } } diff --git a/src/Auth0.AuthenticationApi/Models/AssociateNewAuthenticatorRequest.cs b/src/Auth0.AuthenticationApi/Models/AssociateNewAuthenticatorRequest.cs new file mode 100644 index 000000000..7ef764b35 --- /dev/null +++ b/src/Auth0.AuthenticationApi/Models/AssociateNewAuthenticatorRequest.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; + +namespace Auth0.AuthenticationApi.Models +{ + public class AssociateNewAuthenticatorRequest + { + [JsonIgnore] + public string Token { get; set; } + + /// Your application's Client ID. + [JsonProperty("client_id", Required = Required.Always)] + public string ClientId { get; set; } + + /// + /// A JWT containing a signed assertion with your application credentials. Required when Private Key JWT is your application authentication + /// method. + /// + [JsonProperty("client_assertion")] + public string ClientAssertion { get; set; } + + /// + /// Your application's Client Secret. Required when the Token Endpoint Authentication Method field in your Application Settings is Post or + /// Basic. + /// + [JsonProperty("client_secret")] + public string ClientSecret { get; set; } + + /// + /// The value is urn:ietf:params:oauth:client-assertion-type:jwt-bearer. Required when Private Key JWT is the application authentication + /// method. + /// + [JsonProperty("client_assertion_type")] + public string ClientAssertionType { get; set; } + + /// The type of authenticators supported by the client. Value is an array with values "otp" or "oob". + [JsonProperty("authenticator_types", Required = Required.Always)] + public List AuthenticatorTypes { get; set; } + + /// + /// The type of OOB channels supported by the client. An array with values "auth0", "sms", "voice". Required if authenticator_types include + /// oob. + /// + [JsonProperty("oob_channels")] + public List OobChannels { get; set; } + + /// The phone number to use for SMS or Voice. Required if oob_channels includes sms or voice. + [JsonProperty("phone_number")] + public string PhoneNumber { get; set; } + + internal bool IsValid(out List validationErrors) + { + validationErrors = new List(); + if (string.IsNullOrEmpty(Token)) + { + validationErrors.Add($"{nameof(Token)} is required"); + } + + if (string.IsNullOrEmpty(ClientId)) + { + validationErrors.Add($"{nameof(ClientId)} is required"); + } + + if (AuthenticatorTypes == null || AuthenticatorTypes.Count == 0) + { + validationErrors.Add($"{nameof(AuthenticatorTypes)} is required"); + } + else if (AuthenticatorTypes.Contains("oob")) + { + if (OobChannels == null || OobChannels.Count == 0) + { + validationErrors.Add($"{nameof(OobChannels)} is required when {nameof(AuthenticatorTypes)} includes 'oob'"); + } + else if (OobChannels.Any(x => x != "auth0" && x != "sms" && x != "voice")) + { + validationErrors.Add($"{nameof(OobChannels)} can only include 'auth0', 'sms', 'voice'"); + } + else if (OobChannels.Any(x => x == "sms" || x == "voice") && string.IsNullOrEmpty(PhoneNumber)) + { + validationErrors.Add($"{nameof(PhoneNumber)} is Required when {nameof(OobChannels)} includes 'sms' or 'voice'"); + } + } + + return validationErrors.Count == 0; + } + } +} diff --git a/src/Auth0.AuthenticationApi/Models/AssociateNewAuthenticatorResponse.cs b/src/Auth0.AuthenticationApi/Models/AssociateNewAuthenticatorResponse.cs new file mode 100644 index 000000000..d01830ce5 --- /dev/null +++ b/src/Auth0.AuthenticationApi/Models/AssociateNewAuthenticatorResponse.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Auth0.AuthenticationApi.Models +{ + public class AssociateNewAuthenticatorResponse + { + [JsonProperty("oob_code")] + public string OobCode { get; set; } + + [JsonProperty("binding_method")] + public string BindingMethod { get; set; } + + [JsonProperty("secret")] + public string Secret { get; set; } + + [JsonProperty("barcode_uri")] + public string BarcodeUri { get; set; } + + [JsonProperty("authenticator_type")] + public string AuthenticatorType { get; set; } + + [JsonProperty("oob_channel")] + public string OobChannel { get; set; } + + [JsonProperty("recovery_codes")] + public List RecoveryCodes { get; set; } + } +} diff --git a/src/Auth0.AuthenticationApi/Models/MfaOobTokenRequest.cs b/src/Auth0.AuthenticationApi/Models/MfaOobTokenRequest.cs new file mode 100644 index 000000000..844cdc210 --- /dev/null +++ b/src/Auth0.AuthenticationApi/Models/MfaOobTokenRequest.cs @@ -0,0 +1,32 @@ +namespace Auth0.AuthenticationApi.Models +{ + public class MfaOobTokenRequest + { + /// + /// Your application's Client ID. + /// + public string ClientId { get; set; } + + /// + /// Your application's Client Secret. + /// Required when the Token Endpoint Authentication Method field at your Application Settings is Post or Basic. + /// + public string ClientSecret { get; set; } + + /// + /// The mfa_token you received from mfa_required error or access token with enroll scope and audience: https://{yourDomain}/mfa/ + /// + public string MfaToken { get; set; } + + /// + /// The oob code received from the challenge request. + /// + public string OobCode { get; set; } + + /// + /// A code used to bind the side channel (used to deliver the challenge) with the main channel you are using to authenticate. + /// This is usually an OTP-like code delivered as part of the challenge message. + /// + public string BindingCode { get; set; } + } +} diff --git a/src/Auth0.AuthenticationApi/Models/MfaOobTokenResponse.cs b/src/Auth0.AuthenticationApi/Models/MfaOobTokenResponse.cs new file mode 100644 index 000000000..c0dc93638 --- /dev/null +++ b/src/Auth0.AuthenticationApi/Models/MfaOobTokenResponse.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; + +namespace Auth0.AuthenticationApi.Models +{ + public class MfaOobTokenResponse : TokenBase + { + /// + /// The value of the different scopes issued in the token + /// + [JsonProperty("scope")] + public string Scope { get; set; } + + /// + /// The lifetime (in seconds) of the token + /// + [JsonProperty("expires_in")] + public int ExpiresIn { get; set; } + } +} diff --git a/tests/Auth0.AuthenticationApi.IntegrationTests/MfaTests.cs b/tests/Auth0.AuthenticationApi.IntegrationTests/MfaTests.cs new file mode 100644 index 000000000..2fcbcbb5d --- /dev/null +++ b/tests/Auth0.AuthenticationApi.IntegrationTests/MfaTests.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Auth0.AuthenticationApi.Models; +using Auth0.Tests.Shared; +using FluentAssertions; +using Xunit; + +namespace Auth0.AuthenticationApi.IntegrationTests +{ + public class MfaTests : TestBase + { + private readonly AuthenticationApiClient _authenticationApiClient; + + public MfaTests() + { + _authenticationApiClient = new AuthenticationApiClient(GetVariable("AUTH0_AUTHENTICATION_API_URL")); + } + + [Fact(Skip = "Run manually")] + public async Task Should_Receive_Associate_Response_For_Sms_Mfa_Enrollment() + { + var request = + new AssociateNewAuthenticatorRequest() + { + Token = TestBaseUtils.GetVariable("AUTH0_AUTHENTICATOR_ENROLL_TOKEN"), + ClientId = TestBaseUtils.GetVariable("AUTH0_CLIENT_ID"), + ClientSecret = TestBaseUtils.GetVariable("AUTH0_CLIENT_SECRET"), + AuthenticatorTypes = new List() { "oob" }, + OobChannels = new List() { "sms" }, + PhoneNumber = TestBaseUtils.GetVariable("MFA_PHONE_NUMBER") + }; + var response = await _authenticationApiClient.AssociateNewAuthenticatorAsync(request); + response.Should().NotBeNull(); + response.AuthenticatorType.Should().Be("oob"); + response.BindingMethod.Should().Be("prompt"); + response.OobChannel.Should().Be("sms"); + + response.OobCode.Should().NotBeNullOrEmpty().And.StartWith("Fe26."); + } + + [Fact(Skip = "Run manually")] + public async Task Should_Receive_MfaOobTokenResponse_For_Oob_Mfa_Verification() + { + var request = new MfaOobTokenRequest() + { + ClientId = TestBaseUtils.GetVariable("AUTH0_CLIENT_ID"), + ClientSecret = TestBaseUtils.GetVariable("AUTH0_CLIENT_SECRET"), + MfaToken = TestBaseUtils.GetVariable("MFA_TOKEN"), + OobCode = TestBaseUtils.GetVariable("MFA_OOB_CODE"), + BindingCode = TestBaseUtils.GetVariable("MFA_BINDING_CODE") + }; + + var response = await _authenticationApiClient.GetTokenAsync(request); + response.Should().NotBeNull(); + response.AccessToken.Should().StartWith("ey"); + response.ExpiresIn.Should().BeGreaterThan(0); + response.TokenType.Should().Be("Bearer"); + } + } +} From 841b2b2ad316a41bfe4818e4633d728df3c3fc8a Mon Sep 17 00:00:00 2001 From: czf Date: Thu, 11 Jan 2024 11:46:26 -0800 Subject: [PATCH 2/4] requested changes from PR --- .../AuthenticationApiClient.cs | 11 +----- .../IAuthenticationApiClient.cs | 4 +- ...cs => AssociateMfaAuthenticatorRequest.cs} | 38 +------------------ .../MfaTests.cs | 2 +- 4 files changed, 5 insertions(+), 50 deletions(-) rename src/Auth0.AuthenticationApi/Models/{AssociateNewAuthenticatorRequest.cs => AssociateMfaAuthenticatorRequest.cs} (57%) diff --git a/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs b/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs index fb12a3293..429c07ea4 100644 --- a/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs +++ b/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs @@ -520,21 +520,12 @@ public Task PushedAuthorizationRequestAsync( } /// - public Task AssociateNewAuthenticatorAsync(AssociateNewAuthenticatorRequest request, CancellationToken cancellationToken = default) + public Task AssociateNewAuthenticatorAsync(AssociateMfaAuthenticatorRequest request, CancellationToken cancellationToken = default) { if (request == null) { throw new ArgumentNullException(nameof(request)); } - if (!request.IsValid(out List validationErrors)) - { - if (validationErrors.Count == 1) - { - throw new InvalidOperationException(validationErrors.First()); - } - - throw new InvalidOperationException(validationErrors.Aggregate((x, y) => x + "\n" + y)); - } return connection.SendAsync( HttpMethod.Post, diff --git a/src/Auth0.AuthenticationApi/IAuthenticationApiClient.cs b/src/Auth0.AuthenticationApi/IAuthenticationApiClient.cs index 440654058..b9000d0f5 100644 --- a/src/Auth0.AuthenticationApi/IAuthenticationApiClient.cs +++ b/src/Auth0.AuthenticationApi/IAuthenticationApiClient.cs @@ -193,13 +193,13 @@ Task PushedAuthorizationRequestAsync(PushedA /// /// Sends a Mfa enrollment request /// - /// containing information to enroll a new Authenticator. + /// containing information to enroll a new Authenticator. /// The cancellation token to cancel operation. /// representing the async operation containing /// a with the details of the response. /// Task AssociateNewAuthenticatorAsync( - AssociateNewAuthenticatorRequest request, + AssociateMfaAuthenticatorRequest request, CancellationToken cancellationToken = default); diff --git a/src/Auth0.AuthenticationApi/Models/AssociateNewAuthenticatorRequest.cs b/src/Auth0.AuthenticationApi/Models/AssociateMfaAuthenticatorRequest.cs similarity index 57% rename from src/Auth0.AuthenticationApi/Models/AssociateNewAuthenticatorRequest.cs rename to src/Auth0.AuthenticationApi/Models/AssociateMfaAuthenticatorRequest.cs index 7ef764b35..f1c47bba1 100644 --- a/src/Auth0.AuthenticationApi/Models/AssociateNewAuthenticatorRequest.cs +++ b/src/Auth0.AuthenticationApi/Models/AssociateMfaAuthenticatorRequest.cs @@ -4,7 +4,7 @@ namespace Auth0.AuthenticationApi.Models { - public class AssociateNewAuthenticatorRequest + public class AssociateMfaAuthenticatorRequest { [JsonIgnore] public string Token { get; set; } @@ -48,41 +48,5 @@ public class AssociateNewAuthenticatorRequest /// The phone number to use for SMS or Voice. Required if oob_channels includes sms or voice. [JsonProperty("phone_number")] public string PhoneNumber { get; set; } - - internal bool IsValid(out List validationErrors) - { - validationErrors = new List(); - if (string.IsNullOrEmpty(Token)) - { - validationErrors.Add($"{nameof(Token)} is required"); - } - - if (string.IsNullOrEmpty(ClientId)) - { - validationErrors.Add($"{nameof(ClientId)} is required"); - } - - if (AuthenticatorTypes == null || AuthenticatorTypes.Count == 0) - { - validationErrors.Add($"{nameof(AuthenticatorTypes)} is required"); - } - else if (AuthenticatorTypes.Contains("oob")) - { - if (OobChannels == null || OobChannels.Count == 0) - { - validationErrors.Add($"{nameof(OobChannels)} is required when {nameof(AuthenticatorTypes)} includes 'oob'"); - } - else if (OobChannels.Any(x => x != "auth0" && x != "sms" && x != "voice")) - { - validationErrors.Add($"{nameof(OobChannels)} can only include 'auth0', 'sms', 'voice'"); - } - else if (OobChannels.Any(x => x == "sms" || x == "voice") && string.IsNullOrEmpty(PhoneNumber)) - { - validationErrors.Add($"{nameof(PhoneNumber)} is Required when {nameof(OobChannels)} includes 'sms' or 'voice'"); - } - } - - return validationErrors.Count == 0; - } } } diff --git a/tests/Auth0.AuthenticationApi.IntegrationTests/MfaTests.cs b/tests/Auth0.AuthenticationApi.IntegrationTests/MfaTests.cs index 2fcbcbb5d..05ffd0374 100644 --- a/tests/Auth0.AuthenticationApi.IntegrationTests/MfaTests.cs +++ b/tests/Auth0.AuthenticationApi.IntegrationTests/MfaTests.cs @@ -20,7 +20,7 @@ public MfaTests() public async Task Should_Receive_Associate_Response_For_Sms_Mfa_Enrollment() { var request = - new AssociateNewAuthenticatorRequest() + new AssociateMfaAuthenticatorRequest() { Token = TestBaseUtils.GetVariable("AUTH0_AUTHENTICATOR_ENROLL_TOKEN"), ClientId = TestBaseUtils.GetVariable("AUTH0_CLIENT_ID"), From 0a09a7ca425567b8f7c50104fc7599dd0eb346ba Mon Sep 17 00:00:00 2001 From: czf <2455501+czf@users.noreply.github.com> Date: Tue, 16 Jan 2024 13:59:00 -0800 Subject: [PATCH 3/4] Apply suggestions from code review Co-authored-by: Frederik Prijck --- .../AuthenticationApiClient.cs | 8 ++++++-- .../IAuthenticationApiClient.cs | 4 ++-- .../Models/AssociateMfaAuthenticatorRequest.cs | 4 ++-- ...ponse.cs => AssociateMfaAuthenticatorResponse.cs} | 2 +- .../Models/MfaOobTokenRequest.cs | 12 +++++++++++- 5 files changed, 22 insertions(+), 8 deletions(-) rename src/Auth0.AuthenticationApi/Models/{AssociateNewAuthenticatorResponse.cs => AssociateMfaAuthenticatorResponse.cs} (93%) diff --git a/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs b/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs index 429c07ea4..beb735f86 100644 --- a/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs +++ b/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs @@ -365,8 +365,12 @@ public async Task GetTokenAsync(MfaOobTokenRequest request, { "client_secret", request.ClientSecret }, { "mfa_token", request.MfaToken}, { "oob_code", request.OobCode}, - { "binding_code", request.BindingCode} }; + + body.AddIfNotEmpty("binding_code", request.BindingCode); + + ApplyClientAuthentication(request, body); + return await connection.SendAsync( HttpMethod.Post, tokenUri, @@ -520,7 +524,7 @@ public Task PushedAuthorizationRequestAsync( } /// - public Task AssociateNewAuthenticatorAsync(AssociateMfaAuthenticatorRequest request, CancellationToken cancellationToken = default) + public Task AssociateMfaAuthenticatorAsync(AssociateMfaAuthenticatorRequest request, CancellationToken cancellationToken = default) { if (request == null) { diff --git a/src/Auth0.AuthenticationApi/IAuthenticationApiClient.cs b/src/Auth0.AuthenticationApi/IAuthenticationApiClient.cs index b9000d0f5..6724c3092 100644 --- a/src/Auth0.AuthenticationApi/IAuthenticationApiClient.cs +++ b/src/Auth0.AuthenticationApi/IAuthenticationApiClient.cs @@ -196,9 +196,9 @@ Task PushedAuthorizationRequestAsync(PushedA /// containing information to enroll a new Authenticator. /// The cancellation token to cancel operation. /// representing the async operation containing - /// a with the details of the response. + /// a with the details of the response. /// - Task AssociateNewAuthenticatorAsync( + Task AssociateMfaAuthenticatorAsync( AssociateMfaAuthenticatorRequest request, CancellationToken cancellationToken = default); diff --git a/src/Auth0.AuthenticationApi/Models/AssociateMfaAuthenticatorRequest.cs b/src/Auth0.AuthenticationApi/Models/AssociateMfaAuthenticatorRequest.cs index f1c47bba1..da2a96a01 100644 --- a/src/Auth0.AuthenticationApi/Models/AssociateMfaAuthenticatorRequest.cs +++ b/src/Auth0.AuthenticationApi/Models/AssociateMfaAuthenticatorRequest.cs @@ -10,7 +10,7 @@ public class AssociateMfaAuthenticatorRequest public string Token { get; set; } /// Your application's Client ID. - [JsonProperty("client_id", Required = Required.Always)] + [JsonProperty("client_id")] public string ClientId { get; set; } /// @@ -35,7 +35,7 @@ public class AssociateMfaAuthenticatorRequest public string ClientAssertionType { get; set; } /// The type of authenticators supported by the client. Value is an array with values "otp" or "oob". - [JsonProperty("authenticator_types", Required = Required.Always)] + [JsonProperty("authenticator_types")] public List AuthenticatorTypes { get; set; } /// diff --git a/src/Auth0.AuthenticationApi/Models/AssociateNewAuthenticatorResponse.cs b/src/Auth0.AuthenticationApi/Models/AssociateMfaAuthenticatorResponse.cs similarity index 93% rename from src/Auth0.AuthenticationApi/Models/AssociateNewAuthenticatorResponse.cs rename to src/Auth0.AuthenticationApi/Models/AssociateMfaAuthenticatorResponse.cs index d01830ce5..9939261cf 100644 --- a/src/Auth0.AuthenticationApi/Models/AssociateNewAuthenticatorResponse.cs +++ b/src/Auth0.AuthenticationApi/Models/AssociateMfaAuthenticatorResponse.cs @@ -3,7 +3,7 @@ namespace Auth0.AuthenticationApi.Models { - public class AssociateNewAuthenticatorResponse + public class AssociateMfaAuthenticatorResponse { [JsonProperty("oob_code")] public string OobCode { get; set; } diff --git a/src/Auth0.AuthenticationApi/Models/MfaOobTokenRequest.cs b/src/Auth0.AuthenticationApi/Models/MfaOobTokenRequest.cs index 844cdc210..64aaa67a8 100644 --- a/src/Auth0.AuthenticationApi/Models/MfaOobTokenRequest.cs +++ b/src/Auth0.AuthenticationApi/Models/MfaOobTokenRequest.cs @@ -1,6 +1,6 @@ namespace Auth0.AuthenticationApi.Models { - public class MfaOobTokenRequest + public class MfaOobTokenRequest : IClientAuthentication { /// /// Your application's Client ID. @@ -12,6 +12,16 @@ public class MfaOobTokenRequest /// Required when the Token Endpoint Authentication Method field at your Application Settings is Post or Basic. /// public string ClientSecret { get; set; } + + /// + /// Security Key to use with Client Assertion + /// + public SecurityKey ClientAssertionSecurityKey { get; set; } + + /// + /// Algorithm for the Security Key to use with Client Assertion + /// + public string ClientAssertionSecurityKeyAlgorithm { get; set; } /// /// The mfa_token you received from mfa_required error or access token with enroll scope and audience: https://{yourDomain}/mfa/ From 8e3d9db751cad138e325d61328e22cce9c9aa0f2 Mon Sep 17 00:00:00 2001 From: czf Date: Fri, 5 Apr 2024 17:23:34 -0700 Subject: [PATCH 4/4] Missed cleanup --- src/Auth0.AuthenticationApi/AuthenticationApiClient.cs | 5 ++--- src/Auth0.AuthenticationApi/Models/MfaOobTokenRequest.cs | 4 +++- tests/Auth0.AuthenticationApi.IntegrationTests/MfaTests.cs | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs b/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs index beb735f86..af8c42e7b 100644 --- a/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs +++ b/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs @@ -362,7 +362,6 @@ public async Task GetTokenAsync(MfaOobTokenRequest request, { { "grant_type", "http://auth0.com/oauth/grant-type/mfa-oob" }, { "client_id", request.ClientId }, - { "client_secret", request.ClientSecret }, { "mfa_token", request.MfaToken}, { "oob_code", request.OobCode}, }; @@ -524,14 +523,14 @@ public Task PushedAuthorizationRequestAsync( } /// - public Task AssociateMfaAuthenticatorAsync(AssociateMfaAuthenticatorRequest request, CancellationToken cancellationToken = default) + public Task AssociateMfaAuthenticatorAsync(AssociateMfaAuthenticatorRequest request, CancellationToken cancellationToken = default) { if (request == null) { throw new ArgumentNullException(nameof(request)); } - return connection.SendAsync( + return connection.SendAsync( HttpMethod.Post, BuildUri("mfa/associate"), request, diff --git a/src/Auth0.AuthenticationApi/Models/MfaOobTokenRequest.cs b/src/Auth0.AuthenticationApi/Models/MfaOobTokenRequest.cs index 64aaa67a8..e47aae40a 100644 --- a/src/Auth0.AuthenticationApi/Models/MfaOobTokenRequest.cs +++ b/src/Auth0.AuthenticationApi/Models/MfaOobTokenRequest.cs @@ -1,4 +1,6 @@ -namespace Auth0.AuthenticationApi.Models +using Microsoft.IdentityModel.Tokens; + +namespace Auth0.AuthenticationApi.Models { public class MfaOobTokenRequest : IClientAuthentication { diff --git a/tests/Auth0.AuthenticationApi.IntegrationTests/MfaTests.cs b/tests/Auth0.AuthenticationApi.IntegrationTests/MfaTests.cs index 05ffd0374..7c681a193 100644 --- a/tests/Auth0.AuthenticationApi.IntegrationTests/MfaTests.cs +++ b/tests/Auth0.AuthenticationApi.IntegrationTests/MfaTests.cs @@ -29,7 +29,7 @@ public async Task Should_Receive_Associate_Response_For_Sms_Mfa_Enrollment() OobChannels = new List() { "sms" }, PhoneNumber = TestBaseUtils.GetVariable("MFA_PHONE_NUMBER") }; - var response = await _authenticationApiClient.AssociateNewAuthenticatorAsync(request); + var response = await _authenticationApiClient.AssociateMfaAuthenticatorAsync(request); response.Should().NotBeNull(); response.AuthenticatorType.Should().Be("oob"); response.BindingMethod.Should().Be("prompt");