Skip to content

Commit 65176d0

Browse files
authored
Add Zendesk provider (#587)
Resolves #586.
1 parent f9059d3 commit 65176d0

13 files changed

+579
-0
lines changed

AspNet.Security.OAuth.Providers.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNet.Security.OAuth.Mixcl
257257
EndProject
258258
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNet.Security.OAuth.AdobeIO", "src\AspNet.Security.OAuth.AdobeIO\AspNet.Security.OAuth.AdobeIO.csproj", "{91BB9A49-9C88-4132-96E4-2D6148ACDE77}"
259259
EndProject
260+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNet.Security.OAuth.Zendesk", "src\AspNet.Security.OAuth.Zendesk\AspNet.Security.OAuth.Zendesk.csproj", "{C90BA18B-E6C4-4E84-AFCC-58A4EE13BA40}"
261+
EndProject
260262
Global
261263
GlobalSection(SolutionConfigurationPlatforms) = preSolution
262264
Debug|Any CPU = Debug|Any CPU
@@ -579,6 +581,10 @@ Global
579581
{91BB9A49-9C88-4132-96E4-2D6148ACDE77}.Debug|Any CPU.Build.0 = Debug|Any CPU
580582
{91BB9A49-9C88-4132-96E4-2D6148ACDE77}.Release|Any CPU.ActiveCfg = Release|Any CPU
581583
{91BB9A49-9C88-4132-96E4-2D6148ACDE77}.Release|Any CPU.Build.0 = Release|Any CPU
584+
{C90BA18B-E6C4-4E84-AFCC-58A4EE13BA40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
585+
{C90BA18B-E6C4-4E84-AFCC-58A4EE13BA40}.Debug|Any CPU.Build.0 = Debug|Any CPU
586+
{C90BA18B-E6C4-4E84-AFCC-58A4EE13BA40}.Release|Any CPU.ActiveCfg = Release|Any CPU
587+
{C90BA18B-E6C4-4E84-AFCC-58A4EE13BA40}.Release|Any CPU.Build.0 = Release|Any CPU
582588
EndGlobalSection
583589
GlobalSection(SolutionProperties) = preSolution
584590
HideSolutionNode = FALSE
@@ -669,6 +675,7 @@ Global
669675
{DF2786DF-234D-4A8C-B166-0B8F8B7D527B} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
670676
{7A2EC21F-D411-4B45-AA3B-70143C1A9E2F} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
671677
{91BB9A49-9C88-4132-96E4-2D6148ACDE77} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
678+
{C90BA18B-E6C4-4E84-AFCC-58A4EE13BA40} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
672679
EndGlobalSection
673680
GlobalSection(ExtensibilityGlobals) = postSolution
674681
SolutionGuid = {C7B54DE2-6407-4802-AD9C-CE54BF414C8C}

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
# AspNet.Security.OAuth.Providers
23

34
**AspNet.Security.OAuth.Providers** is a **collection of security middleware** that you can use in your **ASP.NET Core** application to support social authentication providers like **[GitHub](https://github.com/)**, **[Foursquare](https://foursquare.com/)** or **[Dropbox](https://www.dropbox.com/)**. It is directly inspired by **[Jerrie Pelser](https://github.com/jerriep)**'s initiative, **[Owin.Security.Providers](https://github.com/RockstarLabs/OwinOAuthProviders)**.
@@ -67,6 +68,7 @@ We would love it if you could help contributing to this repository.
6768
* [Kévin Chalet](https://github.com/kevinchalet)
6869
* [Konstantin Mamaev](https://github.com/MrMeison)
6970
* [LeaFrock](https://github.com/LeaFrock)
71+
* [Levi Muriuki](https://github.com/levimatheri)
7072
* [Luke Fulliton](https://github.com/lukefulliton)
7173
* [Mariusz Zieliński](https://github.com/mariozski)
7274
* [Martin Costello](https://github.com/martincostello)
@@ -189,6 +191,7 @@ If a provider you're looking for does not exist, consider making a PR to add one
189191
| Yammer | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.Yammer?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.Yammer/ "Download AspNet.Security.OAuth.Yammer from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.Yammer?includePreReleases=true)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Yammer "Download AspNet.Security.OAuth.Yammer from MyGet.org") | [Documentation](https://developer.yammer.com/docs/oauth-2 "Yammer developer documentation") |
190192
| 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") |
191193
| 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") |
194+
| 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") |
192195

193196
<!--
194197
| CHANGEME | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.CHANGEME?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.CHANGEME/ "Download AspNet.Security.OAuth.CHANGEME from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.CHANGEME?includePreReleases=true)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.CHANGEME "Download AspNet.Security.OAuth.CHANGEME from MyGet.org") | [Documentation](CHANGEME "CHANGEME developer documentation") |

docs/zendesk.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Integrating the Zendesk Provider
2+
3+
## Example
4+
5+
```csharp
6+
services.AddAuthentication(options => /* Auth configuration */)
7+
.AddZendesk(options =>
8+
{
9+
options.ClientId = "my-client-id";
10+
options.ClientSecret = "my-client-secret";
11+
options.Domain = "https://glowingwaffle.zendesk.com";
12+
});
13+
```
14+
15+
## Required Additional Settings
16+
17+
| Property Name | Property Type | Description | Default Value |
18+
|:--|:--|:--|:--|
19+
| `Domain` | `string?` | The Zendesk domain (_Account URL_) to use for authentication. | `null` |
20+
21+
## Optional Settings
22+
23+
_None._
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
5+
</PropertyGroup>
6+
7+
<PropertyGroup>
8+
<Description>ASP.NET Core security middleware enabling Zendesk authentication.</Description>
9+
<Authors>Levi Muriuki</Authors>
10+
<PackageTags>aspnetcore;authentication;oauth;security;zendesk</PackageTags>
11+
</PropertyGroup>
12+
13+
<ItemGroup>
14+
<FrameworkReference Include="Microsoft.AspNetCore.App" />
15+
<PackageReference Include="JetBrains.Annotations" PrivateAssets="All" />
16+
</ItemGroup>
17+
18+
</Project>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
3+
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
4+
* for more information concerning the license and the contributors participating to this project.
5+
*/
6+
7+
using Microsoft.AspNetCore.Authentication;
8+
using Microsoft.AspNetCore.Authentication.OAuth;
9+
10+
namespace AspNet.Security.OAuth.Zendesk
11+
{
12+
/// <summary>
13+
/// Default values used by the Zendesk authentication middleware.
14+
/// </summary>
15+
public static class ZendeskAuthenticationDefaults
16+
{
17+
/// <summary>
18+
/// Default value for <see cref="AuthenticationScheme.Name"/>.
19+
/// </summary>
20+
public const string AuthenticationScheme = "Zendesk";
21+
22+
/// <summary>
23+
/// Default value for <see cref="AuthenticationScheme.DisplayName"/>.
24+
/// </summary>
25+
public const string DisplayName = "Zendesk";
26+
27+
/// <summary>
28+
/// Default value for <see cref="AuthenticationSchemeOptions.ClaimsIssuer"/>.
29+
/// </summary>
30+
public const string Issuer = "Zendesk";
31+
32+
/// <summary>
33+
/// Default value for <see cref="RemoteAuthenticationOptions.CallbackPath"/>.
34+
/// </summary>
35+
public const string CallbackPath = "/signin-zendesk";
36+
37+
/// <summary>
38+
/// Default path for <see cref="OAuthOptions.AuthorizationEndpoint"/>.
39+
/// </summary>
40+
public const string AuthorizationEndpointPath = "/oauth/authorizations/new";
41+
42+
/// <summary>
43+
/// Default path for <see cref="OAuthOptions.TokenEndpoint"/>.
44+
/// </summary>
45+
public const string TokenEndpointPath = "/oauth/tokens";
46+
47+
/// <summary>
48+
/// Default path for <see cref="OAuthOptions.UserInformationEndpoint"/>.
49+
/// </summary>
50+
public const string UserInformationEndpointPath = "/api/v2/users/me";
51+
}
52+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
3+
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
4+
* for more information concerning the license and the contributors participating to this project.
5+
*/
6+
7+
using System;
8+
using JetBrains.Annotations;
9+
using Microsoft.AspNetCore.Authentication;
10+
using Microsoft.Extensions.DependencyInjection;
11+
using Microsoft.Extensions.DependencyInjection.Extensions;
12+
using Microsoft.Extensions.Options;
13+
14+
namespace AspNet.Security.OAuth.Zendesk
15+
{
16+
/// <summary>
17+
/// Extension methods to add Zendesk authentication capabilities to an HTTP application pipeline.
18+
/// </summary>
19+
public static class ZendeskAuthenticationExtensions
20+
{
21+
/// <summary>
22+
/// Adds <see cref="ZendeskAuthenticationHandler"/> to the specified
23+
/// <see cref="AuthenticationBuilder"/>, which enables Zendesk authentication capabilities.
24+
/// </summary>
25+
/// <param name="builder">The authentication builder.</param>
26+
/// <returns>A reference to this instance after the operation has completed.</returns>
27+
public static AuthenticationBuilder AddZendesk([NotNull] this AuthenticationBuilder builder)
28+
{
29+
return builder.AddZendesk(ZendeskAuthenticationDefaults.AuthenticationScheme, options => { });
30+
}
31+
32+
/// <summary>
33+
/// Adds <see cref="ZendeskAuthenticationHandler"/> to the specified
34+
/// <see cref="AuthenticationBuilder"/>, which enables Zendesk authentication capabilities.
35+
/// </summary>
36+
/// <param name="builder">The authentication builder.</param>
37+
/// <param name="configuration">The delegate used to configure the OpenID 2.0 options.</param>
38+
/// <returns>A reference to this instance after the operation has completed.</returns>
39+
public static AuthenticationBuilder AddZendesk(
40+
[NotNull] this AuthenticationBuilder builder,
41+
[NotNull] Action<ZendeskAuthenticationOptions> configuration)
42+
{
43+
return builder.AddZendesk(ZendeskAuthenticationDefaults.AuthenticationScheme, configuration);
44+
}
45+
46+
/// <summary>
47+
/// Adds <see cref="ZendeskAuthenticationHandler"/> to the specified
48+
/// <see cref="AuthenticationBuilder"/>, which enables Zendesk authentication capabilities.
49+
/// </summary>
50+
/// <param name="builder">The authentication builder.</param>
51+
/// <param name="scheme">The authentication scheme associated with this instance.</param>
52+
/// <param name="configuration">The delegate used to configure the Zendesk options.</param>
53+
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
54+
public static AuthenticationBuilder AddZendesk(
55+
[NotNull] this AuthenticationBuilder builder,
56+
[NotNull] string scheme,
57+
[NotNull] Action<ZendeskAuthenticationOptions> configuration)
58+
{
59+
return builder.AddZendesk(scheme, ZendeskAuthenticationDefaults.DisplayName, configuration);
60+
}
61+
62+
/// <summary>
63+
/// Adds <see cref="ZendeskAuthenticationHandler"/> to the specified
64+
/// <see cref="AuthenticationBuilder"/>, which enables Zendesk authentication capabilities.
65+
/// </summary>
66+
/// <param name="builder">The authentication builder.</param>
67+
/// <param name="scheme">The authentication scheme associated with this instance.</param>
68+
/// <param name="caption">The optional display name associated with this instance.</param>
69+
/// <param name="configuration">The delegate used to configure the Zendesk options.</param>
70+
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
71+
public static AuthenticationBuilder AddZendesk(
72+
[NotNull] this AuthenticationBuilder builder,
73+
[NotNull] string scheme,
74+
[CanBeNull] string caption,
75+
[NotNull] Action<ZendeskAuthenticationOptions> configuration)
76+
{
77+
builder.Services.TryAddSingleton<IPostConfigureOptions<ZendeskAuthenticationOptions>, ZendeskPostConfigureOptions>();
78+
return builder.AddOAuth<ZendeskAuthenticationOptions, ZendeskAuthenticationHandler>(scheme, caption, configuration);
79+
}
80+
}
81+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
3+
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
4+
* for more information concerning the license and the contributors participating to this project.
5+
*/
6+
7+
using System.Net.Http;
8+
using System.Net.Http.Headers;
9+
using System.Security.Claims;
10+
using System.Text.Encodings.Web;
11+
using System.Text.Json;
12+
using System.Threading.Tasks;
13+
using JetBrains.Annotations;
14+
using Microsoft.AspNetCore.Authentication;
15+
using Microsoft.AspNetCore.Authentication.OAuth;
16+
using Microsoft.Extensions.Logging;
17+
using Microsoft.Extensions.Options;
18+
19+
namespace AspNet.Security.OAuth.Zendesk
20+
{
21+
public class ZendeskAuthenticationHandler : OAuthHandler<ZendeskAuthenticationOptions>
22+
{
23+
public ZendeskAuthenticationHandler(
24+
[NotNull] IOptionsMonitor<ZendeskAuthenticationOptions> options,
25+
[NotNull] ILoggerFactory logger,
26+
[NotNull] UrlEncoder encoder,
27+
[NotNull] ISystemClock clock)
28+
: base(options, logger, encoder, clock)
29+
{
30+
}
31+
32+
protected override async Task<AuthenticationTicket> CreateTicketAsync(
33+
[NotNull] ClaimsIdentity identity,
34+
[NotNull] AuthenticationProperties properties,
35+
[NotNull] OAuthTokenResponse tokens)
36+
{
37+
using var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);
38+
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
39+
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
40+
41+
using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);
42+
if (!response.IsSuccessStatusCode)
43+
{
44+
Logger.LogError("An error occurred while retrieving the user profile: the remote server " +
45+
"returned a {Status} response with the following payload: {Headers} {Body}.",
46+
/* Status: */ response.StatusCode,
47+
/* Headers: */ response.Headers.ToString(),
48+
/* Body: */ await response.Content.ReadAsStringAsync(Context.RequestAborted));
49+
50+
throw new HttpRequestException("An error occurred while retrieving the user profile.");
51+
}
52+
53+
using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted));
54+
55+
var principal = new ClaimsPrincipal(identity);
56+
var context = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement.GetProperty("user"));
57+
context.RunClaimActions();
58+
59+
await Options.Events.CreatingTicket(context);
60+
return new AuthenticationTicket(context.Principal!, context.Properties, Scheme.Name);
61+
}
62+
}
63+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
3+
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
4+
* for more information concerning the license and the contributors participating to this project.
5+
*/
6+
7+
using System;
8+
using System.Security.Claims;
9+
using Microsoft.AspNetCore.Authentication;
10+
using Microsoft.AspNetCore.Authentication.OAuth;
11+
12+
namespace AspNet.Security.OAuth.Zendesk
13+
{
14+
public class ZendeskAuthenticationOptions : OAuthOptions
15+
{
16+
/// <summary>
17+
/// Defines a set of options used by <see cref="ZendeskAuthenticationHandler"/>.
18+
/// </summary>
19+
public ZendeskAuthenticationOptions()
20+
{
21+
ClaimsIssuer = ZendeskAuthenticationDefaults.Issuer;
22+
CallbackPath = ZendeskAuthenticationDefaults.CallbackPath;
23+
24+
ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id");
25+
ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
26+
ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
27+
28+
Scope.Add("read");
29+
}
30+
31+
/// <summary>
32+
/// Gets or sets the Zendesk domain name.
33+
/// For example: <c>glowingwaffle.zendesk.com</c>.
34+
/// </summary>
35+
public string? Domain { get; set; }
36+
37+
/// <inheritdoc/>
38+
public override void Validate()
39+
{
40+
base.Validate();
41+
42+
if (!Uri.TryCreate(AuthorizationEndpoint, UriKind.Absolute, out _))
43+
{
44+
throw new ArgumentException(
45+
$"The '{nameof(AuthorizationEndpoint)}' option must be set to a valid URI.",
46+
nameof(AuthorizationEndpoint));
47+
}
48+
49+
if (!Uri.TryCreate(TokenEndpoint, UriKind.Absolute, out _))
50+
{
51+
throw new ArgumentException(
52+
$"The '{nameof(TokenEndpoint)}' option must be set to a valid URI.",
53+
nameof(TokenEndpoint));
54+
}
55+
56+
if (!Uri.TryCreate(UserInformationEndpoint, UriKind.Absolute, out _))
57+
{
58+
throw new ArgumentException(
59+
$"The '{nameof(UserInformationEndpoint)}' option must be set to a valid URI.",
60+
nameof(UserInformationEndpoint));
61+
}
62+
}
63+
}
64+
}

0 commit comments

Comments
 (0)