diff --git a/.gitattributes b/.gitattributes
index 8a92b1213..10b136d73 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -3,6 +3,10 @@
###############################################################################
* text=auto
+*.cer binary
+*.p8 binary
+*.pfx binary
+
###############################################################################
# Set default behavior for command prompt diff.
#
diff --git a/AspNet.Security.OAuth.Providers.sln b/AspNet.Security.OAuth.Providers.sln
index ead4e67e5..f55209191 100644
--- a/AspNet.Security.OAuth.Providers.sln
+++ b/AspNet.Security.OAuth.Providers.sln
@@ -165,6 +165,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mvc.Client", "samples\Mvc.C
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNet.Security.OAuth.GitLab", "src\AspNet.Security.OAuth.GitLab\AspNet.Security.OAuth.GitLab.csproj", "{FACB1C2F-3BF7-4DD3-958B-E471E69E214A}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNet.Security.OAuth.Apple", "src\AspNet.Security.OAuth.Apple\AspNet.Security.OAuth.Apple.csproj", "{DC80AFEC-EFB4-4B21-BF79-44DEA6E1FE26}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -395,6 +397,10 @@ Global
{FACB1C2F-3BF7-4DD3-958B-E471E69E214A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FACB1C2F-3BF7-4DD3-958B-E471E69E214A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FACB1C2F-3BF7-4DD3-958B-E471E69E214A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DC80AFEC-EFB4-4B21-BF79-44DEA6E1FE26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DC80AFEC-EFB4-4B21-BF79-44DEA6E1FE26}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DC80AFEC-EFB4-4B21-BF79-44DEA6E1FE26}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DC80AFEC-EFB4-4B21-BF79-44DEA6E1FE26}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -458,6 +464,7 @@ Global
{112F6B50-0FD3-45AA-992E-72D7B873BF00} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
{140A6CAD-FC84-4A09-A939-8A5692DF0C7C} = {BAC7067D-88FE-4385-8AC9-1A325FFBDE69}
{FACB1C2F-3BF7-4DD3-958B-E471E69E214A} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
+ {DC80AFEC-EFB4-4B21-BF79-44DEA6E1FE26} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C7B54DE2-6407-4802-AD9C-CE54BF414C8C}
diff --git a/README.md b/README.md
index 02db921f9..d9ef7af4b 100644
--- a/README.md
+++ b/README.md
@@ -105,6 +105,7 @@ If a provider you're looking for does not exist, consider making a PR to add one
| Provider | Stable | Nightly | Documentation |
|:-:|:-:|:-:|:-:|
| Amazon | [](https://www.nuget.org/packages/AspNet.Security.OAuth.Amazon/ "Download AspNet.Security.OAuth.Amazon from NuGet.org") | [](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Amazon "Download AspNet.Security.OAuth.Amazon from MyGet.org") | [Documentation](https://developer.amazon.com/docs/login-with-amazon/documentation-overview.html "Amazon developer documentation") |
+| Apple | [](https://www.nuget.org/packages/AspNet.Security.OAuth.Apple/ "Download AspNet.Security.OAuth.Apple from NuGet.org") | [](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Apple "Download AspNet.Security.OAuth.Apple from MyGet.org") | [Documentation](https://developer.apple.com/documentation/signinwithapplerestapi "Apple developer documentation") |
| ArcGIS | [](https://www.nuget.org/packages/AspNet.Security.OAuth.ArcGIS/ "Download AspNet.Security.OAuth.ArcGIS from NuGet.org") | [](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.ArcGIS "Download AspNet.Security.OAuth.ArcGIS from MyGet.org") | [Documentation](https://developers.arcgis.com/documentation/core-concepts/security-and-authentication/what-is-oauth-2/ "ArcGIS developer documentation") |
| Asana | [](https://www.nuget.org/packages/AspNet.Security.OAuth.Asana/ "Download AspNet.Security.OAuth.Asana from NuGet.org") | [](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Asana "Download AspNet.Security.OAuth.Asana from MyGet.org") | [Documentation](https://asana.com/developers/documentation/getting-started/auth "Asana developer documentation") |
| Autodesk | [](https://www.nuget.org/packages/AspNet.Security.OAuth.Autodesk/ "Download AspNet.Security.OAuth.Autodesk from NuGet.org") | [](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Autodesk "Download AspNet.Security.OAuth.Autodesk from MyGet.org") | [Documentation](https://forge.autodesk.com/en/docs/oauth/v2/developers_guide/overview/ "Autodesk developer documentation") |
diff --git a/build/common.props b/build/common.props
index d6998bc5f..a43ca09ac 100644
--- a/build/common.props
+++ b/build/common.props
@@ -4,6 +4,7 @@
+ latest
$(NoWarn);CS1591
true
true
diff --git a/build/dependencies.props b/build/dependencies.props
index 3b04c9187..d16329a0a 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -9,6 +9,8 @@
0.1.0
1.0.0-beta2-19270-01
3.0.2
+ 5.3.0
+ 4.4.0
2.2.0
2.4.1
diff --git a/samples/Mvc.Client/Startup.cs b/samples/Mvc.Client/Startup.cs
index a685479fe..5396b6530 100644
--- a/samples/Mvc.Client/Startup.cs
+++ b/samples/Mvc.Client/Startup.cs
@@ -7,12 +7,25 @@
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.IdentityModel.Logging;
namespace Mvc.Client
{
public class Startup
{
+ public Startup(IConfiguration configuration, IHostingEnvironment hostingEnvironment)
+ {
+ Configuration = configuration;
+ HostingEnvironment = hostingEnvironment;
+ }
+
+ private IConfiguration Configuration { get; }
+
+ private IHostingEnvironment HostingEnvironment { get; }
+
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(options =>
@@ -45,6 +58,17 @@ public void ConfigureServices(IServiceCollection services)
options.Scope.Add("user:email");
})
+ /*
+ .AddApple(options =>
+ {
+ options.ClientId = Configuration["AppleClientId"];
+ options.KeyId = Configuration["AppleKeyId"];
+ options.TeamId = Configuration["AppleTeamId"];
+ options.UsePrivateKey(
+ (keyId) => HostingEnvironment.ContentRootFileProvider.GetFileInfo($"AuthKey_{keyId}.p8"));
+ })
+ */
+
.AddDropbox(options =>
{
options.ClientId = "jpk24g2uxfxe939";
@@ -56,7 +80,18 @@ public void ConfigureServices(IServiceCollection services)
public void Configure(IApplicationBuilder app)
{
- app.UseStaticFiles();
+ if (HostingEnvironment.IsDevelopment())
+ {
+ IdentityModelEventSource.ShowPII = true;
+ }
+
+ // Required to serve files with no extension in the .well-known folder
+ var options = new StaticFileOptions()
+ {
+ ServeUnknownFileTypes = true,
+ };
+
+ app.UseStaticFiles(options);
app.UseAuthentication();
diff --git a/src/AspNet.Security.OAuth.Apple/AppleAuthenticationConstants.cs b/src/AspNet.Security.OAuth.Apple/AppleAuthenticationConstants.cs
new file mode 100644
index 000000000..1aa1e16d5
--- /dev/null
+++ b/src/AspNet.Security.OAuth.Apple/AppleAuthenticationConstants.cs
@@ -0,0 +1,19 @@
+/*
+ * 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.Apple
+{
+ ///
+ /// Contains constants specific to the .
+ ///
+ public static class AppleAuthenticationConstants
+ {
+ ///
+ /// Default value for the token audience.
+ ///
+ public const string Audience = "https://appleid.apple.com";
+ }
+}
diff --git a/src/AspNet.Security.OAuth.Apple/AppleAuthenticationDefaults.cs b/src/AspNet.Security.OAuth.Apple/AppleAuthenticationDefaults.cs
new file mode 100644
index 000000000..be3ed5558
--- /dev/null
+++ b/src/AspNet.Security.OAuth.Apple/AppleAuthenticationDefaults.cs
@@ -0,0 +1,52 @@
+/*
+ * 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.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authentication.OAuth;
+
+namespace AspNet.Security.OAuth.Apple
+{
+ ///
+ /// Default values used by the Apple (Sign in with Apple) authentication provider.
+ ///
+ public static class AppleAuthenticationDefaults
+ {
+ ///
+ /// Default value for .
+ ///
+ public const string AuthenticationScheme = "Apple";
+
+ ///
+ /// Default value for .
+ ///
+ public const string DisplayName = "Apple";
+
+ ///
+ /// Default value for .
+ ///
+ public const string Issuer = "Apple";
+
+ ///
+ /// Default value for .
+ ///
+ public const string CallbackPath = "/signin-apple";
+
+ ///
+ /// Default value for .
+ ///
+ public const string AuthorizationEndpoint = "https://appleid.apple.com/auth/authorize";
+
+ ///
+ /// Default value for the endpoint to get the Apple public key to verify ID token signatures.
+ ///
+ public const string PublicKeyEndpoint = "https://appleid.apple.com/auth/keys";
+
+ ///
+ /// Default value for .
+ ///
+ public const string TokenEndpoint = "https://appleid.apple.com/auth/token";
+ }
+}
diff --git a/src/AspNet.Security.OAuth.Apple/AppleAuthenticationEvents.cs b/src/AspNet.Security.OAuth.Apple/AppleAuthenticationEvents.cs
new file mode 100644
index 000000000..574680ee3
--- /dev/null
+++ b/src/AspNet.Security.OAuth.Apple/AppleAuthenticationEvents.cs
@@ -0,0 +1,56 @@
+/*
+ * 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;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+using Microsoft.AspNetCore.Authentication.OAuth;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace AspNet.Security.OAuth.Apple
+{
+ ///
+ /// Default implementation.
+ ///
+ public class AppleAuthenticationEvents : OAuthEvents
+ {
+ ///
+ /// Gets or sets the delegate that is invoked when the method is invoked.
+ ///
+ public Func OnGenerateClientSecret { get; set; } = async context =>
+ {
+ var provider = context.HttpContext.RequestServices.GetService();
+ context.Options.ClientSecret = await provider.GenerateAsync(context);
+ };
+
+ ///
+ /// Gets or sets the delegate that is invoked when the method is invoked.
+ ///
+ public Func OnValidateIdToken { get; set; } = async context =>
+ {
+ var validator = context.HttpContext.RequestServices.GetRequiredService();
+ await validator.ValidateAsync(context);
+ };
+
+ ///
+ /// Invoked whenever the client secret needs to be generated.
+ ///
+ /// Contains information about the current request.
+ ///
+ /// A representing the completed operation.
+ ///
+ public virtual async Task GenerateClientSecret([NotNull] AppleGenerateClientSecretContext context) => await OnGenerateClientSecret(context);
+
+ ///
+ /// Invoked whenever the ID token needs to be validated.
+ ///
+ /// Contains information about the ID token to validate.
+ ///
+ /// A representing the completed operation.
+ ///
+ public virtual async Task ValidateIdToken([NotNull] AppleValidateIdTokenContext context) => await OnValidateIdToken(context);
+ }
+}
diff --git a/src/AspNet.Security.OAuth.Apple/AppleAuthenticationExtensions.cs b/src/AspNet.Security.OAuth.Apple/AppleAuthenticationExtensions.cs
new file mode 100644
index 000000000..ba1978971
--- /dev/null
+++ b/src/AspNet.Security.OAuth.Apple/AppleAuthenticationExtensions.cs
@@ -0,0 +1,86 @@
+/*
+ * 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;
+using System.IdentityModel.Tokens.Jwt;
+using AspNet.Security.OAuth.Apple;
+using AspNet.Security.OAuth.Apple.Internal;
+using JetBrains.Annotations;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+
+namespace Microsoft.Extensions.DependencyInjection
+{
+ ///
+ /// Extension methods to add Sign in with Apple authentication capabilities to an HTTP application pipeline.
+ ///
+ public static class AppleAuthenticationExtensions
+ {
+ ///
+ /// Adds to the specified
+ /// , which enables Apple authentication capabilities.
+ ///
+ /// The authentication builder.
+ /// The .
+ public static AuthenticationBuilder AddApple([NotNull] this AuthenticationBuilder builder)
+ {
+ return builder.AddApple(AppleAuthenticationDefaults.AuthenticationScheme, options => { });
+ }
+
+ ///
+ /// Adds to the specified
+ /// , which enables Apple authentication capabilities.
+ ///
+ /// The authentication builder.
+ /// The delegate used to configure the Apple options.
+ /// The .
+ public static AuthenticationBuilder AddApple(
+ [NotNull] this AuthenticationBuilder builder,
+ [NotNull] Action configuration)
+ {
+ return builder.AddApple(AppleAuthenticationDefaults.AuthenticationScheme, configuration);
+ }
+
+ ///
+ /// Adds to the specified
+ /// , which enables Apple authentication capabilities.
+ ///
+ /// The authentication builder.
+ /// The authentication scheme associated with this instance.
+ /// The delegate used to configure the Apple options.
+ /// The .
+ public static AuthenticationBuilder AddApple(
+ [NotNull] this AuthenticationBuilder builder,
+ [NotNull] string scheme,
+ [NotNull] Action configuration)
+ {
+ return builder.AddApple(scheme, AppleAuthenticationDefaults.DisplayName, configuration);
+ }
+
+ ///
+ /// Adds to the specified
+ /// , which enables Apple 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 Apple options.
+ /// The .
+ public static AuthenticationBuilder AddApple(
+ [NotNull] this AuthenticationBuilder builder,
+ [NotNull] string scheme,
+ [CanBeNull] string caption,
+ [NotNull] Action configuration)
+ {
+ builder.Services.TryAddSingleton();
+ builder.Services.TryAddSingleton();
+ builder.Services.TryAddSingleton();
+ builder.Services.TryAddSingleton();
+
+ return builder.AddOAuth(scheme, caption, configuration);
+ }
+ }
+}
diff --git a/src/AspNet.Security.OAuth.Apple/AppleAuthenticationHandler.cs b/src/AspNet.Security.OAuth.Apple/AppleAuthenticationHandler.cs
new file mode 100644
index 000000000..fca84e1bb
--- /dev/null
+++ b/src/AspNet.Security.OAuth.Apple/AppleAuthenticationHandler.cs
@@ -0,0 +1,347 @@
+/*
+ * 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;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IdentityModel.Tokens.Jwt;
+using System.Net.Http;
+using System.Security.Claims;
+using System.Text;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authentication.OAuth;
+using Microsoft.AspNetCore.WebUtilities;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Microsoft.Extensions.Primitives;
+using Newtonsoft.Json.Linq;
+
+namespace AspNet.Security.OAuth.Apple
+{
+ ///
+ /// Defines a handler for authentication using Apple.
+ ///
+ public class AppleAuthenticationHandler : OAuthHandler
+ {
+ private readonly JwtSecurityTokenHandler _tokenHandler;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The authentication options.
+ /// The logger to use.
+ /// The URL encoder to use.
+ /// The system clock to use.
+ /// The JWT security token handler to use.
+ public AppleAuthenticationHandler(
+ [NotNull] IOptionsMonitor options,
+ [NotNull] ILoggerFactory logger,
+ [NotNull] UrlEncoder encoder,
+ [NotNull] ISystemClock clock,
+ [NotNull] JwtSecurityTokenHandler tokenHandler)
+ : base(options, logger, encoder, clock)
+ {
+ _tokenHandler = tokenHandler;
+ }
+
+ ///
+ /// The handler calls methods on the events which give the application control at certain points where processing is occurring.
+ /// If it is not provided a default instance is supplied which does nothing when the methods are called.
+ ///
+ protected new AppleAuthenticationEvents Events
+ {
+ get { return (AppleAuthenticationEvents)base.Events; }
+ set { base.Events = value; }
+ }
+
+ ///
+ protected override string BuildChallengeUrl(
+ [NotNull] AuthenticationProperties properties,
+ [NotNull] string redirectUri)
+ {
+ string challengeUrl = base.BuildChallengeUrl(properties, redirectUri);
+
+ // Apple requires the response mode to be form_post when the email or name scopes are requested
+ return QueryHelpers.AddQueryString(challengeUrl, "response_mode", "form_post");
+ }
+
+ ///
+ protected override Task