diff --git a/NuGet/Owin.Security.Providers.nuspec b/NuGet/Owin.Security.Providers.nuspec index 343003d..6038fd4 100644 --- a/NuGet/Owin.Security.Providers.nuspec +++ b/NuGet/Owin.Security.Providers.nuspec @@ -2,42 +2,33 @@ Owin.Security.Providers - 1.10.0 + 1.11.0 Jerrie Pelser, Jérémie Bertrand, Joseph Yanks, Tomáš Herceg, James Cuthbert, Payoj Baral Jerrie Pelser http://opensource.org/licenses/MIT https://github.com/owin-middleware/OwinOAuthProviders false - Additional OAuth providers for Katana (OWIN). Includes providers for - - LinkedIn - - Yahoo - - Google+ - - GitHub - - Reddit - - Instagram - - StackExchange - - SalesForce - - TripIt - - Buffer - - ArcGIS - - Dropbox - - Wordpress - - OpenID 2.0 providers + Additional OAuth providers for Katana (OWIN). Includes providers for LinkedIn, Yahoo, Google+, GitHub, Reddit, Instagram, StackExchange, SalesForce, TripIt, Buffer, ArcGIS, Dropbox, Wordpress, + OpenID 2.0 providers - Steam + Version 1.11.0 + - Add HealtGraph (Runkeeper) provider. Thank you Roberto Hernandez. (https://github.com/rjhernandez) + - Made change to WordPress provider to also retrieve information about the specific blog. + Version 1.10.0 - Add Dropbox provider - Add WordPress provider - + Version 1.9.1 - A couple of small fixes. - + Version 1.9.0 - Add ArcGIS provider Thank you Dave Timmins (https://github.com/davetimmins) - Fixes to Salesforce provider - + Version 1.8.1 - Fix bug in Reddit provider diff --git a/Owin.Security.Providers/HealthGraph/Constants.cs b/Owin.Security.Providers/HealthGraph/Constants.cs new file mode 100644 index 0000000..3b4b245 --- /dev/null +++ b/Owin.Security.Providers/HealthGraph/Constants.cs @@ -0,0 +1,7 @@ +namespace Owin.Security.Providers.HealthGraph +{ + internal static class Constants + { + public const string DefaultAuthenticationType = "HealthGraph"; + } +} \ No newline at end of file diff --git a/Owin.Security.Providers/HealthGraph/HealthGraphAuthenticationExtensions.cs b/Owin.Security.Providers/HealthGraph/HealthGraphAuthenticationExtensions.cs new file mode 100644 index 0000000..5942962 --- /dev/null +++ b/Owin.Security.Providers/HealthGraph/HealthGraphAuthenticationExtensions.cs @@ -0,0 +1,33 @@ +using System; + +namespace Owin.Security.Providers.HealthGraph +{ + public static class HealthGraphAuthenticationExtensions + { + public static IAppBuilder UseHealthGraphAuthentication( + this IAppBuilder app, + HealthGraphAuthenticationOptions options) + { + if (app == null) + throw new ArgumentNullException("app"); + if (options == null) + throw new ArgumentNullException("options"); + + app.Use(typeof(HealthGraphAuthenticationMiddleware), app, options); + + return app; + } + + public static IAppBuilder UseHealthGraphAuthentication( + this IAppBuilder app, + string clientId, + string clientSecret) + { + return app.UseHealthGraphAuthentication(new HealthGraphAuthenticationOptions + { + ClientId = clientId, + ClientSecret = clientSecret + }); + } + } +} \ No newline at end of file diff --git a/Owin.Security.Providers/HealthGraph/HealthGraphAuthenticationHandler.cs b/Owin.Security.Providers/HealthGraph/HealthGraphAuthenticationHandler.cs new file mode 100644 index 0000000..e8b5c5c --- /dev/null +++ b/Owin.Security.Providers/HealthGraph/HealthGraphAuthenticationHandler.cs @@ -0,0 +1,234 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.Owin; +using Microsoft.Owin.Infrastructure; +using Microsoft.Owin.Logging; +using Microsoft.Owin.Security; +using Microsoft.Owin.Security.Infrastructure; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Owin.Security.Providers.HealthGraph.Provider; + +namespace Owin.Security.Providers.HealthGraph +{ + public class HealthGraphAuthenticationHandler : AuthenticationHandler + { + private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string"; + + private HttpClient httpClient; + private ILogger logger; + + public HealthGraphAuthenticationHandler( + HttpClient httpClient, + ILogger logger) + { + this.httpClient = httpClient; + this.logger = logger; + } + + protected async override Task AuthenticateCoreAsync() + { + AuthenticationProperties properties = null; + + try + { + string code = null; + string state = null; + + IReadableStringCollection query = Request.Query; + IList 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); + } + + string requestPrefix = Request.Scheme + "://" + Request.Host; + string redirectUri = requestPrefix + Request.PathBase + Options.CallbackPath; + + // Build up the body for the token request + var body = new List>(); + body.Add(new KeyValuePair("grant_type", "authorization_code")); + body.Add(new KeyValuePair("code", code)); + body.Add(new KeyValuePair("redirect_uri", redirectUri)); + body.Add(new KeyValuePair("client_id", Options.ClientId)); + body.Add(new KeyValuePair("client_secret", Options.ClientSecret)); + + // Request the token + var requestMessage = new HttpRequestMessage(HttpMethod.Post, Options.Endpoints.TokenEndpoint); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + requestMessage.Content = new FormUrlEncodedContent(body); + HttpResponseMessage tokenResponse = await httpClient.SendAsync(requestMessage); + tokenResponse.EnsureSuccessStatusCode(); + string text = await tokenResponse.Content.ReadAsStringAsync(); + + // Deserializes the token response + dynamic response = JsonConvert.DeserializeObject(text); + string accessToken = (string)response.access_token; + + // Get the RunKeeper user + HttpRequestMessage userRequest = new HttpRequestMessage(HttpMethod.Get, Options.Endpoints.UserInfoEndpoint + "?access_token=" + Uri.EscapeDataString(accessToken)); + HttpResponseMessage userResponse = await httpClient.SendAsync(userRequest, Request.CallCancelled); + userResponse.EnsureSuccessStatusCode(); + var userText = await userResponse.Content.ReadAsStringAsync(); + JObject user = JObject.Parse(userText); + + // Get the RunKeeper Profile + HttpRequestMessage profileRequest = new HttpRequestMessage(HttpMethod.Get, Options.Endpoints.ProfileInfoEndpoint + "?access_token=" + Uri.EscapeDataString(accessToken)); + HttpResponseMessage profileResponse = await httpClient.SendAsync(profileRequest, Request.CallCancelled); + profileResponse.EnsureSuccessStatusCode(); + var profileText = await profileResponse.Content.ReadAsStringAsync(); + JObject profile = JObject.Parse(profileText); + + var context = new HealthGraphAuthenticatedContext(Context, user, profile, accessToken); + context.Identity = new ClaimsIdentity( + Options.AuthenticationType, + ClaimsIdentity.DefaultNameClaimType, + ClaimsIdentity.DefaultRoleClaimType); + + if (!string.IsNullOrEmpty(context.UserId)) + { + context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.UserId, 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(null); + } + + AuthenticationResponseChallenge challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode); + + if (challenge != null) + { + string baseUri = + Request.Scheme + + Uri.SchemeDelimiter + + Request.Host + + Request.PathBase; + + string currentUri = + baseUri + + Request.Path + + Request.QueryString; + + string redirectUri = + baseUri + + Options.CallbackPath; + + AuthenticationProperties properties = challenge.Properties; + if (string.IsNullOrEmpty(properties.RedirectUri)) + { + properties.RedirectUri = currentUri; + } + + // OAuth2 10.12 CSRF + GenerateCorrelationId(properties); + + // comma separated + string state = Options.StateDataFormat.Protect(properties); + + string authorizationEndpoint = + Options.Endpoints.AuthorizationEndpoint + + "?client_id=" + Uri.EscapeDataString(Options.ClientId) + + "&redirect_uri=" + Uri.EscapeDataString(redirectUri) + + "&response_type=code" + + "&state=" + Uri.EscapeDataString(state); + + Response.Redirect(authorizationEndpoint); + } + + return Task.FromResult(null); + } + + public override async Task InvokeAsync() + { + return await InvokeReplyPathAsync(); + } + + private async Task InvokeReplyPathAsync() + { + if (Options.CallbackPath.HasValue && Options.CallbackPath == Request.Path) + { + // TODO: error responses + + AuthenticationTicket ticket = await AuthenticateAsync(); + if (ticket == null) + { + logger.WriteWarning("Invalid return state, unable to redirect."); + Response.StatusCode = 500; + return true; + } + + var context = new HealthGraphReturnEndpointContext(Context, ticket); + context.SignInAsAuthenticationType = Options.SignInAsAuthenticationType; + context.RedirectUri = ticket.Properties.RedirectUri; + + await Options.Provider.ReturnEndpoint(context); + + if (context.SignInAsAuthenticationType != null && + context.Identity != null) + { + ClaimsIdentity 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) + { + string 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; + } + return false; + } + } +} \ No newline at end of file diff --git a/Owin.Security.Providers/HealthGraph/HealthGraphAuthenticationMiddleware.cs b/Owin.Security.Providers/HealthGraph/HealthGraphAuthenticationMiddleware.cs new file mode 100644 index 0000000..434d47e --- /dev/null +++ b/Owin.Security.Providers/HealthGraph/HealthGraphAuthenticationMiddleware.cs @@ -0,0 +1,83 @@ +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.HealthGraph.Provider; +using Owin.Security.Providers.Properties; + +namespace Owin.Security.Providers.HealthGraph +{ + public class HealthGraphAuthenticationMiddleware : AuthenticationMiddleware + { + private readonly HttpClient httpClient; + private readonly ILogger logger; + + public HealthGraphAuthenticationMiddleware( + OwinMiddleware next, + IAppBuilder app, + HealthGraphAuthenticationOptions 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(); + + if (Options.Provider == null) + Options.Provider = new HealthGraphAuthenticationProvider(); + + if (Options.StateDataFormat == null) + { + IDataProtector dataProtector = app.CreateDataProtector( + typeof(HealthGraphAuthenticationMiddleware).FullName, + Options.AuthenticationType, + "v1"); + Options.StateDataFormat = new PropertiesDataFormat(dataProtector); + } + + if (String.IsNullOrEmpty(Options.SignInAsAuthenticationType)) + Options.SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType(); + + httpClient = new HttpClient(ResolveHttpMessageHandler(Options)) + { + Timeout = Options.BackchannelTimeout, + MaxResponseContentBufferSize = 1024 * 1024 * 10, + }; + httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft Owin HealthGraph middleware"); + httpClient.DefaultRequestHeaders.ExpectContinue = false; + } + + protected override AuthenticationHandler CreateHandler() + { + return new HealthGraphAuthenticationHandler(httpClient, logger); + } + + private HttpMessageHandler ResolveHttpMessageHandler(HealthGraphAuthenticationOptions options) + { + HttpMessageHandler handler = options.BackchannelHttpHandler ?? new WebRequestHandler(); + + // If they provided a validator, apply it or fail. + if (options.BackchannelCertificateValidator != null) + { + // Set the cert validate callback + var webRequestHandler = handler as WebRequestHandler; + if (webRequestHandler == null) + { + throw new InvalidOperationException(Resources.Exception_ValidatorHandlerMismatch); + } + webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate; + } + + return handler; + } + } + +} diff --git a/Owin.Security.Providers/HealthGraph/HealthGraphAuthenticationOptions.cs b/Owin.Security.Providers/HealthGraph/HealthGraphAuthenticationOptions.cs new file mode 100644 index 0000000..89d2b3d --- /dev/null +++ b/Owin.Security.Providers/HealthGraph/HealthGraphAuthenticationOptions.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using Microsoft.Owin; +using Microsoft.Owin.Security; +using Microsoft.Owin.Security.DataHandler; +using Owin.Security.Providers.HealthGraph.Provider; + +namespace Owin.Security.Providers.HealthGraph +{ + public class HealthGraphAuthenticationOptions : AuthenticationOptions + { + private const string AuthorizationEndPoint = "https://runkeeper.com/apps/authorize"; + private const string TokenEndpoint = "https://runkeeper.com/apps/token"; + private const string UserInfoEndpoint = "https://api.runkeeper.com/user"; + private const string ProfileInfoEndpoint = "https://api.runkeeper.com/profile"; + + public class HealthGraphAuthenticationEndpoints + { + /// + /// Endpoint which is used to redirect users to request GitHub access + /// + /// + /// Defaults to https://runkeeper.com/apps/authorize + /// + public string AuthorizationEndpoint { get; set; } + + /// + /// Endpoint which is used to exchange code for access token + /// + /// + /// Defaults to https://api.runkeeper.com/profile + /// + public string TokenEndpoint { get; set; } + + /// + /// Endpoint which is used to obtain user information after authentication + /// + /// + /// Defaults to https://api.runkeeper.com/user + /// + public string UserInfoEndpoint { get; set; } + + + /// + /// Endpoint which is used to obtain user information after authentication + /// + /// + /// Defaults to https://api.runkeeper.com/profile + /// + public string ProfileInfoEndpoint { get; set; } + } + + public HealthGraphAuthenticationOptions() + : base(Constants.DefaultAuthenticationType) + { + Caption = Constants.DefaultAuthenticationType; + CallbackPath = new PathString("/signin-healthgraph"); + AuthenticationMode = AuthenticationMode.Passive; + BackchannelTimeout = TimeSpan.FromSeconds(60); + Endpoints = new HealthGraphAuthenticationEndpoints + { + AuthorizationEndpoint = AuthorizationEndPoint, + TokenEndpoint = TokenEndpoint, + UserInfoEndpoint = UserInfoEndpoint, + ProfileInfoEndpoint = ProfileInfoEndpoint, + }; + } + + public ICertificateValidator BackchannelCertificateValidator { get; set; } + + public HttpMessageHandler BackchannelHttpHandler { get; set; } + + public TimeSpan BackchannelTimeout { get; set; } + + public PathString CallbackPath { get; set; } + + public string Caption + { + get { return Description.Caption; } + set { Description.Caption = value; } + } + + public string ClientId { get; set; } + + public string ClientSecret { get; set; } + + public PropertiesDataFormat StateDataFormat { get; set; } + + public HealthGraphAuthenticationEndpoints Endpoints { get; set; } + + public IHealthGraphAuthenticationProvider Provider { get; set; } + + public string SignInAsAuthenticationType { get; set; } + } +} diff --git a/Owin.Security.Providers/HealthGraph/Provider/HealthGraphAuthenticatedContext.cs b/Owin.Security.Providers/HealthGraph/Provider/HealthGraphAuthenticatedContext.cs new file mode 100644 index 0000000..bb0d941 --- /dev/null +++ b/Owin.Security.Providers/HealthGraph/Provider/HealthGraphAuthenticatedContext.cs @@ -0,0 +1,44 @@ +using System.Security.Claims; +using Microsoft.Owin; +using Microsoft.Owin.Security; +using Microsoft.Owin.Security.Provider; +using Newtonsoft.Json.Linq; + +namespace Owin.Security.Providers.HealthGraph.Provider +{ + public class HealthGraphAuthenticatedContext : BaseContext + { + public HealthGraphAuthenticatedContext(IOwinContext context, + JObject user, + JObject profile, + string accessToken) : base(context) + { + User = user; + Profile = profile; + AccessToken = accessToken; + + UserId = TryGetValue(user, "userID"); + Name = TryGetValue(profile, "name"); + } + + public JObject Profile { get; set; } + + public JObject User { get; set; } + + public string UserId { get; set; } + + public string Name { get; set; } + + public string AccessToken { get; set; } + + public ClaimsIdentity Identity { get; set; } + + public AuthenticationProperties Properties { get; set; } + + private static string TryGetValue(JObject user, string propertyName) + { + JToken value; + return user.TryGetValue(propertyName, out value) ? value.ToString() : null; + } + } +} \ No newline at end of file diff --git a/Owin.Security.Providers/HealthGraph/Provider/HealthGraphAuthenticationProvider.cs b/Owin.Security.Providers/HealthGraph/Provider/HealthGraphAuthenticationProvider.cs new file mode 100644 index 0000000..e8755e4 --- /dev/null +++ b/Owin.Security.Providers/HealthGraph/Provider/HealthGraphAuthenticationProvider.cs @@ -0,0 +1,28 @@ +using System; +using System.Threading.Tasks; + +namespace Owin.Security.Providers.HealthGraph.Provider +{ + public class HealthGraphAuthenticationProvider : IHealthGraphAuthenticationProvider + { + public HealthGraphAuthenticationProvider() + { + OnAuthenticated = context => Task.FromResult(null); + OnReturnEndPoint = context => Task.FromResult(null); + } + + public Func OnAuthenticated { get; set; } + + public Func OnReturnEndPoint { get; set; } + + public Task Authenticated(HealthGraphAuthenticatedContext context) + { + return OnAuthenticated(context); + } + + public Task ReturnEndpoint(HealthGraphReturnEndpointContext context) + { + return OnReturnEndPoint(context); + } + } +} \ No newline at end of file diff --git a/Owin.Security.Providers/HealthGraph/Provider/HealthGraphReturnEndpointContext.cs b/Owin.Security.Providers/HealthGraph/Provider/HealthGraphReturnEndpointContext.cs new file mode 100644 index 0000000..10da06f --- /dev/null +++ b/Owin.Security.Providers/HealthGraph/Provider/HealthGraphReturnEndpointContext.cs @@ -0,0 +1,15 @@ +using Microsoft.Owin; +using Microsoft.Owin.Security; +using Microsoft.Owin.Security.Provider; + +namespace Owin.Security.Providers.HealthGraph.Provider +{ + public class HealthGraphReturnEndpointContext : ReturnEndpointContext + { + public HealthGraphReturnEndpointContext(IOwinContext context, + AuthenticationTicket ticket) + : base(context, ticket) + { + } + } +} \ No newline at end of file diff --git a/Owin.Security.Providers/HealthGraph/Provider/IHealthGraphAuthenticationProvider.cs b/Owin.Security.Providers/HealthGraph/Provider/IHealthGraphAuthenticationProvider.cs new file mode 100644 index 0000000..0cfb125 --- /dev/null +++ b/Owin.Security.Providers/HealthGraph/Provider/IHealthGraphAuthenticationProvider.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; +using Owin.Security.Providers.GitHub; + +namespace Owin.Security.Providers.HealthGraph.Provider +{ + public interface IHealthGraphAuthenticationProvider + { + Task Authenticated(HealthGraphAuthenticatedContext context); + + Task ReturnEndpoint(HealthGraphReturnEndpointContext context); + } +} \ No newline at end of file diff --git a/Owin.Security.Providers/Owin.Security.Providers.csproj b/Owin.Security.Providers/Owin.Security.Providers.csproj index f270997..6fa4ed0 100644 --- a/Owin.Security.Providers/Owin.Security.Providers.csproj +++ b/Owin.Security.Providers/Owin.Security.Providers.csproj @@ -95,6 +95,7 @@ + @@ -112,6 +113,13 @@ + + + + + + + @@ -162,6 +170,7 @@ + diff --git a/Owin.Security.Providers/Properties/AssemblyInfo.cs b/Owin.Security.Providers/Properties/AssemblyInfo.cs index aeb9991..fc34214 100644 --- a/Owin.Security.Providers/Properties/AssemblyInfo.cs +++ b/Owin.Security.Providers/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.10.0.0")] -[assembly: AssemblyFileVersion("1.10.0.0")] +[assembly: AssemblyVersion("1.11.0.0")] +[assembly: AssemblyFileVersion("1.11.0.0")] diff --git a/Owin.Security.Providers/WordPress/Provider/WordPressAuthenticatedContext.cs b/Owin.Security.Providers/WordPress/Provider/WordPressAuthenticatedContext.cs index df8ed3b..22a0b4c 100644 --- a/Owin.Security.Providers/WordPress/Provider/WordPressAuthenticatedContext.cs +++ b/Owin.Security.Providers/WordPress/Provider/WordPressAuthenticatedContext.cs @@ -23,13 +23,15 @@ namespace Owin.Security.Providers.WordPress /// WordPress Access token /// The ID for this blog /// The URL for this blog - public WordPressAuthenticatedContext(IOwinContext context, JObject user, string accessToken, string blogId, string blogUrl) + public WordPressAuthenticatedContext(IOwinContext context, JObject user, JObject site, string accessToken, string blogId, string blogUrl) : base(context) { User = user; + Site = site; AccessToken = accessToken; BlogId = blogId; BlogUrl = blogUrl; + BlogName = TryGetValue(site, "name"); Id = TryGetValue(user, "ID"); Name = TryGetValue(user, "display_name"); @@ -49,6 +51,13 @@ namespace Owin.Security.Providers.WordPress /// public JObject User { get; private set; } + /// + /// Gets the JSON-serialized user + /// + /// + /// Contains the WordPress user obtained from the endpoint https://public-api.wordpress.com/rest/v1/sites/{siteId} + /// + public JObject Site { get; private set; } /// /// Gets the WordPress OAuth access token /// @@ -64,6 +73,11 @@ namespace Owin.Security.Providers.WordPress /// public string BlogUrl { get; private set; } + /// + /// The name of the blog for the token + /// + public string BlogName { get; set; } + /// /// Gets the WordPress access token expiration time /// diff --git a/Owin.Security.Providers/WordPress/WordPressAuthenticationHandler.cs b/Owin.Security.Providers/WordPress/WordPressAuthenticationHandler.cs index 77a1e4d..94dcd44 100644 --- a/Owin.Security.Providers/WordPress/WordPressAuthenticationHandler.cs +++ b/Owin.Security.Providers/WordPress/WordPressAuthenticationHandler.cs @@ -18,6 +18,7 @@ namespace Owin.Security.Providers.WordPress private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string"; private const string TokenEndpoint = "https://public-api.wordpress.com/oauth2/token"; private const string UserInfoEndpoint = "https://public-api.wordpress.com/rest/v1/me"; + private const string SiteInfoEndpoint = "https://public-api.wordpress.com/rest/v1/sites/"; private readonly ILogger logger; private readonly HttpClient httpClient; @@ -93,7 +94,16 @@ namespace Owin.Security.Providers.WordPress text = await graphResponse.Content.ReadAsStringAsync(); JObject user = JObject.Parse(text); - var context = new WordPressAuthenticatedContext(Context, user, accessToken, blogId, blogUrl); + // Get the site details + HttpRequestMessage siteRequest = new HttpRequestMessage(HttpMethod.Get, SiteInfoEndpoint + blogId); + siteRequest.Headers.Add("User-Agent", "OWIN OAuth Provider"); + siteRequest.Headers.Add("Authorization", "BEARER " + accessToken); + HttpResponseMessage siteResponse = await httpClient.SendAsync(siteRequest, Request.CallCancelled); + siteResponse.EnsureSuccessStatusCode(); + text = await siteResponse.Content.ReadAsStringAsync(); + JObject site = JObject.Parse(text); + + var context = new WordPressAuthenticatedContext(Context, user, site, accessToken, blogId, blogUrl); context.Identity = new ClaimsIdentity( Options.AuthenticationType, ClaimsIdentity.DefaultNameClaimType, diff --git a/OwinOAuthProviders.sln b/OwinOAuthProviders.sln index 4317902..0f05d64 100644 --- a/OwinOAuthProviders.sln +++ b/OwinOAuthProviders.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 -VisualStudioVersion = 12.0.21005.1 +VisualStudioVersion = 12.0.30723.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OwinOAuthProvidersDemo", "OwinOAuthProvidersDemo\OwinOAuthProvidersDemo.csproj", "{5A438007-0C90-4DAC-BAA1-54A32164067F}" EndProject diff --git a/OwinOAuthProvidersDemo/App_Data/aspnet-OwinOAuthProvidersDemo-20131113093838.mdf b/OwinOAuthProvidersDemo/App_Data/aspnet-OwinOAuthProvidersDemo-20131113093838.mdf index 1d4a316..20d9ae7 100644 Binary files a/OwinOAuthProvidersDemo/App_Data/aspnet-OwinOAuthProvidersDemo-20131113093838.mdf and b/OwinOAuthProvidersDemo/App_Data/aspnet-OwinOAuthProvidersDemo-20131113093838.mdf differ diff --git a/OwinOAuthProvidersDemo/App_Data/aspnet-OwinOAuthProvidersDemo-20131113093838_log.ldf b/OwinOAuthProvidersDemo/App_Data/aspnet-OwinOAuthProvidersDemo-20131113093838_log.ldf index d98241e..631372c 100644 Binary files a/OwinOAuthProvidersDemo/App_Data/aspnet-OwinOAuthProvidersDemo-20131113093838_log.ldf and b/OwinOAuthProvidersDemo/App_Data/aspnet-OwinOAuthProvidersDemo-20131113093838_log.ldf differ diff --git a/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs b/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs index 0178faa..dcf6b3d 100755 --- a/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs +++ b/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs @@ -9,6 +9,7 @@ using Owin.Security.Providers.Dropbox; using Owin.Security.Providers.GitHub; using Owin.Security.Providers.GooglePlus; using Owin.Security.Providers.GooglePlus.Provider; +using Owin.Security.Providers.HealthGraph; using Owin.Security.Providers.Instagram; using Owin.Security.Providers.LinkedIn; using Owin.Security.Providers.Reddit; @@ -56,7 +57,7 @@ namespace OwinOAuthProvidersDemo //app.UseYahooAuthentication("", ""); //app.UseTripItAuthentication("", ""); - + //app.UseGitHubAuthentication("", ""); //app.UseBufferAuthentication("", ""); @@ -130,13 +131,17 @@ namespace OwinOAuthProvidersDemo // clientId: "", // clientSecret: ""); - //app.UseWordPressAuthentication( - // clientId: "", - // clientSecret: ""); + app.UseWordPressAuthentication( + clientId: "37490", + clientSecret: "rt3YrwGaX6yPtAnfwLuz1KBxwEVrYz4LdTynnNe15hxFQZc0ysqjrccl7689IlbD"); //app.UseDropboxAuthentication( // appKey: "", // appSecret: ""); + + //app.UseHealthGraphAuthentication( + // clientId: "", + // clientSecret: ""); } } } \ No newline at end of file diff --git a/README.md b/README.md index 2407237..963e719 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Provides a set of extra authentication providers for OWIN ([Project Katana](http - GitHub - Instagram - StackExchange + - HealthGraph - Battle.net - OpenID - Generic OpenID 2.0 provider