diff --git a/OwinOAuthProviders.sln b/OwinOAuthProviders.sln
index d2b7287c..94154535 100644
--- a/OwinOAuthProviders.sln
+++ b/OwinOAuthProviders.sln
@@ -98,6 +98,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers.Dis
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers.Geni", "src\Owin.Security.Providers.Geni\Owin.Security.Providers.Geni.csproj", "{9DE25431-F935-48D7-8EB5-ACB6F918111C}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers.MyHeritage", "src\Owin.Security.Providers.MyHeritage\Owin.Security.Providers.MyHeritage.csproj", "{84795078-31B5-4369-BD1B-F960165F8C71}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -296,6 +298,10 @@ Global
{9DE25431-F935-48D7-8EB5-ACB6F918111C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9DE25431-F935-48D7-8EB5-ACB6F918111C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9DE25431-F935-48D7-8EB5-ACB6F918111C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {84795078-31B5-4369-BD1B-F960165F8C71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {84795078-31B5-4369-BD1B-F960165F8C71}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {84795078-31B5-4369-BD1B-F960165F8C71}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {84795078-31B5-4369-BD1B-F960165F8C71}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs b/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs
index 185130af..23ed7430 100755
--- a/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs
+++ b/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs
@@ -278,8 +278,9 @@ public void ConfigureAuth(IAppBuilder app)
//app.("", "");
//app.UseOrcidAuthentication("","");
- //app.UseDiscordAuthentication("", "");
+ //app.UseDiscordAuthentication("", "");
//app.UseGeniAuthentication("", "");
+ //app.UseMyHeritageAuthentication("", "");
}
}
}
\ No newline at end of file
diff --git a/OwinOAuthProvidersDemo/OwinOAuthProvidersDemo.csproj b/OwinOAuthProvidersDemo/OwinOAuthProvidersDemo.csproj
index 3cd9038b..b99c417d 100644
--- a/OwinOAuthProvidersDemo/OwinOAuthProvidersDemo.csproj
+++ b/OwinOAuthProvidersDemo/OwinOAuthProvidersDemo.csproj
@@ -245,12 +245,6 @@
-
-
- {4be728eb-778a-41af-8dea-0c7159711d44}
- Owin.Security.Providers.Discord
-
-
diff --git a/README.md b/README.md
index 765a5e5d..eeb8cda4 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,7 @@ Provides a set of extra authentication providers for OWIN ([Project Katana](http
- HealthGraph
- Instagram
- LinkedIn
+ - MyHeritage
- Onshape
- ORCID
- PayPal
diff --git a/src/Owin.Security.Providers.MyHeritage/Constants.cs b/src/Owin.Security.Providers.MyHeritage/Constants.cs
new file mode 100644
index 00000000..bc3dca09
--- /dev/null
+++ b/src/Owin.Security.Providers.MyHeritage/Constants.cs
@@ -0,0 +1,7 @@
+namespace Owin.Security.Providers.MyHeritage
+{
+ internal static class Constants
+ {
+ public const string DefaultAuthenticationType = "MyHeritage";
+ }
+}
\ No newline at end of file
diff --git a/src/Owin.Security.Providers.MyHeritage/MyHeritageAuthenticationExtensions.cs b/src/Owin.Security.Providers.MyHeritage/MyHeritageAuthenticationExtensions.cs
new file mode 100644
index 00000000..8346f041
--- /dev/null
+++ b/src/Owin.Security.Providers.MyHeritage/MyHeritageAuthenticationExtensions.cs
@@ -0,0 +1,29 @@
+using System;
+
+namespace Owin.Security.Providers.MyHeritage
+{
+ public static class MyHeritageAuthenticationExtensions
+ {
+ public static IAppBuilder UseMyHeritageAuthentication(this IAppBuilder app,
+ MyHeritageAuthenticationOptions options)
+ {
+ if (app == null)
+ throw new ArgumentNullException(nameof(app));
+ if (options == null)
+ throw new ArgumentNullException(nameof(options));
+
+ app.Use(typeof(MyHeritageAuthenticationMiddleware), app, options);
+
+ return app;
+ }
+
+ public static IAppBuilder UseMyHeritageAuthentication(this IAppBuilder app, string clientId, string clientSecret)
+ {
+ return app.UseMyHeritageAuthentication(new MyHeritageAuthenticationOptions
+ {
+ ClientId = clientId,
+ ClientSecret = clientSecret
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Owin.Security.Providers.MyHeritage/MyHeritageAuthenticationHandler.cs b/src/Owin.Security.Providers.MyHeritage/MyHeritageAuthenticationHandler.cs
new file mode 100644
index 00000000..9e9e691d
--- /dev/null
+++ b/src/Owin.Security.Providers.MyHeritage/MyHeritageAuthenticationHandler.cs
@@ -0,0 +1,222 @@
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Security.Claims;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Owin.Infrastructure;
+using Microsoft.Owin.Logging;
+using Microsoft.Owin.Security;
+using Microsoft.Owin.Security.DataHandler.Encoder;
+using Microsoft.Owin.Security.Infrastructure;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Owin.Security.Providers.MyHeritage.Provider;
+
+namespace Owin.Security.Providers.MyHeritage
+{
+ public class MyHeritageAuthenticationHandler : AuthenticationHandler
+ {
+ private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
+
+ private readonly ILogger _logger;
+ private readonly HttpClient _httpClient;
+
+ public MyHeritageAuthenticationHandler(HttpClient httpClient, ILogger logger)
+ {
+ _httpClient = httpClient;
+ _logger = logger;
+ }
+
+ protected override async Task AuthenticateCoreAsync()
+ {
+ AuthenticationProperties properties = null;
+
+ try
+ {
+ string code = null;
+ string state = null;
+
+ var query = Request.Query;
+ var values = query.GetValues("code");
+ if (values != null && values.Count == 1)
+ {
+ code = values[0];
+ }
+ values = query.GetValues("state");
+ if (values != null && values.Count == 1)
+ {
+ state = values[0];
+ }
+
+ properties = Options.StateDataFormat.Unprotect(state);
+ if (properties == null)
+ {
+ return null;
+ }
+
+ // OAuth2 10.12 CSRF
+ if (!ValidateCorrelationId(properties, _logger))
+ {
+ return new AuthenticationTicket(null, properties);
+ }
+
+ var requestPrefix = Request.Scheme + "://" + Request.Host;
+ var redirectUri = requestPrefix + Request.PathBase + Options.CallbackPath;
+
+ // Build up the body for the token request
+ var body = new List>
+ {
+ new KeyValuePair("code", code),
+ new KeyValuePair("grant_type", "authorization_code"),
+ new KeyValuePair("client_id", Options.ClientId),
+ new KeyValuePair("client_secret", Options.ClientSecret),
+ new KeyValuePair("redirect_uri", redirectUri)
+ };
+
+ // Request the token
+ var tokenResponse =
+ await _httpClient.PostAsync(Options.Endpoints.TokenEndpoint, new FormUrlEncodedContent(body));
+ tokenResponse.EnsureSuccessStatusCode();
+ var text = await tokenResponse.Content.ReadAsStringAsync();
+
+ // Deserializes the token response
+ dynamic response = JsonConvert.DeserializeObject(text);
+ var accessToken = (string)response.access_token;
+ var refreshToken = (string)response.refresh_token;
+
+ // Get the MyHeritage user
+ var userInfoResponse = await _httpClient.GetAsync(
+ Options.Endpoints.UserEndpoint + "?bearer_token=" + Uri.EscapeDataString(accessToken), Request.CallCancelled);
+ userInfoResponse.EnsureSuccessStatusCode();
+ text = await userInfoResponse.Content.ReadAsStringAsync();
+ var user = JObject.Parse(text);
+
+ var context = new MyHeritageAuthenticatedContext(Context, user, accessToken, refreshToken)
+ {
+ Identity = new ClaimsIdentity(
+ Options.AuthenticationType,
+ ClaimsIdentity.DefaultNameClaimType,
+ ClaimsIdentity.DefaultRoleClaimType)
+ };
+ if (!string.IsNullOrEmpty(context.Id))
+ {
+ context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.Id, XmlSchemaString, Options.AuthenticationType));
+ }
+ if (!string.IsNullOrEmpty(context.Name))
+ {
+ context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.Name, XmlSchemaString, Options.AuthenticationType));
+ }
+ context.Properties = properties;
+
+ await Options.Provider.Authenticated(context);
+
+ return new AuthenticationTicket(context.Identity, context.Properties);
+ }
+ catch (Exception ex)
+ {
+ _logger.WriteError(ex.Message);
+ }
+ return new AuthenticationTicket(null, properties);
+ }
+
+ protected override Task ApplyResponseChallengeAsync()
+ {
+ if (Response.StatusCode != 401)
+ {
+ return Task.FromResult