From ba948120044147de112884f1676f45cb0f36df3b Mon Sep 17 00:00:00 2001
From: denis-goncharenko <52198869+denis-goncharenko@users.noreply.github.com>
Date: Mon, 24 Jun 2024 13:52:18 +0300
Subject: [PATCH 01/11] Implement Zoho OAuth provider
---
AspNet.Security.OAuth.Providers.sln | 8 ++
README.md | 1 +
docs/zoho.md | 23 ++++++
.../AspNet.Security.OAuth.Zoho.csproj | 24 ++++++
.../ZohoAuthenticationDefaults.cs | 48 ++++++++++++
.../ZohoAuthenticationExtensions.cs | 77 +++++++++++++++++++
.../ZohoAuthenticationHandler.cs | 72 +++++++++++++++++
.../ZohoAuthenticationOptions.cs | 34 ++++++++
.../ZohoAuthenticationPostConfigureOptions.cs | 60 +++++++++++++++
.../ZohoAuthenticationRegion.cs | 21 +++++
...AuthenticationPostConfigureOptionsTests.cs | 63 +++++++++++++++
.../Zoho/ZohoTests.cs | 24 ++++++
.../Zoho/bundle.json | 26 +++++++
13 files changed, 481 insertions(+)
create mode 100644 docs/zoho.md
create mode 100644 src/AspNet.Security.OAuth.Zoho/AspNet.Security.OAuth.Zoho.csproj
create mode 100644 src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationDefaults.cs
create mode 100644 src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationExtensions.cs
create mode 100644 src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs
create mode 100644 src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs
create mode 100644 src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationPostConfigureOptions.cs
create mode 100644 src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationRegion.cs
create mode 100644 test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoAuthenticationPostConfigureOptionsTests.cs
create mode 100644 test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoTests.cs
create mode 100644 test/AspNet.Security.OAuth.Providers.Tests/Zoho/bundle.json
diff --git a/AspNet.Security.OAuth.Providers.sln b/AspNet.Security.OAuth.Providers.sln
index 2d794948e..848748c70 100644
--- a/AspNet.Security.OAuth.Providers.sln
+++ b/AspNet.Security.OAuth.Providers.sln
@@ -220,6 +220,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{C2CA4B38-A
docs\xumm.md = docs\xumm.md
docs\zendesk.md = docs\zendesk.md
docs\docusign.md = docs\docusign.md
+ docs\zoho.md = docs\zoho.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNet.Security.OAuth.Basecamp", "src\AspNet.Security.OAuth.Basecamp\AspNet.Security.OAuth.Basecamp.csproj", "{42306484-B2BF-4B52-B950-E0CDFA58B02A}"
@@ -309,6 +310,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNet.Security.OAuth.Piped
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNet.Security.OAuth.Docusign", "src\AspNet.Security.OAuth.Docusign\AspNet.Security.OAuth.Docusign.csproj", "{4E96BD06-04CD-4014-BA42-10D2CDB820D6}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNet.Security.OAuth.Zoho", "src\AspNet.Security.OAuth.Zoho\AspNet.Security.OAuth.Zoho.csproj", "{CD56ABE4-1CD2-4029-B556-E110A31A2CC4}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -715,6 +718,10 @@ Global
{4E96BD06-04CD-4014-BA42-10D2CDB820D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4E96BD06-04CD-4014-BA42-10D2CDB820D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4E96BD06-04CD-4014-BA42-10D2CDB820D6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CD56ABE4-1CD2-4029-B556-E110A31A2CC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD56ABE4-1CD2-4029-B556-E110A31A2CC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD56ABE4-1CD2-4029-B556-E110A31A2CC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD56ABE4-1CD2-4029-B556-E110A31A2CC4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -826,6 +833,7 @@ Global
{83C37AC5-51FB-47CD-8CBE-77AA114FF6F3} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
{55975423-C9C0-4C47-AD00-0F012F30AD3C} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
{4E96BD06-04CD-4014-BA42-10D2CDB820D6} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
+ {CD56ABE4-1CD2-4029-B556-E110A31A2CC4} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C7B54DE2-6407-4802-AD9C-CE54BF414C8C}
diff --git a/README.md b/README.md
index d6e35ffe4..75d07ffec 100644
--- a/README.md
+++ b/README.md
@@ -256,6 +256,7 @@ If a provider you're looking for does not exist, consider making a PR to add one
| Yandex | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.Yandex?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.Yandex/ "Download AspNet.Security.OAuth.Yandex from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.Yandex?includePreReleases=true)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Yandex "Download AspNet.Security.OAuth.Yandex from MyGet.org") | [Documentation](https://tech.yandex.com/oauth/ "Yandex developer documentation") |
| Zalo | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.Zalo?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.Zalo/ "Download AspNet.Security.OAuth.Zalo from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.Zalo?includePreReleases=true)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Zalo "Download AspNet.Security.OAuth.Zalo from MyGet.org") | [Documentation](https://developers.zalo.me/docs/api/social-api-4 "Zalo developer documentation") |
| Zendesk | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.Zendesk?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.Zendesk/ "Download AspNet.Security.OAuth.Zendesk from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.Zendesk?includePreReleases=true)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Zendesk "Download AspNet.Security.OAuth.Zendesk from MyGet.org") | [Documentation](https://support.zendesk.com/hc/en-us/articles/203663836#topic_ar1_mfs_qk "Zendesk developer documentation") |
+| Zoho | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.Zoho?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.Zoho/ "Download AspNet.Security.OAuth.Zoho from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.Zoho?includePreReleases=true)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Zoho "Download AspNet.Security.OAuth.Zoho from MyGet.org") | [Documentation](https://www.zoho.com/accounts/protocol/oauth.html "Zoho developer documentation") |
| Zoom | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.Zoom?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.Zoom/ "Download AspNet.Security.OAuth.Zoom from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.Zoom?includePreReleases=true)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Zoom "Download AspNet.Security.OAuth.Zoom from MyGet.org") | [Documentation](https://developers.zoom.us/docs/integrations/ "Zoom developer documentation") |
+
+ true
+
+
+
+ ASP.NET Core security middleware enabling Zoho authentication.
+ Denys Goncharenko
+ aspnetcore;authentication;oauth;zoho;security
+
+
+
+
+
+
+
+
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationDefaults.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationDefaults.cs
new file mode 100644
index 000000000..3f28c9e24
--- /dev/null
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationDefaults.cs
@@ -0,0 +1,48 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+ * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
+ * for more information concerning the license and the contributors participating to this project.
+ */
+
+namespace AspNet.Security.OAuth.Zoho;
+
+///
+/// Default values used by the Zoho authentication middleware.
+///
+public static class ZohoAuthenticationDefaults
+{
+ ///
+ /// Default value for .
+ ///
+ public const string AuthenticationScheme = "Zoho";
+
+ ///
+ /// Default value for .
+ ///
+ public static readonly string DisplayName = "Zoho";
+
+ ///
+ /// Default value for .
+ ///
+ public static readonly string Issuer = "Zoho";
+
+ ///
+ /// Default value for .
+ ///
+ public static readonly string CallbackPath = "/signin-zoho";
+
+ ///
+ /// Default value for .
+ ///
+ public static readonly string AuthorizationPath = "/oauth/v2/auth";
+
+ ///
+ /// Default value for .
+ ///
+ public static readonly string TokenPath = "/oauth/v2/token";
+
+ ///
+ /// Default value for .
+ ///
+ public static readonly string UserInformationPath = "/oauth/user/info";
+}
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationExtensions.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationExtensions.cs
new file mode 100644
index 000000000..d2e5558e0
--- /dev/null
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationExtensions.cs
@@ -0,0 +1,77 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+ * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
+ * for more information concerning the license and the contributors participating to this project.
+ */
+
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Options;
+
+namespace AspNet.Security.OAuth.Zoho;
+
+///
+/// Extension methods to add Zoho authentication capabilities to an HTTP application pipeline.
+///
+public static class ZohoAuthenticationExtensions
+{
+ ///
+ /// Adds to the specified
+ /// , which enables Zoho authentication capabilities.
+ ///
+ /// The authentication builder.
+ /// A reference to this instance after the operation has completed.
+ public static AuthenticationBuilder AddZoho([NotNull] this AuthenticationBuilder builder)
+ {
+ return builder.AddZoho(ZohoAuthenticationDefaults.AuthenticationScheme, options => { });
+ }
+
+ ///
+ /// Adds to the specified
+ /// , which enables Zoho authentication capabilities.
+ ///
+ /// The authentication builder.
+ /// The delegate used to configure the OpenID 2.0 options.
+ /// A reference to this instance after the operation has completed.
+ public static AuthenticationBuilder AddZoho(
+ [NotNull] this AuthenticationBuilder builder,
+ [NotNull] Action configuration)
+ {
+ return builder.AddZoho(ZohoAuthenticationDefaults.AuthenticationScheme, configuration);
+ }
+
+ ///
+ /// Adds to the specified
+ /// , which enables Zoho authentication capabilities.
+ ///
+ /// The authentication builder.
+ /// The authentication scheme associated with this instance.
+ /// The delegate used to configure the Zoho options.
+ /// The .
+ public static AuthenticationBuilder AddZoho(
+ [NotNull] this AuthenticationBuilder builder,
+ [NotNull] string scheme,
+ [NotNull] Action configuration)
+ {
+ return builder.AddZoho(scheme, ZohoAuthenticationDefaults.DisplayName, configuration);
+ }
+
+ ///
+ /// Adds to the specified
+ /// , which enables Zoho authentication capabilities.
+ ///
+ /// The authentication builder.
+ /// The authentication scheme associated with this instance.
+ /// The optional display name associated with this instance.
+ /// The delegate used to configure the Zoho options.
+ /// The .
+ public static AuthenticationBuilder AddZoho(
+ [NotNull] this AuthenticationBuilder builder,
+ [NotNull] string scheme,
+ [CanBeNull] string caption,
+ [NotNull] Action configuration)
+ {
+ builder.Services.TryAddSingleton, ZohoAuthenticationPostConfigureOptions>();
+ return builder.AddOAuth(scheme, caption, configuration);
+ }
+}
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs
new file mode 100644
index 000000000..684824d00
--- /dev/null
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs
@@ -0,0 +1,72 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+ * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
+ * for more information concerning the license and the contributors participating to this project.
+ */
+
+using System.Net.Http.Headers;
+using System.Net.Mime;
+using System.Security.Claims;
+using System.Text.Encodings.Web;
+using System.Text.Json;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+namespace AspNet.Security.OAuth.Zoho;
+
+public partial class ZohoAuthenticationHandler : OAuthHandler
+{
+ public ZohoAuthenticationHandler(
+ [NotNull] IOptionsMonitor options,
+ [NotNull] ILoggerFactory logger,
+ [NotNull] UrlEncoder encoder)
+ : base(options, logger, encoder)
+ {
+ }
+
+ protected override async Task CreateTicketAsync(
+ [NotNull] ClaimsIdentity identity,
+ [NotNull] AuthenticationProperties properties,
+ [NotNull] OAuthTokenResponse tokens)
+ {
+ using var requestMessage = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);
+ requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
+ requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
+ requestMessage.Version = Backchannel.DefaultRequestVersion;
+
+ using var response = await Backchannel.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);
+ if (!response.IsSuccessStatusCode)
+ {
+ await Log.UserProfileErrorAsync(Logger, response, Context.RequestAborted);
+ throw new HttpRequestException("An error occurred while retrieving the user profile.");
+ }
+
+ using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted));
+
+ var principal = new ClaimsPrincipal(identity);
+ var context = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement);
+ context.RunClaimActions();
+
+ await Events.CreatingTicket(context);
+ return new AuthenticationTicket(context.Principal!, context.Properties, Scheme.Name);
+ }
+
+ private static partial class Log
+ {
+ internal static async Task UserProfileErrorAsync(ILogger logger, HttpResponseMessage response, CancellationToken cancellationToken)
+ {
+ UserProfileError(
+ logger,
+ response.StatusCode,
+ response.Headers.ToString(),
+ await response.Content.ReadAsStringAsync(cancellationToken));
+ }
+
+ [LoggerMessage(1, LogLevel.Error, "An error occurred while retrieving the user profile: the remote server returned a {Status} response with the following payload: {Headers} {Body}.")]
+ private static partial void UserProfileError(
+ ILogger logger,
+ System.Net.HttpStatusCode status,
+ string headers,
+ string body);
+ }
+}
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs
new file mode 100644
index 000000000..08cc34b27
--- /dev/null
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs
@@ -0,0 +1,34 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+ * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
+ * for more information concerning the license and the contributors participating to this project.
+ */
+
+using System.Security.Claims;
+
+namespace AspNet.Security.OAuth.Zoho;
+
+///
+/// Defines a set of options used by .
+///
+public class ZohoAuthenticationOptions : OAuthOptions
+{
+ ///
+ /// Gets or sets a value that determines whether development or production endpoints are used.
+ /// The default value of this property is .
+ ///
+ public ZohoAuthenticationRegion Region { get; set; }
+
+ public ZohoAuthenticationOptions()
+ {
+ ClaimsIssuer = ZohoAuthenticationDefaults.Issuer;
+ CallbackPath = ZohoAuthenticationDefaults.CallbackPath;
+ Region = ZohoAuthenticationRegion.Global;
+
+ Scope.Add("AaaServer.profile.READ");
+
+ ClaimActions.MapCustomJson(ClaimTypes.NameIdentifier, user => user.GetString("ZUID"));
+ ClaimActions.MapCustomJson(ClaimTypes.Name, user => user.GetString("Display_Name"));
+ ClaimActions.MapCustomJson(ClaimTypes.Email, user => user.GetString("Email"));
+ }
+}
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationPostConfigureOptions.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationPostConfigureOptions.cs
new file mode 100644
index 000000000..416b367fb
--- /dev/null
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationPostConfigureOptions.cs
@@ -0,0 +1,60 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+ * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
+ * for more information concerning the license and the contributors participating to this project.
+ */
+
+using Microsoft.Extensions.Options;
+
+namespace AspNet.Security.OAuth.Zoho;
+
+///
+/// Used to configure instances.
+///
+public sealed class ZohoAuthenticationPostConfigureOptions : IPostConfigureOptions
+{
+ ///
+ public void PostConfigure(
+ string? name,
+ [NotNull] ZohoAuthenticationOptions options)
+ {
+ ConfigureEndpoints(options);
+ }
+
+ private static void ConfigureEndpoints(ZohoAuthenticationOptions options)
+ {
+ var domain = GetDomain(options.Region);
+
+ options.AuthorizationEndpoint = CreateUrl(domain, ZohoAuthenticationDefaults.AuthorizationPath);
+ options.TokenEndpoint = CreateUrl(domain, ZohoAuthenticationDefaults.TokenPath);
+ options.UserInformationEndpoint = CreateUrl(domain, ZohoAuthenticationDefaults.UserInformationPath);
+ }
+
+ private static string CreateUrl(string domain, string path)
+ {
+ // Enforce use of HTTPS
+ var builder = new UriBuilder(domain)
+ {
+ Path = path,
+ Port = -1,
+ Scheme = Uri.UriSchemeHttps,
+ };
+
+ return builder.Uri.ToString();
+ }
+
+ private static string GetDomain(ZohoAuthenticationRegion region)
+ {
+ return region switch
+ {
+ ZohoAuthenticationRegion.Global => "accounts.zoho.com",
+ ZohoAuthenticationRegion.Europe => "accounts.zoho.eu",
+ ZohoAuthenticationRegion.India => "accounts.zoho.in",
+ ZohoAuthenticationRegion.Australia => "accounts.zoho.com.au",
+ ZohoAuthenticationRegion.Japan => "accounts.zoho.jp",
+ ZohoAuthenticationRegion.Canada => "accounts.zohocloud.ca",
+ ZohoAuthenticationRegion.SaudiArabia => "accounts.zoho.sa",
+ _ => throw new InvalidOperationException($"The {nameof(ZohoAuthenticationRegion)} is not supported."),
+ };
+ }
+}
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationRegion.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationRegion.cs
new file mode 100644
index 000000000..6a29931d9
--- /dev/null
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationRegion.cs
@@ -0,0 +1,21 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+ * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
+ * for more information concerning the license and the contributors participating to this project.
+ */
+
+namespace AspNet.Security.OAuth.Zoho;
+
+///
+/// Used to determine which region to use.
+///
+public enum ZohoAuthenticationRegion
+{
+ Global = 0,
+ Europe,
+ India,
+ Australia,
+ Japan,
+ Canada,
+ SaudiArabia
+}
diff --git a/test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoAuthenticationPostConfigureOptionsTests.cs b/test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoAuthenticationPostConfigureOptionsTests.cs
new file mode 100644
index 000000000..351a2284b
--- /dev/null
+++ b/test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoAuthenticationPostConfigureOptionsTests.cs
@@ -0,0 +1,63 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+ * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
+ * for more information concerning the license and the contributors participating to this project.
+ */
+
+namespace AspNet.Security.OAuth.Zoho;
+
+public static class ZohoAuthenticationPostConfigureOptionsTests
+{
+ [Theory]
+ [InlineData(ZohoAuthenticationRegion.Global, "accounts.zoho.com")]
+ [InlineData(ZohoAuthenticationRegion.Europe, "accounts.zoho.eu")]
+ [InlineData(ZohoAuthenticationRegion.India, "accounts.zoho.in")]
+ [InlineData(ZohoAuthenticationRegion.Australia, "accounts.zoho.com.au")]
+ [InlineData(ZohoAuthenticationRegion.Japan, "accounts.zoho.jp")]
+ [InlineData(ZohoAuthenticationRegion.Canada, "accounts.zohocloud.ca")]
+ [InlineData(ZohoAuthenticationRegion.SaudiArabia, "accounts.zoho.sa")]
+ public static void PostConfigure_Configures_Valid_Authentication_Region(ZohoAuthenticationRegion region, string domain)
+ {
+ // Arrange
+ const string name = "Zoho";
+ var target = new ZohoAuthenticationPostConfigureOptions();
+
+ var options = new ZohoAuthenticationOptions
+ {
+ Region = region
+ };
+
+ // Act
+ target.PostConfigure(name, options);
+
+ // Assert
+ options.AuthorizationEndpoint.ShouldBeEquivalentTo(
+ $"https://{domain}{ZohoAuthenticationDefaults.AuthorizationPath}");
+ Uri.TryCreate(options.AuthorizationEndpoint, UriKind.Absolute, out _).ShouldBeTrue();
+
+ options.TokenEndpoint.ShouldBeEquivalentTo(
+ $"https://{domain}{ZohoAuthenticationDefaults.TokenPath}");
+ Uri.TryCreate(options.TokenEndpoint, UriKind.Absolute, out _).ShouldBeTrue();
+
+ options.UserInformationEndpoint.ShouldBeEquivalentTo(
+ $"https://{domain}{ZohoAuthenticationDefaults.UserInformationPath}");
+ Uri.TryCreate(options.UserInformationEndpoint, UriKind.Absolute, out _).ShouldBeTrue();
+ }
+
+ [Fact]
+ public static void PostConfigure_Invalid_Authentication_Region_ThrowsException()
+ {
+ // Arrange
+ const string name = "Zoho";
+ var target = new ZohoAuthenticationPostConfigureOptions();
+
+ var options = new ZohoAuthenticationOptions
+ {
+ Region = (ZohoAuthenticationRegion)10
+ };
+
+ // Act
+ Action act = () => target.PostConfigure(name, options);
+ act.ShouldThrow();
+ }
+}
diff --git a/test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoTests.cs b/test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoTests.cs
new file mode 100644
index 000000000..3e6b066b8
--- /dev/null
+++ b/test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoTests.cs
@@ -0,0 +1,24 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+ * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
+ * for more information concerning the license and the contributors participating to this project.
+ */
+
+namespace AspNet.Security.OAuth.Zoho;
+
+public class ZohoTests(ITestOutputHelper outputHelper) : OAuthTests(outputHelper)
+{
+ public override string DefaultScheme => ZohoAuthenticationDefaults.AuthenticationScheme;
+
+ protected internal override void RegisterAuthentication(AuthenticationBuilder builder)
+ {
+ builder.AddZoho(options => ConfigureDefaults(builder, options));
+ }
+
+ [Theory]
+ [InlineData(ClaimTypes.NameIdentifier, "1234567890")]
+ [InlineData(ClaimTypes.Name, "User Name")]
+ [InlineData(ClaimTypes.Email, "testuser@example.com")]
+ public async Task Can_Sign_In_Using_Zoho(string claimType, string claimValue)
+ => await AuthenticateUserAndAssertClaimValue(claimType, claimValue);
+}
diff --git a/test/AspNet.Security.OAuth.Providers.Tests/Zoho/bundle.json b/test/AspNet.Security.OAuth.Providers.Tests/Zoho/bundle.json
new file mode 100644
index 000000000..7bf95559c
--- /dev/null
+++ b/test/AspNet.Security.OAuth.Providers.Tests/Zoho/bundle.json
@@ -0,0 +1,26 @@
+{
+ "$schema": "https://raw.githubusercontent.com/justeat/httpclient-interception/master/src/HttpClientInterception/Bundles/http-request-bundle-schema.json",
+ "items": [
+ {
+ "uri": "https://accounts.zoho.com/oauth/v2/token",
+ "method": "POST",
+ "contentFormat": "json",
+ "contentJson": {
+ "access_token": "secret-access-token",
+ "token_type": "Bearer",
+ "refresh_token": "secret-refresh-token",
+ "expires_in": 3600
+ }
+ },
+ {
+ "uri": "https://accounts.zoho.com/oauth/user/info",
+ "method": "GET",
+ "contentFormat": "json",
+ "contentJson": {
+ "ZUID": "1234567890",
+ "Email": "testuser@example.com",
+ "Display_Name": "User Name"
+ }
+ }
+ ]
+}
From 16541c41a71c54571e2e1750b9396fd2186f03aa Mon Sep 17 00:00:00 2001
From: denis-goncharenko <52198869+denis-goncharenko@users.noreply.github.com>
Date: Mon, 24 Jun 2024 17:09:44 +0300
Subject: [PATCH 02/11] Fix orders for regions and change post-configuration
behavior
---
.../ZohoAuthenticationOptions.cs | 1 -
.../ZohoAuthenticationPostConfigureOptions.cs | 18 +++++++++++++++---
...oAuthenticationPostConfigureOptionsTests.cs | 6 +++---
3 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs
index 08cc34b27..bfb1619d7 100644
--- a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs
@@ -23,7 +23,6 @@ public ZohoAuthenticationOptions()
{
ClaimsIssuer = ZohoAuthenticationDefaults.Issuer;
CallbackPath = ZohoAuthenticationDefaults.CallbackPath;
- Region = ZohoAuthenticationRegion.Global;
Scope.Add("AaaServer.profile.READ");
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationPostConfigureOptions.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationPostConfigureOptions.cs
index 416b367fb..e99659e02 100644
--- a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationPostConfigureOptions.cs
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationPostConfigureOptions.cs
@@ -23,6 +23,11 @@ public void PostConfigure(
private static void ConfigureEndpoints(ZohoAuthenticationOptions options)
{
+ if (AreEndpointsInitialized(options))
+ {
+ return;
+ }
+
var domain = GetDomain(options.Region);
options.AuthorizationEndpoint = CreateUrl(domain, ZohoAuthenticationDefaults.AuthorizationPath);
@@ -47,14 +52,21 @@ private static string GetDomain(ZohoAuthenticationRegion region)
{
return region switch
{
- ZohoAuthenticationRegion.Global => "accounts.zoho.com",
+ ZohoAuthenticationRegion.Australia => "accounts.zoho.com.au",
+ ZohoAuthenticationRegion.Canada => "accounts.zohocloud.ca",
ZohoAuthenticationRegion.Europe => "accounts.zoho.eu",
+ ZohoAuthenticationRegion.Global => "accounts.zoho.com",
ZohoAuthenticationRegion.India => "accounts.zoho.in",
- ZohoAuthenticationRegion.Australia => "accounts.zoho.com.au",
ZohoAuthenticationRegion.Japan => "accounts.zoho.jp",
- ZohoAuthenticationRegion.Canada => "accounts.zohocloud.ca",
ZohoAuthenticationRegion.SaudiArabia => "accounts.zoho.sa",
_ => throw new InvalidOperationException($"The {nameof(ZohoAuthenticationRegion)} is not supported."),
};
}
+
+ private static bool AreEndpointsInitialized(ZohoAuthenticationOptions options)
+ {
+ return !string.IsNullOrEmpty(options.AuthorizationEndpoint) &&
+ !string.IsNullOrEmpty(options.TokenEndpoint) &&
+ !string.IsNullOrEmpty(options.UserInformationEndpoint);
+ }
}
diff --git a/test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoAuthenticationPostConfigureOptionsTests.cs b/test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoAuthenticationPostConfigureOptionsTests.cs
index 351a2284b..2960a14f7 100644
--- a/test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoAuthenticationPostConfigureOptionsTests.cs
+++ b/test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoAuthenticationPostConfigureOptionsTests.cs
@@ -9,12 +9,12 @@ namespace AspNet.Security.OAuth.Zoho;
public static class ZohoAuthenticationPostConfigureOptionsTests
{
[Theory]
- [InlineData(ZohoAuthenticationRegion.Global, "accounts.zoho.com")]
+ [InlineData(ZohoAuthenticationRegion.Australia, "accounts.zoho.com.au")]
+ [InlineData(ZohoAuthenticationRegion.Canada, "accounts.zohocloud.ca")]
[InlineData(ZohoAuthenticationRegion.Europe, "accounts.zoho.eu")]
+ [InlineData(ZohoAuthenticationRegion.Global, "accounts.zoho.com")]
[InlineData(ZohoAuthenticationRegion.India, "accounts.zoho.in")]
- [InlineData(ZohoAuthenticationRegion.Australia, "accounts.zoho.com.au")]
[InlineData(ZohoAuthenticationRegion.Japan, "accounts.zoho.jp")]
- [InlineData(ZohoAuthenticationRegion.Canada, "accounts.zohocloud.ca")]
[InlineData(ZohoAuthenticationRegion.SaudiArabia, "accounts.zoho.sa")]
public static void PostConfigure_Configures_Valid_Authentication_Region(ZohoAuthenticationRegion region, string domain)
{
From eff828d328138acdc680e4a71095fb2a71a49188 Mon Sep 17 00:00:00 2001
From: denis-goncharenko <52198869+denis-goncharenko@users.noreply.github.com>
Date: Mon, 24 Jun 2024 17:10:53 +0300
Subject: [PATCH 03/11] fix zoho documentation.
---
docs/zoho.md | 1 -
src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs | 1 +
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/zoho.md b/docs/zoho.md
index 3f6aa3bb0..73ec272c3 100644
--- a/docs/zoho.md
+++ b/docs/zoho.md
@@ -8,7 +8,6 @@ services.AddAuthentication(options => /* Auth configuration */)
{
options.ClientId = "my-client-id";
options.ClientSecret = "my-client-secret";
- options.Region = ZohoAuthenticationRegion.Global;
});
```
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs
index bfb1619d7..08cc34b27 100644
--- a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs
@@ -23,6 +23,7 @@ public ZohoAuthenticationOptions()
{
ClaimsIssuer = ZohoAuthenticationDefaults.Issuer;
CallbackPath = ZohoAuthenticationDefaults.CallbackPath;
+ Region = ZohoAuthenticationRegion.Global;
Scope.Add("AaaServer.profile.READ");
From efa0da5ce8f613e66618b5324544674f3081fd0e Mon Sep 17 00:00:00 2001
From: denis-goncharenko <52198869+denis-goncharenko@users.noreply.github.com>
Date: Mon, 24 Jun 2024 17:30:31 +0300
Subject: [PATCH 04/11] rework zoho region param to be optional
---
docs/zoho.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/docs/zoho.md b/docs/zoho.md
index 73ec272c3..e341cfd78 100644
--- a/docs/zoho.md
+++ b/docs/zoho.md
@@ -13,10 +13,10 @@ services.AddAuthentication(options => /* Auth configuration */)
## Required Additional Settings
-| Property Name | Property Type | Description | Default Value |
-|:--------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------|:----------------------------------|
-| `Region` | [`ZohoAuthenticationRegion`](https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers/blob/dev/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationRegion.cs "ZohoAuthenticationRegion enumeration") | The target online region for Zoho authentication. | `ZohoAuthenticationRegion.Global` |
+_None._
## Optional Settings
-_None._
+| Property Name | Property Type | Description | Default Value |
+|:--------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------|:----------------------------------|
+| `Region` | [`ZohoAuthenticationRegion`](https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers/blob/dev/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationRegion.cs "ZohoAuthenticationRegion enumeration") | The target online region for Zoho authentication. | `ZohoAuthenticationRegion.Global` |
From 5ae027ce0468958672aac4bc9d01c3567a77d4ab Mon Sep 17 00:00:00 2001
From: denis-goncharenko <52198869+denis-goncharenko@users.noreply.github.com>
Date: Mon, 24 Jun 2024 17:53:26 +0300
Subject: [PATCH 05/11] update endpoints only when null or empty.
---
.../ZohoAuthenticationPostConfigureOptions.cs | 25 +++++++++----------
1 file changed, 12 insertions(+), 13 deletions(-)
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationPostConfigureOptions.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationPostConfigureOptions.cs
index e99659e02..45340aa57 100644
--- a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationPostConfigureOptions.cs
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationPostConfigureOptions.cs
@@ -23,16 +23,22 @@ public void PostConfigure(
private static void ConfigureEndpoints(ZohoAuthenticationOptions options)
{
- if (AreEndpointsInitialized(options))
+ var domain = GetDomain(options.Region);
+
+ if (string.IsNullOrEmpty(options.AuthorizationEndpoint))
{
- return;
+ options.AuthorizationEndpoint = CreateUrl(domain, ZohoAuthenticationDefaults.AuthorizationPath);
}
- var domain = GetDomain(options.Region);
+ if (string.IsNullOrEmpty(options.TokenEndpoint))
+ {
+ options.TokenEndpoint = CreateUrl(domain, ZohoAuthenticationDefaults.TokenPath);
+ }
- options.AuthorizationEndpoint = CreateUrl(domain, ZohoAuthenticationDefaults.AuthorizationPath);
- options.TokenEndpoint = CreateUrl(domain, ZohoAuthenticationDefaults.TokenPath);
- options.UserInformationEndpoint = CreateUrl(domain, ZohoAuthenticationDefaults.UserInformationPath);
+ if (string.IsNullOrEmpty(options.UserInformationEndpoint))
+ {
+ options.UserInformationEndpoint = CreateUrl(domain, ZohoAuthenticationDefaults.UserInformationPath);
+ }
}
private static string CreateUrl(string domain, string path)
@@ -62,11 +68,4 @@ private static string GetDomain(ZohoAuthenticationRegion region)
_ => throw new InvalidOperationException($"The {nameof(ZohoAuthenticationRegion)} is not supported."),
};
}
-
- private static bool AreEndpointsInitialized(ZohoAuthenticationOptions options)
- {
- return !string.IsNullOrEmpty(options.AuthorizationEndpoint) &&
- !string.IsNullOrEmpty(options.TokenEndpoint) &&
- !string.IsNullOrEmpty(options.UserInformationEndpoint);
- }
}
From f82dcd5b4636149f85da57459088538f0155851c Mon Sep 17 00:00:00 2001
From: denis-goncharenko <52198869+denis-goncharenko@users.noreply.github.com>
Date: Tue, 23 Jul 2024 18:50:04 +0300
Subject: [PATCH 06/11] rework hard coded domains to dynamic
---
AspNet.Security.OAuth.Providers.sln | 1 -
docs/zoho.md | 22 ------
.../ZohoAuthenticationDefaults.cs | 12 +++-
.../ZohoAuthenticationExtensions.cs | 3 -
.../ZohoAuthenticationHandler.cs | 40 +++++++++++
.../ZohoAuthenticationOptions.cs | 9 +--
.../ZohoAuthenticationPostConfigureOptions.cs | 71 -------------------
.../ZohoAuthenticationRegion.cs | 21 ------
...AuthenticationPostConfigureOptionsTests.cs | 63 ----------------
9 files changed, 53 insertions(+), 189 deletions(-)
delete mode 100644 docs/zoho.md
delete mode 100644 src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationPostConfigureOptions.cs
delete mode 100644 src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationRegion.cs
delete mode 100644 test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoAuthenticationPostConfigureOptionsTests.cs
diff --git a/AspNet.Security.OAuth.Providers.sln b/AspNet.Security.OAuth.Providers.sln
index 848748c70..d81fac018 100644
--- a/AspNet.Security.OAuth.Providers.sln
+++ b/AspNet.Security.OAuth.Providers.sln
@@ -220,7 +220,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{C2CA4B38-A
docs\xumm.md = docs\xumm.md
docs\zendesk.md = docs\zendesk.md
docs\docusign.md = docs\docusign.md
- docs\zoho.md = docs\zoho.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNet.Security.OAuth.Basecamp", "src\AspNet.Security.OAuth.Basecamp\AspNet.Security.OAuth.Basecamp.csproj", "{42306484-B2BF-4B52-B950-E0CDFA58B02A}"
diff --git a/docs/zoho.md b/docs/zoho.md
deleted file mode 100644
index e341cfd78..000000000
--- a/docs/zoho.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# Integrating the Zoho Provider
-
-## Example
-
-```csharp
-services.AddAuthentication(options => /* Auth configuration */)
- .AddZoho(options =>
- {
- options.ClientId = "my-client-id";
- options.ClientSecret = "my-client-secret";
- });
-```
-
-## Required Additional Settings
-
-_None._
-
-## Optional Settings
-
-| Property Name | Property Type | Description | Default Value |
-|:--------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------|:----------------------------------|
-| `Region` | [`ZohoAuthenticationRegion`](https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers/blob/dev/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationRegion.cs "ZohoAuthenticationRegion enumeration") | The target online region for Zoho authentication. | `ZohoAuthenticationRegion.Global` |
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationDefaults.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationDefaults.cs
index 3f28c9e24..4cdf34b0d 100644
--- a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationDefaults.cs
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationDefaults.cs
@@ -34,15 +34,25 @@ public static class ZohoAuthenticationDefaults
///
/// Default value for .
///
- public static readonly string AuthorizationPath = "/oauth/v2/auth";
+ public static readonly string AuthorizeEndpoint = "https://accounts.zoho.com/oauth/v2/auth";
///
/// Default value for .
///
public static readonly string TokenPath = "/oauth/v2/token";
+ ///
+ /// Default value for .
+ ///
+ public static readonly string TokenEndpoint = "https://accounts.zoho.com/oauth/v2/token";
+
///
/// Default value for .
///
public static readonly string UserInformationPath = "/oauth/user/info";
+
+ ///
+ /// Default value for Zoho Server Info.
+ ///
+ public static readonly string ServerInfoEndpoint = "https://accounts.zoho.com/oauth/serverinfo";
}
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationExtensions.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationExtensions.cs
index d2e5558e0..5b8574942 100644
--- a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationExtensions.cs
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationExtensions.cs
@@ -5,8 +5,6 @@
*/
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-using Microsoft.Extensions.Options;
namespace AspNet.Security.OAuth.Zoho;
@@ -71,7 +69,6 @@ public static AuthenticationBuilder AddZoho(
[CanBeNull] string caption,
[NotNull] Action configuration)
{
- builder.Services.TryAddSingleton, ZohoAuthenticationPostConfigureOptions>();
return builder.AddOAuth(scheme, caption, configuration);
}
}
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs
index 684824d00..cf8651441 100644
--- a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs
@@ -51,6 +51,30 @@ protected override async Task CreateTicketAsync(
return new AuthenticationTicket(context.Principal!, context.Properties, Scheme.Name);
}
+ protected override async Task HandleRemoteAuthenticateAsync()
+ {
+ var location = Context.Request.Query["location"];
+
+ using var requestMessage = new HttpRequestMessage(HttpMethod.Get, ZohoAuthenticationDefaults.ServerInfoEndpoint);
+ requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
+ requestMessage.Version = Backchannel.DefaultRequestVersion;
+
+ using var response = await Backchannel.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);
+ if (!response.IsSuccessStatusCode)
+ {
+ await Log.ServerInfoErrorAsync(Logger, response, Context.RequestAborted);
+ throw new HttpRequestException("An error occurred while retrieving the server info.");
+ }
+
+ using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted));
+
+ var domain = payload.RootElement.GetProperty("locations").GetProperty(location.ToString());
+ Options.UserInformationEndpoint = $"{domain}{ZohoAuthenticationDefaults.UserInformationPath}";
+ Options.TokenEndpoint = $"{domain}{ZohoAuthenticationDefaults.TokenPath}";
+
+ return await base.HandleRemoteAuthenticateAsync();
+ }
+
private static partial class Log
{
internal static async Task UserProfileErrorAsync(ILogger logger, HttpResponseMessage response, CancellationToken cancellationToken)
@@ -62,11 +86,27 @@ internal static async Task UserProfileErrorAsync(ILogger logger, HttpResponseMes
await response.Content.ReadAsStringAsync(cancellationToken));
}
+ internal static async Task ServerInfoErrorAsync(ILogger logger, HttpResponseMessage response, CancellationToken cancellationToken)
+ {
+ ServerInfoErrorAsync(
+ logger,
+ response.StatusCode,
+ response.Headers.ToString(),
+ await response.Content.ReadAsStringAsync(cancellationToken));
+ }
+
[LoggerMessage(1, LogLevel.Error, "An error occurred while retrieving the user profile: the remote server returned a {Status} response with the following payload: {Headers} {Body}.")]
private static partial void UserProfileError(
ILogger logger,
System.Net.HttpStatusCode status,
string headers,
string body);
+
+ [LoggerMessage(2, LogLevel.Error, "An error occurred while retrieving the server info: the remote server returned a {Status} response with the following payload: {Headers} {Body}.")]
+ private static partial void ServerInfoErrorAsync(
+ ILogger logger,
+ System.Net.HttpStatusCode status,
+ string headers,
+ string body);
}
}
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs
index 08cc34b27..1e533fec3 100644
--- a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs
@@ -13,17 +13,12 @@ namespace AspNet.Security.OAuth.Zoho;
///
public class ZohoAuthenticationOptions : OAuthOptions
{
- ///
- /// Gets or sets a value that determines whether development or production endpoints are used.
- /// The default value of this property is .
- ///
- public ZohoAuthenticationRegion Region { get; set; }
-
public ZohoAuthenticationOptions()
{
ClaimsIssuer = ZohoAuthenticationDefaults.Issuer;
CallbackPath = ZohoAuthenticationDefaults.CallbackPath;
- Region = ZohoAuthenticationRegion.Global;
+ AuthorizationEndpoint = ZohoAuthenticationDefaults.AuthorizeEndpoint;
+ TokenEndpoint = ZohoAuthenticationDefaults.TokenEndpoint;
Scope.Add("AaaServer.profile.READ");
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationPostConfigureOptions.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationPostConfigureOptions.cs
deleted file mode 100644
index 45340aa57..000000000
--- a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationPostConfigureOptions.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
- * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
- * for more information concerning the license and the contributors participating to this project.
- */
-
-using Microsoft.Extensions.Options;
-
-namespace AspNet.Security.OAuth.Zoho;
-
-///
-/// Used to configure instances.
-///
-public sealed class ZohoAuthenticationPostConfigureOptions : IPostConfigureOptions
-{
- ///
- public void PostConfigure(
- string? name,
- [NotNull] ZohoAuthenticationOptions options)
- {
- ConfigureEndpoints(options);
- }
-
- private static void ConfigureEndpoints(ZohoAuthenticationOptions options)
- {
- var domain = GetDomain(options.Region);
-
- if (string.IsNullOrEmpty(options.AuthorizationEndpoint))
- {
- options.AuthorizationEndpoint = CreateUrl(domain, ZohoAuthenticationDefaults.AuthorizationPath);
- }
-
- if (string.IsNullOrEmpty(options.TokenEndpoint))
- {
- options.TokenEndpoint = CreateUrl(domain, ZohoAuthenticationDefaults.TokenPath);
- }
-
- if (string.IsNullOrEmpty(options.UserInformationEndpoint))
- {
- options.UserInformationEndpoint = CreateUrl(domain, ZohoAuthenticationDefaults.UserInformationPath);
- }
- }
-
- private static string CreateUrl(string domain, string path)
- {
- // Enforce use of HTTPS
- var builder = new UriBuilder(domain)
- {
- Path = path,
- Port = -1,
- Scheme = Uri.UriSchemeHttps,
- };
-
- return builder.Uri.ToString();
- }
-
- private static string GetDomain(ZohoAuthenticationRegion region)
- {
- return region switch
- {
- ZohoAuthenticationRegion.Australia => "accounts.zoho.com.au",
- ZohoAuthenticationRegion.Canada => "accounts.zohocloud.ca",
- ZohoAuthenticationRegion.Europe => "accounts.zoho.eu",
- ZohoAuthenticationRegion.Global => "accounts.zoho.com",
- ZohoAuthenticationRegion.India => "accounts.zoho.in",
- ZohoAuthenticationRegion.Japan => "accounts.zoho.jp",
- ZohoAuthenticationRegion.SaudiArabia => "accounts.zoho.sa",
- _ => throw new InvalidOperationException($"The {nameof(ZohoAuthenticationRegion)} is not supported."),
- };
- }
-}
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationRegion.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationRegion.cs
deleted file mode 100644
index 6a29931d9..000000000
--- a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationRegion.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
- * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
- * for more information concerning the license and the contributors participating to this project.
- */
-
-namespace AspNet.Security.OAuth.Zoho;
-
-///
-/// Used to determine which region to use.
-///
-public enum ZohoAuthenticationRegion
-{
- Global = 0,
- Europe,
- India,
- Australia,
- Japan,
- Canada,
- SaudiArabia
-}
diff --git a/test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoAuthenticationPostConfigureOptionsTests.cs b/test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoAuthenticationPostConfigureOptionsTests.cs
deleted file mode 100644
index 2960a14f7..000000000
--- a/test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoAuthenticationPostConfigureOptionsTests.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
- * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
- * for more information concerning the license and the contributors participating to this project.
- */
-
-namespace AspNet.Security.OAuth.Zoho;
-
-public static class ZohoAuthenticationPostConfigureOptionsTests
-{
- [Theory]
- [InlineData(ZohoAuthenticationRegion.Australia, "accounts.zoho.com.au")]
- [InlineData(ZohoAuthenticationRegion.Canada, "accounts.zohocloud.ca")]
- [InlineData(ZohoAuthenticationRegion.Europe, "accounts.zoho.eu")]
- [InlineData(ZohoAuthenticationRegion.Global, "accounts.zoho.com")]
- [InlineData(ZohoAuthenticationRegion.India, "accounts.zoho.in")]
- [InlineData(ZohoAuthenticationRegion.Japan, "accounts.zoho.jp")]
- [InlineData(ZohoAuthenticationRegion.SaudiArabia, "accounts.zoho.sa")]
- public static void PostConfigure_Configures_Valid_Authentication_Region(ZohoAuthenticationRegion region, string domain)
- {
- // Arrange
- const string name = "Zoho";
- var target = new ZohoAuthenticationPostConfigureOptions();
-
- var options = new ZohoAuthenticationOptions
- {
- Region = region
- };
-
- // Act
- target.PostConfigure(name, options);
-
- // Assert
- options.AuthorizationEndpoint.ShouldBeEquivalentTo(
- $"https://{domain}{ZohoAuthenticationDefaults.AuthorizationPath}");
- Uri.TryCreate(options.AuthorizationEndpoint, UriKind.Absolute, out _).ShouldBeTrue();
-
- options.TokenEndpoint.ShouldBeEquivalentTo(
- $"https://{domain}{ZohoAuthenticationDefaults.TokenPath}");
- Uri.TryCreate(options.TokenEndpoint, UriKind.Absolute, out _).ShouldBeTrue();
-
- options.UserInformationEndpoint.ShouldBeEquivalentTo(
- $"https://{domain}{ZohoAuthenticationDefaults.UserInformationPath}");
- Uri.TryCreate(options.UserInformationEndpoint, UriKind.Absolute, out _).ShouldBeTrue();
- }
-
- [Fact]
- public static void PostConfigure_Invalid_Authentication_Region_ThrowsException()
- {
- // Arrange
- const string name = "Zoho";
- var target = new ZohoAuthenticationPostConfigureOptions();
-
- var options = new ZohoAuthenticationOptions
- {
- Region = (ZohoAuthenticationRegion)10
- };
-
- // Act
- Action act = () => target.PostConfigure(name, options);
- act.ShouldThrow();
- }
-}
From 3fd3bd9a8f265f41cd24611caf0c025c90ffc890 Mon Sep 17 00:00:00 2001
From: denis-goncharenko <52198869+denis-goncharenko@users.noreply.github.com>
Date: Tue, 23 Jul 2024 19:40:39 +0300
Subject: [PATCH 07/11] fix tests
---
.../Zoho/ZohoTests.cs | 13 +++++++++++--
.../Zoho/bundle.json | 18 ++++++++++++++++++
2 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoTests.cs b/test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoTests.cs
index 3e6b066b8..6c985e61e 100644
--- a/test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoTests.cs
+++ b/test/AspNet.Security.OAuth.Providers.Tests/Zoho/ZohoTests.cs
@@ -6,13 +6,22 @@
namespace AspNet.Security.OAuth.Zoho;
-public class ZohoTests(ITestOutputHelper outputHelper) : OAuthTests(outputHelper)
+public class ZohoTests : OAuthTests
{
+ public ZohoTests(ITestOutputHelper outputHelper)
+ : base(outputHelper)
+ {
+ LoopbackRedirectHandler.LoopbackParameters.Add("location", "us");
+ }
+
public override string DefaultScheme => ZohoAuthenticationDefaults.AuthenticationScheme;
protected internal override void RegisterAuthentication(AuthenticationBuilder builder)
{
- builder.AddZoho(options => ConfigureDefaults(builder, options));
+ builder.AddZoho(options =>
+ {
+ ConfigureDefaults(builder, options);
+ });
}
[Theory]
diff --git a/test/AspNet.Security.OAuth.Providers.Tests/Zoho/bundle.json b/test/AspNet.Security.OAuth.Providers.Tests/Zoho/bundle.json
index 7bf95559c..68718dd85 100644
--- a/test/AspNet.Security.OAuth.Providers.Tests/Zoho/bundle.json
+++ b/test/AspNet.Security.OAuth.Providers.Tests/Zoho/bundle.json
@@ -21,6 +21,24 @@
"Email": "testuser@example.com",
"Display_Name": "User Name"
}
+ },
+ {
+ "uri": "https://accounts.zoho.com/oauth/serverinfo",
+ "method": "GET",
+ "contentFormat": "json",
+ "contentJson": {
+ "result": "success",
+ "locations": {
+ "eu": "https://accounts.zoho.eu",
+ "au": "https://accounts.zoho.com.au",
+ "in": "https://accounts.zoho.in",
+ "jp": "https://accounts.zoho.jp",
+ "uk": "https://accounts.zoho.uk",
+ "us": "https://accounts.zoho.com",
+ "ca": "https://accounts.zohocloud.ca",
+ "sa": "https://accounts.zoho.sa"
+ }
+ }
}
]
}
From 736e488e00251b645cac1b71c083b64c4fb7cc92 Mon Sep 17 00:00:00 2001
From: denis-goncharenko <52198869+denis-goncharenko@users.noreply.github.com>
Date: Tue, 23 Jul 2024 20:30:46 +0300
Subject: [PATCH 08/11] fix remote authentication process
---
.../ZohoAuthenticationHandler.cs | 84 +++++++++++++++----
1 file changed, 69 insertions(+), 15 deletions(-)
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs
index cf8651441..241b9fd19 100644
--- a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs
@@ -4,6 +4,7 @@
* for more information concerning the license and the contributors participating to this project.
*/
+using System.Net;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Security.Claims;
@@ -29,7 +30,8 @@ protected override async Task CreateTicketAsync(
[NotNull] AuthenticationProperties properties,
[NotNull] OAuthTokenResponse tokens)
{
- using var requestMessage = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);
+ var userInformationEndpoint = CreateEndpoint(ZohoAuthenticationDefaults.UserInformationPath);
+ using var requestMessage = new HttpRequestMessage(HttpMethod.Get, userInformationEndpoint);
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
requestMessage.Version = Backchannel.DefaultRequestVersion;
@@ -51,28 +53,64 @@ protected override async Task CreateTicketAsync(
return new AuthenticationTicket(context.Principal!, context.Properties, Scheme.Name);
}
- protected override async Task HandleRemoteAuthenticateAsync()
+ protected override async Task ExchangeCodeAsync(OAuthCodeExchangeContext context)
{
- var location = Context.Request.Query["location"];
+ var nameValueCollection = new Dictionary
+ {
+ ["client_id"] = Options.ClientId,
+ ["client_secret"] = Options.ClientSecret,
+ ["code"] = context.Code,
+ ["redirect_uri"] = context.RedirectUri,
+ ["grant_type"] = "authorization_code"
+ };
+
+ if (context.Properties.Items.TryGetValue(OAuthConstants.CodeVerifierKey, out var codeVerifier))
+ {
+ nameValueCollection.Add(OAuthConstants.CodeVerifierKey, codeVerifier!);
+ context.Properties.Items.Remove(OAuthConstants.CodeVerifierKey);
+ }
- using var requestMessage = new HttpRequestMessage(HttpMethod.Get, ZohoAuthenticationDefaults.ServerInfoEndpoint);
- requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
- requestMessage.Version = Backchannel.DefaultRequestVersion;
+ var tokenEndpoint = CreateEndpoint(ZohoAuthenticationDefaults.TokenPath);
+ using var request = new HttpRequestMessage(HttpMethod.Post, tokenEndpoint);
+ request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
+ request.Content = new FormUrlEncodedContent(nameValueCollection);
+ request.Version = Backchannel.DefaultRequestVersion;
- using var response = await Backchannel.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);
+ using var response = await Backchannel.SendAsync(request, Context.RequestAborted);
if (!response.IsSuccessStatusCode)
{
- await Log.ServerInfoErrorAsync(Logger, response, Context.RequestAborted);
- throw new HttpRequestException("An error occurred while retrieving the server info.");
+ await Log.ExchangeCodeErrorAsync(Logger, response, Context.RequestAborted);
+ return OAuthTokenResponse.Failed(new Exception("An error occurred while retrieving an access token."));
}
- using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted));
+ var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted));
- var domain = payload.RootElement.GetProperty("locations").GetProperty(location.ToString());
- Options.UserInformationEndpoint = $"{domain}{ZohoAuthenticationDefaults.UserInformationPath}";
- Options.TokenEndpoint = $"{domain}{ZohoAuthenticationDefaults.TokenPath}";
+ return OAuthTokenResponse.Success(payload);
+ }
+
+ private string CreateEndpoint(string path)
+ {
+ var location = Context.Request.Query["location"];
- return await base.HandleRemoteAuthenticateAsync();
+ var domain = GetDomain(location.ToString());
+
+ return $"{domain}{path}";
+ }
+
+ private static string GetDomain(string location)
+ {
+ return location switch
+ {
+ "au" => "https://accounts.zoho.com.au",
+ "ca" => "https://accounts.zohocloud.ca",
+ "eu" => "https://accounts.zoho.eu",
+ "us" => "https://accounts.zoho.com",
+ "in" => "https://accounts.zoho.in",
+ "jp" => "https://accounts.zoho.jp",
+ "sa" => "https://accounts.zoho.sa",
+ "uk" => "https://accounts.zoho.uk",
+ _ => "https://accounts.zoho.com"
+ };
}
private static partial class Log
@@ -95,6 +133,15 @@ internal static async Task ServerInfoErrorAsync(ILogger logger, HttpResponseMess
await response.Content.ReadAsStringAsync(cancellationToken));
}
+ internal static async Task ExchangeCodeErrorAsync(ILogger logger, HttpResponseMessage response, CancellationToken cancellationToken)
+ {
+ ExchangeCodeError(
+ logger,
+ response.StatusCode,
+ response.Headers.ToString(),
+ await response.Content.ReadAsStringAsync(cancellationToken));
+ }
+
[LoggerMessage(1, LogLevel.Error, "An error occurred while retrieving the user profile: the remote server returned a {Status} response with the following payload: {Headers} {Body}.")]
private static partial void UserProfileError(
ILogger logger,
@@ -105,7 +152,14 @@ private static partial void UserProfileError(
[LoggerMessage(2, LogLevel.Error, "An error occurred while retrieving the server info: the remote server returned a {Status} response with the following payload: {Headers} {Body}.")]
private static partial void ServerInfoErrorAsync(
ILogger logger,
- System.Net.HttpStatusCode status,
+ HttpStatusCode status,
+ string headers,
+ string body);
+
+ [LoggerMessage(3, LogLevel.Error, "An error occurred while retrieving an access token: the remote server returned a {Status} response with the following payload: {Headers} {Body}.")]
+ private static partial void ExchangeCodeError(
+ ILogger logger,
+ HttpStatusCode status,
string headers,
string body);
}
From 760469aeeed9ea5bd6f92f5d85db829599356487 Mon Sep 17 00:00:00 2001
From: denis-goncharenko <52198869+denis-goncharenko@users.noreply.github.com>
Date: Tue, 10 Sep 2024 18:39:56 +0300
Subject: [PATCH 09/11] fix code smells, add summary for CreateEndpoint
---
.../ZohoAuthenticationDefaults.cs | 7 +------
.../ZohoAuthenticationHandler.cs | 18 ++++++++++--------
.../ZohoAuthenticationOptions.cs | 2 +-
3 files changed, 12 insertions(+), 15 deletions(-)
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationDefaults.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationDefaults.cs
index 4cdf34b0d..ad48a07b7 100644
--- a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationDefaults.cs
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationDefaults.cs
@@ -34,7 +34,7 @@ public static class ZohoAuthenticationDefaults
///
/// Default value for .
///
- public static readonly string AuthorizeEndpoint = "https://accounts.zoho.com/oauth/v2/auth";
+ public static readonly string AuthorizationEndpoint = "https://accounts.zoho.com/oauth/v2/auth";
///
/// Default value for .
@@ -50,9 +50,4 @@ public static class ZohoAuthenticationDefaults
/// Default value for .
///
public static readonly string UserInformationPath = "/oauth/user/info";
-
- ///
- /// Default value for Zoho Server Info.
- ///
- public static readonly string ServerInfoEndpoint = "https://accounts.zoho.com/oauth/serverinfo";
}
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs
index 241b9fd19..edbb4348f 100644
--- a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs
@@ -88,18 +88,18 @@ protected override async Task ExchangeCodeAsync(OAuthCodeExc
return OAuthTokenResponse.Success(payload);
}
+ ///
+ /// Creates the endpoint for the Zoho API using the location parameter.
+ /// If the location parameter doesn't match any of the supported locations, the default location (US) is used.
+ /// We don't use the accounts-server parameter due to security reasons.
+ ///
+ /// The request path.
+ /// The API endpoint for the Zoho API.
private string CreateEndpoint(string path)
{
var location = Context.Request.Query["location"];
- var domain = GetDomain(location.ToString());
-
- return $"{domain}{path}";
- }
-
- private static string GetDomain(string location)
- {
- return location switch
+ var domain = location.ToString().ToLowerInvariant() switch
{
"au" => "https://accounts.zoho.com.au",
"ca" => "https://accounts.zohocloud.ca",
@@ -111,6 +111,8 @@ private static string GetDomain(string location)
"uk" => "https://accounts.zoho.uk",
_ => "https://accounts.zoho.com"
};
+
+ return $"{domain}{path}";
}
private static partial class Log
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs
index 1e533fec3..7feb8631e 100644
--- a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationOptions.cs
@@ -17,7 +17,7 @@ public ZohoAuthenticationOptions()
{
ClaimsIssuer = ZohoAuthenticationDefaults.Issuer;
CallbackPath = ZohoAuthenticationDefaults.CallbackPath;
- AuthorizationEndpoint = ZohoAuthenticationDefaults.AuthorizeEndpoint;
+ AuthorizationEndpoint = ZohoAuthenticationDefaults.AuthorizationEndpoint;
TokenEndpoint = ZohoAuthenticationDefaults.TokenEndpoint;
Scope.Add("AaaServer.profile.READ");
From 8ccb7f55a66966599630fd359c0b5f5f7f1b21f2 Mon Sep 17 00:00:00 2001
From: denis-goncharenko <52198869+denis-goncharenko@users.noreply.github.com>
Date: Tue, 10 Sep 2024 18:52:48 +0300
Subject: [PATCH 10/11] use UriBuilder instead of interpolation
---
.../ZohoAuthenticationHandler.cs | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs
index edbb4348f..469e67b5e 100644
--- a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs
@@ -112,7 +112,14 @@ private string CreateEndpoint(string path)
_ => "https://accounts.zoho.com"
};
- return $"{domain}{path}";
+ var builder = new UriBuilder(domain)
+ {
+ Path = path,
+ Port = -1,
+ Scheme = Uri.UriSchemeHttps,
+ };
+
+ return builder.Uri.ToString();
}
private static partial class Log
From bc050864662ca1e675e5c94f6fbec50f8164ed89 Mon Sep 17 00:00:00 2001
From: denis-goncharenko <52198869+denis-goncharenko@users.noreply.github.com>
Date: Thu, 12 Sep 2024 13:08:00 +0300
Subject: [PATCH 11/11] remove unused config, fix summary
---
.../ZohoAuthenticationHandler.cs | 2 +-
.../Zoho/bundle.json | 18 ------------------
2 files changed, 1 insertion(+), 19 deletions(-)
diff --git a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs
index 469e67b5e..96df44898 100644
--- a/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs
+++ b/src/AspNet.Security.OAuth.Zoho/ZohoAuthenticationHandler.cs
@@ -91,7 +91,7 @@ protected override async Task ExchangeCodeAsync(OAuthCodeExc
///
/// Creates the endpoint for the Zoho API using the location parameter.
/// If the location parameter doesn't match any of the supported locations, the default location (US) is used.
- /// We don't use the accounts-server parameter due to security reasons.
+ /// We don't use the accounts-server parameter for security reasons.
///
/// The request path.
/// The API endpoint for the Zoho API.
diff --git a/test/AspNet.Security.OAuth.Providers.Tests/Zoho/bundle.json b/test/AspNet.Security.OAuth.Providers.Tests/Zoho/bundle.json
index 68718dd85..7bf95559c 100644
--- a/test/AspNet.Security.OAuth.Providers.Tests/Zoho/bundle.json
+++ b/test/AspNet.Security.OAuth.Providers.Tests/Zoho/bundle.json
@@ -21,24 +21,6 @@
"Email": "testuser@example.com",
"Display_Name": "User Name"
}
- },
- {
- "uri": "https://accounts.zoho.com/oauth/serverinfo",
- "method": "GET",
- "contentFormat": "json",
- "contentJson": {
- "result": "success",
- "locations": {
- "eu": "https://accounts.zoho.eu",
- "au": "https://accounts.zoho.com.au",
- "in": "https://accounts.zoho.in",
- "jp": "https://accounts.zoho.jp",
- "uk": "https://accounts.zoho.uk",
- "us": "https://accounts.zoho.com",
- "ca": "https://accounts.zohocloud.ca",
- "sa": "https://accounts.zoho.sa"
- }
- }
}
]
}