Skip to content

Commit

Permalink
Merge branch 'master' of github.com:tparnell8/OwinOAuthProviders
Browse files Browse the repository at this point in the history
  • Loading branch information
TerribleDev committed Jun 11, 2016
2 parents f37aea4 + 45c98eb commit 63564a6
Show file tree
Hide file tree
Showing 18 changed files with 977 additions and 7 deletions.
6 changes: 6 additions & 0 deletions OwinOAuthProviders.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,9 @@ public void ConfigureAuth(IAppBuilder app)
//app.("", "");
//app.UseOrcidAuthentication("","");

//app.UseDiscordAuthentication("", "");
//app.UseDiscordAuthentication("", "");
//app.UseGeniAuthentication("", "");
//app.UseMyHeritageAuthentication("", "");
}
}
}
6 changes: 0 additions & 6 deletions OwinOAuthProvidersDemo/OwinOAuthProvidersDemo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -245,12 +245,6 @@
<ItemGroup>
<Folder Include="App_Data\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\src\Owin.Security.Providers.Discord\Owin.Security.Providers.Discord.csproj">
<Project>{4be728eb-778a-41af-8dea-0c7159711d44}</Project>
<Name>Owin.Security.Providers.Discord</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="packages.config" />
</ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Provides a set of extra authentication providers for OWIN ([Project Katana](http
- HealthGraph
- Instagram
- LinkedIn
- MyHeritage
- Onshape
- ORCID
- PayPal
Expand Down
7 changes: 7 additions & 0 deletions src/Owin.Security.Providers.MyHeritage/Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Owin.Security.Providers.MyHeritage
{
internal static class Constants
{
public const string DefaultAuthenticationType = "MyHeritage";
}
}
Original file line number Diff line number Diff line change
@@ -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
});
}
}
}
Original file line number Diff line number Diff line change
@@ -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<MyHeritageAuthenticationOptions>
{
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<AuthenticationTicket> 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<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("code", code),
new KeyValuePair<string, string>("grant_type", "authorization_code"),
new KeyValuePair<string, string>("client_id", Options.ClientId),
new KeyValuePair<string, string>("client_secret", Options.ClientSecret),
new KeyValuePair<string, string>("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<dynamic>(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<object>(null);
}

var challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode);

if (challenge == null) return Task.FromResult<object>(null);
var baseUri =
Request.Scheme +
Uri.SchemeDelimiter +
Request.Host +
Request.PathBase;

var currentUri =
baseUri +
Request.Path +
Request.QueryString;

var redirectUri =
baseUri +
Options.CallbackPath;

var properties = challenge.Properties;
if (string.IsNullOrEmpty(properties.RedirectUri))
{
properties.RedirectUri = currentUri;
}

// OAuth2 10.12 CSRF
GenerateCorrelationId(properties);

var state = Options.StateDataFormat.Protect(properties);

var authorizationEndpoint =
Options.Endpoints.AuthorizationEndpoint +
"?client_id=" + Uri.EscapeDataString(Options.ClientId) +
"&response_type=" + Uri.EscapeDataString("code") +
"&redirect_uri=" + Uri.EscapeDataString(redirectUri) +
"&state=" + Uri.EscapeDataString(state);

Response.Redirect(authorizationEndpoint);

return Task.FromResult<object>(null);
}

public override async Task<bool> InvokeAsync()
{
return await InvokeReplyPathAsync();
}

private async Task<bool> InvokeReplyPathAsync()
{
if (!Options.CallbackPath.HasValue || Options.CallbackPath != Request.Path) return false;
// TODO: error responses

var ticket = await AuthenticateAsync();
if (ticket == null)
{
_logger.WriteWarning("Invalid return state, unable to redirect.");
Response.StatusCode = 500;
return true;
}

var context = new MyHeritageReturnEndpointContext(Context, ticket)
{
SignInAsAuthenticationType = Options.SignInAsAuthenticationType,
RedirectUri = ticket.Properties.RedirectUri
};

await Options.Provider.ReturnEndpoint(context);

if (context.SignInAsAuthenticationType != null &&
context.Identity != null)
{
var grantIdentity = context.Identity;
if (!string.Equals(grantIdentity.AuthenticationType, context.SignInAsAuthenticationType, StringComparison.Ordinal))
{
grantIdentity = new ClaimsIdentity(grantIdentity.Claims, context.SignInAsAuthenticationType, grantIdentity.NameClaimType, grantIdentity.RoleClaimType);
}
Context.Authentication.SignIn(context.Properties, grantIdentity);
}

if (context.IsRequestCompleted || context.RedirectUri == null) return context.IsRequestCompleted;
var redirectUri = context.RedirectUri;
if (context.Identity == null)
{
// add a redirect hint that sign-in failed in some way
redirectUri = WebUtilities.AddQueryString(redirectUri, "error", "access_denied");
}
Response.Redirect(redirectUri);
context.RequestCompleted();

return context.IsRequestCompleted;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System;
using System.Globalization;
using System.Net.Http;
using Microsoft.Owin;
using Microsoft.Owin.Logging;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler;
using Microsoft.Owin.Security.DataProtection;
using Microsoft.Owin.Security.Infrastructure;
using Owin.Security.Providers.MyHeritage.Provider;

namespace Owin.Security.Providers.MyHeritage
{
public class MyHeritageAuthenticationMiddleware : AuthenticationMiddleware<MyHeritageAuthenticationOptions>
{
private readonly HttpClient _httpClient;
private readonly ILogger _logger;

public MyHeritageAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app,
MyHeritageAuthenticationOptions options)
: base(next, options)
{
if (string.IsNullOrWhiteSpace(Options.ClientId))
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
Resources.Exception_OptionMustBeProvided, "ClientId"));
if (string.IsNullOrWhiteSpace(Options.ClientSecret))
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
Resources.Exception_OptionMustBeProvided, "ClientSecret"));

_logger = app.CreateLogger<MyHeritageAuthenticationMiddleware>();

if (Options.Provider == null)
Options.Provider = new MyHeritageAuthenticationProvider();

if (Options.StateDataFormat == null)
{
var dataProtector = app.CreateDataProtector(
typeof (MyHeritageAuthenticationMiddleware).FullName,
Options.AuthenticationType, "v1");
Options.StateDataFormat = new PropertiesDataFormat(dataProtector);
}

if (string.IsNullOrEmpty(Options.SignInAsAuthenticationType))
Options.SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType();

_httpClient = new HttpClient(new WebRequestHandler())
{
Timeout = Options.BackchannelTimeout,
MaxResponseContentBufferSize = 1024*1024*10,
};
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft Owin MyHeritage middleware");
_httpClient.DefaultRequestHeaders.ExpectContinue = false;
}

/// <summary>
/// Provides the <see cref="T:Microsoft.Owin.Security.Infrastructure.AuthenticationHandler" /> object for processing
/// authentication-related requests.
/// </summary>
/// <returns>
/// An <see cref="T:Microsoft.Owin.Security.Infrastructure.AuthenticationHandler" /> configured with the
/// <see cref="T:Owin.Security.Providers.MyHeritage.MyHeritageAuthenticationOptions" /> supplied to the constructor.
/// </returns>
protected override AuthenticationHandler<MyHeritageAuthenticationOptions> CreateHandler()
{
return new MyHeritageAuthenticationHandler(_httpClient, _logger);
}
}
}
Loading

0 comments on commit 63564a6

Please sign in to comment.