-
Notifications
You must be signed in to change notification settings - Fork 235
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' of github.com:tparnell8/OwinOAuthProviders
- Loading branch information
Showing
18 changed files
with
977 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"; | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
src/Owin.Security.Providers.MyHeritage/MyHeritageAuthenticationExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
}); | ||
} | ||
} | ||
} |
222 changes: 222 additions & 0 deletions
222
src/Owin.Security.Providers.MyHeritage/MyHeritageAuthenticationHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
src/Owin.Security.Providers.MyHeritage/MyHeritageAuthenticationMiddleware.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
Oops, something went wrong.