diff --git a/.gitignore b/.gitignore index 12792ed..b3aafc2 100644 --- a/.gitignore +++ b/.gitignore @@ -11,14 +11,15 @@ *.sln.docstates # Build results - +.gitignore/ +.bundle/ [Dd]ebug/ [Rr]elease/ x64/ build/ [Bb]in/ [Oo]bj/ - +OwinOAuthProvidersDemo/App_Data/ # Visual Studo 2015 cache/options directory .vs/ Owin.Security.Providers.nuspec diff --git a/Gemfile.lock b/Gemfile.lock index a65d7e6..71308d0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -21,7 +21,6 @@ DEPENDENCIES albacore nokogiri os - rake BUNDLED WITH - 1.10.6 + 1.12.1 diff --git a/OwinOAuthProviders.sln b/OwinOAuthProviders.sln index 5178cf3..6141d54 100644 --- a/OwinOAuthProviders.sln +++ b/OwinOAuthProviders.sln @@ -2,8 +2,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OwinOAuthProvidersDemo", "OwinOAuthProvidersDemo\OwinOAuthProvidersDemo.csproj", "{5A438007-0C90-4DAC-BAA1-54A32164067F}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers.ArcGISOnline", "src\Owin.Security.Providers.ArcGISOnline\Owin.Security.Providers.ArcGISOnline.csproj", "{8A49FAEF-D365-4D25-942C-1CAD03845A5E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers.Asana", "src\Owin.Security.Providers.Asana\Owin.Security.Providers.Asana.csproj", "{F3E27220-1D8C-4037-94AA-7B7F4A12F351}" @@ -92,16 +90,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers.Ope EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers.SlackProvider", "src\Owin.Security.Providers.SlackProvider\Owin.Security.Providers.SlackProvider.csproj", "{3E6F293D-8500-428D-BDC9-27440CC91E16}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers.Orcid", "src\Owin.Security.Providers.Orcid\Owin.Security.Providers.Orcid.csproj", "{89CB4342-E23D-4E7C-89E5-C369599A5860}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OwinOAuthProvidersDemo", "OwinOAuthProvidersDemo\OwinOAuthProvidersDemo.csproj", "{5A438007-0C90-4DAC-BAA1-54A32164067F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5A438007-0C90-4DAC-BAA1-54A32164067F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5A438007-0C90-4DAC-BAA1-54A32164067F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5A438007-0C90-4DAC-BAA1-54A32164067F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5A438007-0C90-4DAC-BAA1-54A32164067F}.Release|Any CPU.Build.0 = Release|Any CPU {8A49FAEF-D365-4D25-942C-1CAD03845A5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8A49FAEF-D365-4D25-942C-1CAD03845A5E}.Debug|Any CPU.Build.0 = Debug|Any CPU {8A49FAEF-D365-4D25-942C-1CAD03845A5E}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -278,6 +276,14 @@ Global {3E6F293D-8500-428D-BDC9-27440CC91E16}.Debug|Any CPU.Build.0 = Debug|Any CPU {3E6F293D-8500-428D-BDC9-27440CC91E16}.Release|Any CPU.ActiveCfg = Release|Any CPU {3E6F293D-8500-428D-BDC9-27440CC91E16}.Release|Any CPU.Build.0 = Release|Any CPU + {89CB4342-E23D-4E7C-89E5-C369599A5860}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {89CB4342-E23D-4E7C-89E5-C369599A5860}.Debug|Any CPU.Build.0 = Debug|Any CPU + {89CB4342-E23D-4E7C-89E5-C369599A5860}.Release|Any CPU.ActiveCfg = Release|Any CPU + {89CB4342-E23D-4E7C-89E5-C369599A5860}.Release|Any CPU.Build.0 = Release|Any CPU + {5A438007-0C90-4DAC-BAA1-54A32164067F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A438007-0C90-4DAC-BAA1-54A32164067F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A438007-0C90-4DAC-BAA1-54A32164067F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A438007-0C90-4DAC-BAA1-54A32164067F}.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 75ddad9..dd040c8 100755 --- a/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs +++ b/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs @@ -3,278 +3,279 @@ using Microsoft.Owin; using Microsoft.Owin.Security.Cookies; using Owin; +//using Owin.Security.Providers.Orcid; + namespace OwinOAuthProvidersDemo { - public partial class Startup - { - // For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864 - public void ConfigureAuth(IAppBuilder app) - { - // Enable the application to use a cookie to store information for the signed in user - app.UseCookieAuthentication(new CookieAuthenticationOptions - { - AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, - LoginPath = new PathString("/Account/Login") - }); - // Use a cookie to temporarily store information about a user logging in with a third party login provider - app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie); - //app.UseDeviantArtAuthentication("id", "secret"); - //app.UseUntappdAuthentication("id", "secret"); - // Uncomment the following lines to enable logging in with third party login providers - //app.UseMicrosoftAccountAuthentication( - // clientId: "", - // clientSecret: ""); + public partial class Startup + { + // For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864 + public void ConfigureAuth(IAppBuilder app) + { + // Enable the application to use a cookie to store information for the signed in user + app.UseCookieAuthentication(new CookieAuthenticationOptions + { + AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, + LoginPath = new PathString("/Account/Login") + }); + // Use a cookie to temporarily store information about a user logging in with a third party login provider + app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie); + //app.UseDeviantArtAuthentication("id", "secret"); + //app.UseUntappdAuthentication("id", "secret"); + // Uncomment the following lines to enable logging in with third party login providers + //app.UseMicrosoftAccountAuthentication( + // clientId: "", + // clientSecret: ""); - //app.UseTwitterAuthentication( - // consumerKey: "", - // consumerSecret: ""); + //app.UseTwitterAuthentication( + // consumerKey: "", + // consumerSecret: ""); - //app.UseFacebookAuthentication( - // appId: "", - // appSecret: ""); + //app.UseFacebookAuthentication( + // appId: "", + // appSecret: ""); - //app.UseGoogleAuthentication(); + //app.UseGoogleAuthentication(); - //app.UseLinkedInAuthentication("", ""); + //app.UseLinkedInAuthentication("", ""); - //app.UseYahooAuthentication("", ""); + //app.UseYahooAuthentication("", ""); - //app.UseTripItAuthentication("", ""); + //app.UseTripItAuthentication("", ""); - //app.UseGitHubAuthentication("", ""); + //app.UseGitHubAuthentication("", ""); - //app.UseBufferAuthentication("", ""); + //app.UseBufferAuthentication("", ""); - //app.UseRedditAuthentication("", ""); + //app.UseRedditAuthentication("", ""); - //app.UseStackExchangeAuthentication( - // clientId: "", - // clientSecret: "", - // key: ""); + //app.UseStackExchangeAuthentication( + // clientId: "", + // clientSecret: "", + // key: ""); - //app.UseInstagramInAuthentication("", ""); + //app.UseInstagramInAuthentication("", ""); - //var options = new GooglePlusAuthenticationOptions - //{ - // ClientId = "", - // ClientSecret = "", - // RequestOfflineAccess = true, - // Provider = new GooglePlusAuthenticationProvider - // { - // OnAuthenticated = async context => System.Diagnostics.Debug.WriteLine(String.Format("Refresh Token: {0}", context.RefreshToken)) - // } - //}; - //options.MomentTypes.Add("http://schemas.google.com/AddActivity"); - //options.MomentTypes.Add("http://schemas.google.com/CheckInActivity"); - //options.MomentTypes.Add("http://schemas.google.com/BuyActivity"); - //app.UseGooglePlusAuthentication(options); + //var options = new GooglePlusAuthenticationOptions + //{ + // ClientId = "", + // ClientSecret = "", + // RequestOfflineAccess = true, + // Provider = new GooglePlusAuthenticationProvider + // { + // OnAuthenticated = async context => System.Diagnostics.Debug.WriteLine(String.Format("Refresh Token: {0}", context.RefreshToken)) + // } + //}; + //options.MomentTypes.Add("http://schemas.google.com/AddActivity"); + //options.MomentTypes.Add("http://schemas.google.com/CheckInActivity"); + //options.MomentTypes.Add("http://schemas.google.com/BuyActivity"); + //app.UseGooglePlusAuthentication(options); - /* + /* * Twitch sign-ins use /signin-Twitch as the URL for authentication - * - + * + */ - ////Simple Twitch Sign-in - //app.UseTwitchAuthentication("", ""); + ////Simple Twitch Sign-in + //app.UseTwitchAuthentication("", ""); - ////More complex Twitch Sign-in - //var opt = new TwitchAuthenticationOptions() - //{ - // ClientId = "", - // ClientSecret = "", - // Provider = new TwitchAuthenticationProvider() - // { + ////More complex Twitch Sign-in + //var opt = new TwitchAuthenticationOptions() + //{ + // ClientId = "", + // ClientSecret = "", + // Provider = new TwitchAuthenticationProvider() + // { + // OnAuthenticated = async z => + // { + //// Getting the twitch users picture + // z.Identity.AddClaim(new Claim("Picture", z.User.GetValue("logo").ToString())); + // } + //// You should be able to access these claims with HttpContext.GetOwinContext().Authentication.GetExternalLoginInfoAsync().Claims in your Account Controller + // // Commonly used in the ExternalLoginCallback() in AccountController.cs + // /* - // OnAuthenticated = async z => - // { - //// Getting the twitch users picture - // z.Identity.AddClaim(new Claim("Picture", z.User.GetValue("logo").ToString())); - // } - //// You should be able to access these claims with HttpContext.GetOwinContext().Authentication.GetExternalLoginInfoAsync().Claims in your Account Controller - // // Commonly used in the ExternalLoginCallback() in AccountController.cs - // /* + // if (user != null) + // { + // var claim = (await AuthenticationManager.GetExternalLoginInfoAsync()).ExternalIdentity.Claims.First( + // a => a.Type == "Picture"); + // user.Claims.Add(new IdentityUserClaim() { ClaimType = claim.Type, ClaimValue = claim.Value }); + // await SignInAsync(user, isPersistent: false); + // return RedirectToLocal(returnUrl); + // } + // */ + // } + //}; + //app.UseTwitchAuthentication(opt); - // if (user != null) - // { - // var claim = (await AuthenticationManager.GetExternalLoginInfoAsync()).ExternalIdentity.Claims.First( - // a => a.Type == "Picture"); - // user.Claims.Add(new IdentityUserClaim() { ClaimType = claim.Type, ClaimValue = claim.Value }); - // await SignInAsync(user, isPersistent: false); - // return RedirectToLocal(returnUrl); - // } - // */ - // } - //}; - //app.UseTwitchAuthentication(opt); + //app.UseOpenIDAuthentication("http://me.yahoo.com/", "Yahoo"); + //app.UseOpenIDAuthentication("https://openid.stackexchange.com/", "StackExchange"); + //app.UseOpenIDAuthentication("https://www.google.com/accounts/o8/id", "Google"); - //app.UseOpenIDAuthentication("http://me.yahoo.com/", "Yahoo"); + //app.UseSteamAuthentication(applicationKey: ""); - //app.UseOpenIDAuthentication("https://openid.stackexchange.com/", "StackExchange"); + //app.UseOpenIDAuthentication("http://orange.fr", "Orange"); + // Use OpenId provider login uri instead of discovery uri + //app.UseOpenIDAuthentication("http://openid.orange.fr/server", "Orange", true); - //app.UseOpenIDAuthentication("https://www.google.com/accounts/o8/id", "Google"); + //app.UseSalesforceAuthentication( + // clientId: "", + // clientSecret: ""); - //app.UseSteamAuthentication(applicationKey: ""); + //in scenarios where a sandbox URL needs to be used + //var salesforceOptions = new SalesforceAuthenticationOptions + //{ + // Endpoints = + // new SalesforceAuthenticationOptions.SalesforceAuthenticationEndpoints + // { + // AuthorizationEndpoint = + // "https://ap1.salesforce.com/services/oauth2/authorize", + // TokenEndpoint = "https://ap1.salesforce.com/services/oauth2/token" + // }, + // ClientId = "", + // ClientSecret = "", + // Provider = new SalesforceAuthenticationProvider() + // { + // OnAuthenticated = async context => + // { + // System.Diagnostics.Debug.WriteLine(context.AccessToken); + // System.Diagnostics.Debug.WriteLine(context.RefreshToken); + // System.Diagnostics.Debug.WriteLine(context.OrganizationId); + // } + // } + //}; + //app.UseSalesforceAuthentication(salesforceOptions); - //app.UseOpenIDAuthentication("http://orange.fr", "Orange"); - // Use OpenId provider login uri instead of discovery uri - //app.UseOpenIDAuthentication("http://openid.orange.fr/server", "Orange", true); + ////app.UseShopifyAuthentication("", ""); - //app.UseSalesforceAuthentication( - // clientId: "", - // clientSecret: ""); + //app.UseArcGISOnlineAuthentication( + // clientId: "", + // clientSecret: ""); - //in scenarios where a sandbox URL needs to be used - //var salesforceOptions = new SalesforceAuthenticationOptions - //{ - // Endpoints = - // new SalesforceAuthenticationOptions.SalesforceAuthenticationEndpoints - // { - // AuthorizationEndpoint = - // "https://ap1.salesforce.com/services/oauth2/authorize", - // TokenEndpoint = "https://ap1.salesforce.com/services/oauth2/token" - // }, - // ClientId = "", - // ClientSecret = "", - // Provider = new SalesforceAuthenticationProvider() - // { - // OnAuthenticated = async context => - // { - // System.Diagnostics.Debug.WriteLine(context.AccessToken); - // System.Diagnostics.Debug.WriteLine(context.RefreshToken); - // System.Diagnostics.Debug.WriteLine(context.OrganizationId); - // } - // } - //}; - //app.UseSalesforceAuthentication(salesforceOptions); + //app.UseWordPressAuthentication( + // clientId: "", + // clientSecret: ""); - ////app.UseShopifyAuthentication("", ""); + //app.UseDropboxAuthentication( + // appKey: "", + // appSecret: ""); - //app.UseArcGISOnlineAuthentication( - // clientId: "", - // clientSecret: ""); + //app.UseHealthGraphAuthentication( + // clientId: "", + // clientSecret: ""); - //app.UseWordPressAuthentication( - // clientId: "", - // clientSecret: ""); + //app.UseBattleNetAuthentication(new BattleNetAuthenticationOptions + //{ + // ClientId = "", + // ClientSecret = "" + //}); + //app.UseBattleNetAuthentication( + // clientId: "", + // clientSecret: ""); - //app.UseDropboxAuthentication( - // appKey: "", - // appSecret: ""); + //app.UseAsanaAuthentication("", ""); - //app.UseHealthGraphAuthentication( - // clientId: "", - // clientSecret: ""); + //app.UseEveOnlineAuthentication("", ""); - //app.UseBattleNetAuthentication(new BattleNetAuthenticationOptions - //{ - // ClientId = "", - // ClientSecret = "" - //}); - //app.UseBattleNetAuthentication( - // clientId: "", - // clientSecret: ""); + //app.UseSoundCloudAuthentication("", ""); - //app.UseAsanaAuthentication("", ""); + //app.UseFoursquareAuthentication( + // clientId: "", + // clientSecret: ""); - //app.UseEveOnlineAuthentication("", ""); + //app.UsePayPalAuthentication( + // clientId: "", + // clientSecret: "", + // isSandbox: false); - //app.UseSoundCloudAuthentication("", ""); + //app.UseWargamingAccountAuthentication("", WargamingAuthenticationOptions.Region.NorthAmerica); - //app.UseFoursquareAuthentication( - // clientId: "", - // clientSecret: ""); + //app.UseFlickrAuthentication("", ""); + //app.UseVisualStudioAuthentication( + // appId: "", + // appSecret: ""); - //app.UsePayPalAuthentication( - // clientId: "", - // clientSecret: "", - // isSandbox: false); + //app.UseSpotifyAuthentication( + // clientId: "", + // clientSecret: ""); - //app.UseWargamingAccountAuthentication("", WargamingAuthenticationOptions.Region.NorthAmerica); + //var options = new SlackAuthenticationOptions + //{ + // ClientId = "", + // ClientSecret = "", + // TeamId = "" // optional + //}; + //options.Scope.Add("identify"); + //app.UseSlackAuthentication(options); - //app.UseFlickrAuthentication("", ""); - //app.UseVisualStudioAuthentication( - // appId: "", - // appSecret: ""); + //app.UseGitterAuthentication( + // clientId: "", + // clientSecret: "" + //); - //app.UseSpotifyAuthentication( - // clientId: "", - // clientSecret: ""); + //app.UseImgurAuthentication( + // new ImgurAuthenticationOptions + // { + // ClientId = "", + // ClientSecret = "" + // }); - //var options = new SlackAuthenticationOptions - //{ - // ClientId = "", - // ClientSecret = "", - // TeamId = "" // optional - //}; - //options.Scope.Add("identify"); - //app.UseSlackAuthentication(options); + //var options = new BacklogAuthenticationOptions + //{ + // ClientId = "", + // ClientSecret = "", + // ContractName = "", + // CallbackPath = new PathString(""), // ex.new PathString("/OauthTokenRequest") + // Provider = new BacklogAuthenticationProvider + // { + // OnAuthenticated = async context => await System.Threading.Tasks.Task.Run(()=> { System.Diagnostics.Debug.WriteLine(String.Format("Refresh Token: {0}", context.RefreshToken)); }) + // } + //}; - //app.UseGitterAuthentication( - // clientId: "", - // clientSecret: "" - //); + //app.UseBacklogAuthentication(options); - //app.UseImgurAuthentication( - // new ImgurAuthenticationOptions - // { - // ClientId = "", - // ClientSecret = "" - // }); + //var cosignOptions = new CosignAuthenticationOptions + //{ + // AuthenticationType = "Cosign", + // SignInAsAuthenticationType = signInAsType, + // CosignServer = "weblogin.umich.edu", + // CosignServicePort = 6663, + // IdentityServerHostInstance = "core1", + // ClientServer = "cosignservername" + //}; + //app.UseCosignAuthentication(cosignOptions); - //var options = new BacklogAuthenticationOptions - //{ - // ClientId = "", - // ClientSecret = "", - // ContractName = "", - // CallbackPath = new PathString(""), // ex.new PathString("/OauthTokenRequest") - // Provider = new BacklogAuthenticationProvider - // { - // OnAuthenticated = async context => await System.Threading.Tasks.Task.Run(()=> { System.Diagnostics.Debug.WriteLine(String.Format("Refresh Token: {0}", context.RefreshToken)); }) - // } - //}; + //app.UseVimeoAuthentication("", ""); - //app.UseBacklogAuthentication(options); + //app.UseFitbitAuthentication(new FitbitAuthenticationOptions + //{ + // ClientId = "", + // ClientSecret = "" + //}); - //var cosignOptions = new CosignAuthenticationOptions - //{ - // AuthenticationType = "Cosign", - // SignInAsAuthenticationType = signInAsType, - // CosignServer = "weblogin.umich.edu", - // CosignServicePort = 6663, - // IdentityServerHostInstance = "core1", - // ClientServer = "cosignservername" - //}; - //app.UseCosignAuthentication(cosignOptions); + //app.UseOnshapeAuthentication( + // appKey: "", + // appSecret: ""); + // + // + //app.UseOnshapeAuthentication(new OnshapeAuthenticationOptions() + //{ + // AppKey = "", + // AppSecret = "", + // CallbackPath = new PathString("/oauthRedirect"), + // Hostname = "partner.dev.onshape.com" + //}); - //app.UseVimeoAuthentication("", ""); - - //app.UseFitbitAuthentication(new FitbitAuthenticationOptions - //{ - // ClientId = "", - // ClientSecret = "" - //}); + //app.UseVKontakteAuthentication("", ""); - //app.UseOnshapeAuthentication( - // appKey: "", - // appSecret: ""); - // - // - //app.UseOnshapeAuthentication(new OnshapeAuthenticationOptions() - //{ - // AppKey = "", - // AppSecret = "", - // CallbackPath = new PathString("/oauthRedirect"), - // Hostname = "partner.dev.onshape.com" - //}); + //app.UseXingAuthentication("", ""); - //app.UseVKontakteAuthentication("", ""); - - //app.UseXingAuthentication("", ""); - - //app.UseDoYouBuzzAuthentication("", ""); - } - } -} + //app.UseDoYouBuzzAuthentication("", ""); + //app.("", ""); + //app.UseOrcidAuthentication("APP-QQ4XO1AYU3WR696B", "6bb22d2e-71b3-4d5d-b1af-f5d3a8cd4270"); + } + } +} \ No newline at end of file diff --git a/OwinOAuthProvidersDemo/OwinOAuthProvidersDemo.csproj b/OwinOAuthProvidersDemo/OwinOAuthProvidersDemo.csproj index bbaaf3f..2d2c708 100644 --- a/OwinOAuthProvidersDemo/OwinOAuthProvidersDemo.csproj +++ b/OwinOAuthProvidersDemo/OwinOAuthProvidersDemo.csproj @@ -13,7 +13,7 @@ Properties OwinOAuthProvidersDemo OwinOAuthProvidersDemo - v4.5 + v4.5.2 false true 44300 @@ -21,6 +21,7 @@ disabled false + true @@ -65,16 +66,16 @@ True - - ..\packages\Microsoft.Owin.2.1.0\lib\net45\Microsoft.Owin.dll + + ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll True ..\packages\Microsoft.Owin.Host.SystemWeb.2.1.0\lib\net45\Microsoft.Owin.Host.SystemWeb.dll True - - ..\packages\Microsoft.Owin.Security.2.1.0\lib\net45\Microsoft.Owin.Security.dll + + ..\packages\Microsoft.Owin.Security.3.0.1\lib\net45\Microsoft.Owin.Security.dll True @@ -105,8 +106,8 @@ ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll True - - ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll + + ..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll True @@ -115,13 +116,13 @@ + - - + ..\packages\Microsoft.AspNet.WebPages.3.2.0\lib\net45\System.Web.Helpers.dll True @@ -150,9 +151,7 @@ ..\packages\Microsoft.AspNet.WebPages.3.2.0\lib\net45\System.Web.WebPages.Razor.dll True - - @@ -163,6 +162,7 @@ + ..\packages\WebGrease.1.6.0\lib\WebGrease.dll True diff --git a/OwinOAuthProvidersDemo/Web.config b/OwinOAuthProvidersDemo/Web.config index dc9e98f..0f5f4a7 100644 --- a/OwinOAuthProvidersDemo/Web.config +++ b/OwinOAuthProvidersDemo/Web.config @@ -1,16 +1,15 @@ - + -
- + @@ -18,9 +17,17 @@ + - + @@ -40,11 +47,11 @@ - + - + @@ -60,7 +67,7 @@ - + diff --git a/OwinOAuthProvidersDemo/packages.config b/OwinOAuthProvidersDemo/packages.config index d4b92b5..54ead64 100644 --- a/OwinOAuthProvidersDemo/packages.config +++ b/OwinOAuthProvidersDemo/packages.config @@ -13,9 +13,9 @@ - + - + @@ -24,7 +24,7 @@ - + diff --git a/README.md b/README.md index 718641a..f31335f 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ Provides a set of extra authentication providers for OWIN ([Project Katana](http - Instagram - LinkedIn - Onshape + - ORCID - PayPal - Reddit - Salesforce diff --git a/src/Owin.Security.Providers.Orcid/Constants.cs b/src/Owin.Security.Providers.Orcid/Constants.cs new file mode 100644 index 0000000..acb1dd2 --- /dev/null +++ b/src/Owin.Security.Providers.Orcid/Constants.cs @@ -0,0 +1,7 @@ +namespace Owin.Security.Providers.Orcid +{ + internal static class Constants + { + public const string DefaultAuthenticationType = "Orcid"; + } +} \ No newline at end of file diff --git a/src/Owin.Security.Providers.Orcid/Message/OrcidMessage.cs b/src/Owin.Security.Providers.Orcid/Message/OrcidMessage.cs new file mode 100644 index 0000000..f60bf79 --- /dev/null +++ b/src/Owin.Security.Providers.Orcid/Message/OrcidMessage.cs @@ -0,0 +1,293 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Owin.Security.Providers.Orcid.Message +{ + public class OrcidIdentifier + { + + [JsonProperty("value")] + public object Value { get; set; } + + [JsonProperty("uri")] + public string Uri { get; set; } + + [JsonProperty("path")] + public string Path { get; set; } + + [JsonProperty("host")] + public string Host { get; set; } + } + + public class OrcidPreferences + { + + [JsonProperty("locale")] + public string Locale { get; set; } + } + + public class SubmissionDate + { + + [JsonProperty("value")] + public long Value { get; set; } + } + + public class LastModifiedDate + { + + [JsonProperty("value")] + public long Value { get; set; } + } + + public class Claimed + { + + [JsonProperty("value")] + public bool Value { get; set; } + } + + public class VerifiedEmail + { + + [JsonProperty("value")] + public bool Value { get; set; } + } + + public class VerifiedPrimaryEmail + { + + [JsonProperty("value")] + public bool Value { get; set; } + } + + public class OrcidHistory + { + + [JsonProperty("creation-method")] + public string CreationMethod { get; set; } + + [JsonProperty("completion-date")] + public object CompletionDate { get; set; } + + [JsonProperty("submission-date")] + public SubmissionDate SubmissionDate { get; set; } + + [JsonProperty("last-modified-date")] + public LastModifiedDate LastModifiedDate { get; set; } + + [JsonProperty("claimed")] + public Claimed Claimed { get; set; } + + [JsonProperty("source")] + public object Source { get; set; } + + [JsonProperty("deactivation-date")] + public object DeactivationDate { get; set; } + + [JsonProperty("verified-email")] + public VerifiedEmail VerifiedEmail { get; set; } + + [JsonProperty("verified-primary-email")] + public VerifiedPrimaryEmail VerifiedPrimaryEmail { get; set; } + + [JsonProperty("visibility")] + public object Visibility { get; set; } + } + + public class GivenNames + { + + [JsonProperty("value")] + public string Value { get; set; } + + [JsonProperty("visibility")] + public object Visibility { get; set; } + } + + public class FamilyName + { + + [JsonProperty("value")] + public string Value { get; set; } + + [JsonProperty("visibility")] + public object Visibility { get; set; } + } + + public class OtherNames + { + + [JsonProperty("other-name")] + public object[] OtherName { get; set; } + + [JsonProperty("visibility")] + public string Visibility { get; set; } + } + + public class PersonalDetails + { + + [JsonProperty("given-names")] + public GivenNames GivenNames { get; set; } + + [JsonProperty("family-name")] + public FamilyName FamilyName { get; set; } + + [JsonProperty("credit-name")] + public object CreditName { get; set; } + + [JsonProperty("other-names")] + public OtherNames OtherNames { get; set; } + } + + public class Biography + { + + [JsonProperty("value")] + public object Value { get; set; } + + [JsonProperty("visibility")] + public string Visibility { get; set; } + } + + public class ResearcherUrls + { + + [JsonProperty("researcher-url")] + public object[] ResearcherUrl { get; set; } + + [JsonProperty("visibility")] + public string Visibility { get; set; } + } + + public class Email + { + + [JsonProperty("value")] + public string Value { get; set; } + + [JsonProperty("primary")] + public bool Primary { get; set; } + + [JsonProperty("current")] + public bool Current { get; set; } + + [JsonProperty("verified")] + public bool Verified { get; set; } + + [JsonProperty("visibility")] + public string Visibility { get; set; } + + [JsonProperty("source")] + public string Source { get; set; } + + [JsonProperty("source-client-id")] + public object SourceClientId { get; set; } + } + + public class ContactDetails + { + + [JsonProperty("email")] + public Email[] Email { get; set; } + + [JsonProperty("address")] + public object Address { get; set; } + } + + public class ExternalIdentifiers + { + + [JsonProperty("external-identifier")] + public object[] ExternalIdentifier { get; set; } + + [JsonProperty("visibility")] + public string Visibility { get; set; } + } + + public class OrcidBio + { + + [JsonProperty("personal-details")] + public PersonalDetails PersonalDetails { get; set; } + + [JsonProperty("biography")] + public Biography Biography { get; set; } + + [JsonProperty("researcher-urls")] + public ResearcherUrls ResearcherUrls { get; set; } + + [JsonProperty("contact-details")] + public ContactDetails ContactDetails { get; set; } + + [JsonProperty("keywords")] + public object Keywords { get; set; } + + [JsonProperty("external-identifiers")] + public ExternalIdentifiers ExternalIdentifiers { get; set; } + + [JsonProperty("delegation")] + public object Delegation { get; set; } + + [JsonProperty("scope")] + public object Scope { get; set; } + } + + public class OrcidProfile + { + + [JsonProperty("orcid")] + public object Orcid { get; set; } + + [JsonProperty("orcid-id")] + public object OrcidId { get; set; } + + [JsonProperty("orcid-identifier")] + public OrcidIdentifier OrcidIdentifier { get; set; } + + [JsonProperty("orcid-deprecated")] + public object OrcidDeprecated { get; set; } + + [JsonProperty("orcid-preferences")] + public OrcidPreferences OrcidPreferences { get; set; } + + [JsonProperty("orcid-history")] + public OrcidHistory OrcidHistory { get; set; } + + [JsonProperty("orcid-bio")] + public OrcidBio OrcidBio { get; set; } + + [JsonProperty("orcid-activities")] + public object OrcidActivities { get; set; } + + [JsonProperty("orcid-internal")] + public object OrcidInternal { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("group-type")] + public object GroupType { get; set; } + + [JsonProperty("client-type")] + public object ClientType { get; set; } + } + + public class OrcidProfileMessage + { + + [JsonProperty("message-version")] + public string MessageVersion { get; set; } + + [JsonProperty("orcid-profile")] + public OrcidProfile OrcidProfile { get; set; } + + [JsonProperty("error-desc")] + public object ErrorDesc { get; set; } + } +} diff --git a/src/Owin.Security.Providers.Orcid/Message/OrcidMessageExtensions.cs b/src/Owin.Security.Providers.Orcid/Message/OrcidMessageExtensions.cs new file mode 100644 index 0000000..467a539 --- /dev/null +++ b/src/Owin.Security.Providers.Orcid/Message/OrcidMessageExtensions.cs @@ -0,0 +1,35 @@ +using Microsoft.Owin; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Owin.Security.Providers.Orcid.Message +{ + public static class OrcidMessageExtensions + { + public static OrcidAuthenticatedContext ToAuthenticationContext(this string json, IOwinContext context, string orcid, string accessToken) + { + var profile = JsonConvert.DeserializeObject(json); + + var user = JObject.Parse(json); + + var authenticatedContext = new OrcidAuthenticatedContext(context, user, accessToken); + + var email = profile.OrcidProfile.OrcidBio?.ContactDetails?.Email?.LastOrDefault(); + if(email != null) + authenticatedContext.Email = email.Value; + + authenticatedContext.Id = orcid; + authenticatedContext.UserName = orcid; + + authenticatedContext.FirstName = profile.OrcidProfile.OrcidBio.PersonalDetails.GivenNames.Value; + authenticatedContext.LastName = profile.OrcidProfile.OrcidBio.PersonalDetails.FamilyName.Value; + + return authenticatedContext; + } + } +} \ No newline at end of file diff --git a/src/Owin.Security.Providers.Orcid/OrcidAuthenticationEndpoints.cs b/src/Owin.Security.Providers.Orcid/OrcidAuthenticationEndpoints.cs new file mode 100644 index 0000000..bf878b9 --- /dev/null +++ b/src/Owin.Security.Providers.Orcid/OrcidAuthenticationEndpoints.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using Microsoft.Owin; +using Microsoft.Owin.Security; + +namespace Owin.Security.Providers.Orcid +{ + public class OrcidAuthenticationEndpoints + { + public static class Default + { + public const string AuthorizationEndPoint = @"https://orcid.org/oauth/authorize"; + public const string TokenEndpoint = @"https://pub.orcid.org/oauth/token"; + public const string ApiEndpoint = @"http://pub.orcid.org/v1.2"; + } + + /// + /// Endpoint which is used to redirect users to request Orcid access + /// + public string AuthorizationEndpoint { get; set; } + + /// + /// Endpoint which is used to exchange code for access token + /// + public string TokenEndpoint { get; set; } + + /// + /// Endpoint which is used to exchange code for access token + /// + public string ApiEndpoint { get; set; } + } +} \ No newline at end of file diff --git a/src/Owin.Security.Providers.Orcid/OrcidAuthenticationExtensions.cs b/src/Owin.Security.Providers.Orcid/OrcidAuthenticationExtensions.cs new file mode 100644 index 0000000..c0ecfed --- /dev/null +++ b/src/Owin.Security.Providers.Orcid/OrcidAuthenticationExtensions.cs @@ -0,0 +1,29 @@ +using System; + +namespace Owin.Security.Providers.Orcid +{ + public static class OrcidAuthenticationExtensions + { + public static IAppBuilder UseOrcidAuthentication(this IAppBuilder app, + OrcidAuthenticationOptions options) + { + if (app == null) + throw new ArgumentNullException(nameof(app)); + if (options == null) + throw new ArgumentNullException(nameof(options)); + + app.Use(typeof(OrcidAuthenticationMiddleware), app, options); + + return app; + } + + public static IAppBuilder UseOrcidAuthentication(this IAppBuilder app, string clientId, string clientSecret) + { + return app.UseOrcidAuthentication(new OrcidAuthenticationOptions + { + ClientId = clientId, + ClientSecret = clientSecret + }); + } + } +} \ No newline at end of file diff --git a/src/Owin.Security.Providers.Orcid/OrcidAuthenticationHandler.cs b/src/Owin.Security.Providers.Orcid/OrcidAuthenticationHandler.cs new file mode 100644 index 0000000..a01e5e3 --- /dev/null +++ b/src/Owin.Security.Providers.Orcid/OrcidAuthenticationHandler.cs @@ -0,0 +1,275 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Security.Claims; +using System.Threading.Tasks; +using System.Web; +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.Orcid.Message; + +namespace Owin.Security.Providers.Orcid +{ + public class OrcidAuthenticationHandler : AuthenticationHandler + { + private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string"; + + private readonly ILogger _logger; + private readonly HttpClient _httpClient; + + public OrcidAuthenticationHandler(HttpClient httpClient, ILogger logger) + { + this._httpClient = httpClient; + this._logger = logger; + } + + protected override async Task AuthenticateCoreAsync() + { + AuthenticationProperties properties = new AuthenticationProperties(); + + 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]; + } + + if (state != null) + { + 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> + { + new KeyValuePair("client_id", Options.ClientId), + new KeyValuePair("client_secret", Options.ClientSecret), + new KeyValuePair("scope", "/read-public"), + new KeyValuePair("grant_type", "authorization_code"), + new KeyValuePair("code", code), + new KeyValuePair("redirect_uri", redirectUri), + }; + + // 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; + string refreshToken = (string)response.refresh_token; + string orcid = (string)response.orcid; + + string profileEndpoint = + string.Format("{0}/{1}/{2}", + Options.Endpoints.ApiEndpoint, + orcid, + "orcid-profile/"); + + // Get Orcid profile + HttpRequestMessage userRequest = new HttpRequestMessage(HttpMethod.Get, profileEndpoint); + + //Requesting public info for now - no authirization needed + //userRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); + + userRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + + HttpResponseMessage userResponse = await _httpClient.SendAsync(userRequest, Request.CallCancelled); + userResponse.EnsureSuccessStatusCode(); + text = await userResponse.Content.ReadAsStringAsync(); + + // Get the Orcid user using the user info endpoint, which is part of the token - response.id + var context = text.ToAuthenticationContext(Context, orcid, accessToken); + + context.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.UserName)) + { + context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.UserName, XmlSchemaString, Options.AuthenticationType)); + } + + if (!string.IsNullOrEmpty(context.Email)) + { + context.Identity.AddClaim(new Claim(ClaimTypes.Email, context.Email, XmlSchemaString, Options.AuthenticationType)); + } + + if (!string.IsNullOrEmpty(context.FirstName)) + { + context.Identity.AddClaim(new Claim(ClaimTypes.GivenName, context.FirstName, XmlSchemaString, Options.AuthenticationType)); + } + + if (!string.IsNullOrEmpty(context.LastName)) + { + context.Identity.AddClaim(new Claim(ClaimTypes.Surname, context.LastName, 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); + + string state = Options.StateDataFormat.Protect(properties); + + string authorizationEndpoint = string.Format( + "{0}?client_id={1}&response_type={2}&scope={3}&redirect_uri={4}&state={5}", + Options.Endpoints.AuthorizationEndpoint, + Options.ClientId, + "code", + @"/authenticate", + HttpUtility.UrlEncode(redirectUri), + state + ); + + //RZ: Need this? + if (Options.Prompt != null) + { + authorizationEndpoint += string.Format("&prompt={0}", Options.Prompt); + } + + 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 OrcidReturnEndpointContext(Context, ticket) + { + SignInAsAuthenticationType = Options.SignInAsAuthenticationType, + 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; + } + } +} diff --git a/src/Owin.Security.Providers.Orcid/OrcidAuthenticationMiddleware.cs b/src/Owin.Security.Providers.Orcid/OrcidAuthenticationMiddleware.cs new file mode 100644 index 0000000..ceff614 --- /dev/null +++ b/src/Owin.Security.Providers.Orcid/OrcidAuthenticationMiddleware.cs @@ -0,0 +1,82 @@ +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; + +namespace Owin.Security.Providers.Orcid +{ + public class OrcidAuthenticationMiddleware : AuthenticationMiddleware + { + private readonly HttpClient _httpClient; + private readonly ILogger _logger; + + public OrcidAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, + OrcidAuthenticationOptions options) + : base(next, options) + { + if (string.IsNullOrWhiteSpace(Options.ClientId)) + throw new ArgumentException("ClientId"); + if (string.IsNullOrWhiteSpace(Options.ClientSecret)) + throw new ArgumentException("ClientSecret"); + + _logger = app.CreateLogger(); + + if (Options.Provider == null) + Options.Provider = new OrcidAuthenticationProvider(); + + if (Options.StateDataFormat == null) + { + IDataProtector dataProtector = app.CreateDataProtector( + typeof(OrcidAuthenticationMiddleware).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, + }; + } + + /// + /// Provides the object for processing + /// authentication-related requests. + /// + /// + /// An configured with the + /// supplied to the constructor. + /// + protected override AuthenticationHandler CreateHandler() + { + return new OrcidAuthenticationHandler(_httpClient, _logger); + } + + private HttpMessageHandler ResolveHttpMessageHandler(OrcidAuthenticationOptions 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("ValidatorHandlerMismatch"); + } + webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate; + } + + return handler; + } + } +} \ No newline at end of file diff --git a/src/Owin.Security.Providers.Orcid/OrcidAuthenticationOptions.cs b/src/Owin.Security.Providers.Orcid/OrcidAuthenticationOptions.cs new file mode 100644 index 0000000..126a7a6 --- /dev/null +++ b/src/Owin.Security.Providers.Orcid/OrcidAuthenticationOptions.cs @@ -0,0 +1,120 @@ +using Microsoft.Owin; +using Microsoft.Owin.Security; +using System; +using System.Collections.Generic; +using System.Net.Http; + +namespace Owin.Security.Providers.Orcid +{ + public class OrcidAuthenticationOptions : AuthenticationOptions + { + /// + /// Gets or sets the a pinned certificate validator to use to validate the endpoints used + /// in back channel communications belong to Orcid. + /// + /// + /// The pinned certificate validator. + /// + /// + /// If this property is null then the default certificate checks are performed, + /// validating the subject name and if the signing chain is a trusted party. + /// + public ICertificateValidator BackchannelCertificateValidator { get; set; } + + /// + /// The HttpMessageHandler used to communicate with Orcid. + /// This cannot be set at the same time as BackchannelCertificateValidator unless the value + /// can be downcast to a WebRequestHandler. + /// + public HttpMessageHandler BackchannelHttpHandler { get; set; } + + /// + /// Gets or sets timeout value in milliseconds for back channel communications with Orcid. + /// + /// + /// The back channel timeout in milliseconds. + /// + public TimeSpan BackchannelTimeout { get; set; } + + /// + /// The request path within the application's base path where the user-agent will be returned. + /// The middleware will process this request when it arrives. + /// Default value is "/signin-orcid". + /// + public PathString CallbackPath { get; set; } + + /// + /// Get or sets the text that the user can display on a sign in user interface. + /// + public string Caption + { + get { return Description.Caption; } + set { Description.Caption = value; } + } + + /// + /// Gets or sets the Orcid supplied Client ID + /// + public string ClientId { get; set; } + + /// + /// Gets or sets the Orcid supplied Client Secret + /// + public string ClientSecret { get; set; } + + /// + /// Gets the sets of OAuth endpoints used to authenticate against Orcid. Overriding these endpoints allows you to use Orcid Enterprise for + /// authentication. + /// + public OrcidAuthenticationEndpoints Endpoints { get; set; } + + /// + /// Gets or sets the used in the authentication events + /// + public IOrcidAuthenticationProvider Provider { get; set; } + + /// + /// A list of permissions to request. + /// + public IList Scope { get; private set; } + + /// + /// Specifies how the authorization server prompts the user for reauthentication and reapproval. This parameter is optional. + /// The only values Orcid supports are: + /// login—The authorization server must prompt the user for reauthentication, forcing the user to log in again. + /// consent—The authorization server must prompt the user for reapproval before returning information to the client. + /// It is valid to pass both values, separated by a space, to require the user to both log in and reauthorize. + /// + public string Prompt { get; set; } + + /// + /// Gets or sets the name of another authentication middleware which will be responsible for actually issuing a user + /// . + /// + public string SignInAsAuthenticationType { get; set; } + + /// + /// Gets or sets the type used to secure data handled by the middleware. + /// + public ISecureDataFormat StateDataFormat { get; set; } + + /// + /// Initializes a new + /// + public OrcidAuthenticationOptions() + : base("Orcid") + { + Caption = Constants.DefaultAuthenticationType; + CallbackPath = new PathString("/signin-orcid"); + AuthenticationMode = AuthenticationMode.Passive; + Scope = new List(); + BackchannelTimeout = TimeSpan.FromSeconds(60); + Endpoints = new OrcidAuthenticationEndpoints + { + AuthorizationEndpoint = OrcidAuthenticationEndpoints.Default.AuthorizationEndPoint, + TokenEndpoint = OrcidAuthenticationEndpoints.Default.TokenEndpoint, + ApiEndpoint = OrcidAuthenticationEndpoints.Default.ApiEndpoint + }; + } + } +} \ No newline at end of file diff --git a/src/Owin.Security.Providers.Orcid/Owin.Security.Providers.Orcid.csproj b/src/Owin.Security.Providers.Orcid/Owin.Security.Providers.Orcid.csproj new file mode 100644 index 0000000..bfcc773 --- /dev/null +++ b/src/Owin.Security.Providers.Orcid/Owin.Security.Providers.Orcid.csproj @@ -0,0 +1,86 @@ + + + + + Debug + AnyCPU + {89CB4342-E23D-4E7C-89E5-C369599A5860} + Library + Properties + Owin.Security.Providers.Orcid + Owin.Security.Providers.Orcid + v4.5.2 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll + True + + + ..\..\packages\Microsoft.Owin.Security.3.0.1\lib\net45\Microsoft.Owin.Security.dll + True + + + ..\..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll + True + + + ..\..\packages\Owin.1.0\lib\net40\Owin.dll + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Owin.Security.Providers.Orcid/Properties/AssemblyInfo.cs b/src/Owin.Security.Providers.Orcid/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8ade133 --- /dev/null +++ b/src/Owin.Security.Providers.Orcid/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Owin.Security.Providers.Orcid")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Owin.Security.Providers.Orcid")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("89cb4342-e23d-4e7c-89e5-c369599a5860")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Owin.Security.Providers.Orcid/Provider/IOrcidAuthenticationProvider.cs b/src/Owin.Security.Providers.Orcid/Provider/IOrcidAuthenticationProvider.cs new file mode 100644 index 0000000..8dd4184 --- /dev/null +++ b/src/Owin.Security.Providers.Orcid/Provider/IOrcidAuthenticationProvider.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; + +namespace Owin.Security.Providers.Orcid +{ + /// + /// Specifies callback methods which the invokes to enable developer control over the authentication process. /> + /// + public interface IOrcidAuthenticationProvider + { + /// + /// Invoked whenever Orcid succesfully authenticates a user + /// + /// Contains information about the login session as well as the user . + /// A representing the completed operation. + Task Authenticated(OrcidAuthenticatedContext context); + + /// + /// Invoked prior to the being saved in a local cookie and the browser being redirected to the originally requested URL. + /// + /// + /// A representing the completed operation. + Task ReturnEndpoint(OrcidReturnEndpointContext context); + } +} \ No newline at end of file diff --git a/src/Owin.Security.Providers.Orcid/Provider/OrcidAuthenticatedContext.cs b/src/Owin.Security.Providers.Orcid/Provider/OrcidAuthenticatedContext.cs new file mode 100644 index 0000000..0aae73a --- /dev/null +++ b/src/Owin.Security.Providers.Orcid/Provider/OrcidAuthenticatedContext.cs @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Security.Claims; +using Microsoft.Owin; +using Microsoft.Owin.Security; +using Microsoft.Owin.Security.Provider; +using Newtonsoft.Json.Linq; +using Owin.Security.Providers.Orcid.Message; + +namespace Owin.Security.Providers.Orcid +{ + /// + /// Contains information about the login session as well as the user . + /// + public class OrcidAuthenticatedContext : BaseContext + { + /// + /// Initializes a + /// + /// The OWIN environment + /// The JSON-serialized user + /// VK Access token + public OrcidAuthenticatedContext(IOwinContext context, JObject user, string accessToken) + : base(context) + { + User = user; + AccessToken = accessToken; + } + + /// + /// Gets the JSON-serialized user + /// + /// + /// Contains the user obtained from the User Info endpoint. By default this ishttp://pub.orcid.org/v1.2/{orcid}/orcid-profile/ but it can be + /// overridden in the options + /// + public JObject User { get; internal set; } + + /// + /// Gets the VK access token + /// + public string AccessToken { get; internal set; } + + /// + /// Gets the user ID + /// + public string Id { get; internal set; } + + /// + /// Gets the user's name + /// + public string UserName { get; internal set; } + + /// + /// Gets the user's last name + /// + public string LastName { get; internal set; } + /// + /// Gets the user's first name + /// + public string FirstName { get; internal set; } + + /// + /// Gets the user's Email + /// + public string Email { get; internal set; } + + /// + /// Gets the representing the user + /// + public ClaimsIdentity Identity { get; set; } + + /// + /// Gets or sets a property bag for common authentication properties + /// + public AuthenticationProperties Properties { get; set; } + + private static string TryGetValue(JObject user, string propertyName) + { + JToken value; + return user.TryGetValue(propertyName, out value) ? value.ToString() : null; + } + } +} diff --git a/src/Owin.Security.Providers.Orcid/Provider/OrcidAuthenticationProvider.cs b/src/Owin.Security.Providers.Orcid/Provider/OrcidAuthenticationProvider.cs new file mode 100644 index 0000000..d5e2c32 --- /dev/null +++ b/src/Owin.Security.Providers.Orcid/Provider/OrcidAuthenticationProvider.cs @@ -0,0 +1,50 @@ +using System; +using System.Threading.Tasks; + +namespace Owin.Security.Providers.Orcid +{ + /// + /// Default implementation. + /// + public class OrcidAuthenticationProvider : IOrcidAuthenticationProvider + { + /// + /// Initializes a + /// + public OrcidAuthenticationProvider() + { + OnAuthenticated = context => Task.FromResult(null); + OnReturnEndpoint = context => Task.FromResult(null); + } + + /// + /// Gets or sets the function that is invoked when the Authenticated method is invoked. + /// + public Func OnAuthenticated { get; set; } + + /// + /// Gets or sets the function that is invoked when the ReturnEndpoint method is invoked. + /// + public Func OnReturnEndpoint { get; set; } + + /// + /// Invoked whenever Orcid succesfully authenticates a user + /// + /// Contains information about the login session as well as the user . + /// A representing the completed operation. + public virtual Task Authenticated(OrcidAuthenticatedContext context) + { + return OnAuthenticated(context); + } + + /// + /// Invoked prior to the being saved in a local cookie and the browser being redirected to the originally requested URL. + /// + /// + /// A representing the completed operation. + public virtual Task ReturnEndpoint(OrcidReturnEndpointContext context) + { + return OnReturnEndpoint(context); + } + } +} \ No newline at end of file diff --git a/src/Owin.Security.Providers.Orcid/Provider/OrcidReturnEndpointContext.cs b/src/Owin.Security.Providers.Orcid/Provider/OrcidReturnEndpointContext.cs new file mode 100644 index 0000000..8cc6d7e --- /dev/null +++ b/src/Owin.Security.Providers.Orcid/Provider/OrcidReturnEndpointContext.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.Owin; +using Microsoft.Owin.Security; +using Microsoft.Owin.Security.Provider; + +namespace Owin.Security.Providers.Orcid +{ + /// + /// Provides context information to middleware providers. + /// + public class OrcidReturnEndpointContext : ReturnEndpointContext + { + /// + /// + /// + /// OWIN environment + /// The authentication ticket + public OrcidReturnEndpointContext( + IOwinContext context, + AuthenticationTicket ticket) + : base(context, ticket) + { + } + } +} diff --git a/src/Owin.Security.Providers.Orcid/packages.config b/src/Owin.Security.Providers.Orcid/packages.config new file mode 100644 index 0000000..a35e97b --- /dev/null +++ b/src/Owin.Security.Providers.Orcid/packages.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file