Compare commits

..

20 Commits

Author SHA1 Message Date
Tommy Parnell
e88f958804 bump version 2016-05-18 08:29:21 -04:00
Andrew Rogers
cb62315235 Discord authentication. (#161)
* Discord authentication.

* Remove compiler directive, fix indents.
2016-05-18 07:20:58 -04:00
Tommy Parnell
bfe06e94a0 fixed version in global nuspec 2016-05-10 19:50:41 -04:00
Tommy Parnell
b001c079fb bump version 2016-05-10 19:41:36 -04:00
Eric Green
e919934222 LinkedIn Provider - Added headline, summary, positions and industry (#158) 2016-05-10 19:36:12 -04:00
Tommy Parnell
d47a6033f8 minor readme edit 2016-05-04 23:41:57 -04:00
Tommy Parnell
606c386bf0 try to get the orcid pr to show as merged 2016-05-04 18:48:50 -04:00
Tommy Parnell
02bb482859 Merge branch 'RickZee-master' 2016-05-04 18:44:27 -04:00
Tommy Parnell
d92c907e35 merge both things together 2016-05-04 18:44:18 -04:00
Tommy Parnell
7f4684d4ec add readme changes 2016-05-04 18:40:32 -04:00
Tommy Parnell
38e46d4f83 release 2.0.0 2016-05-04 18:31:51 -04:00
Tommy Parnell
63a04c3208 Merge branch 'RickZee-master' 2016-05-04 18:30:31 -04:00
Tommy Parnell
1e63b8335b fix up orcid a little 2016-05-04 18:30:03 -04:00
Rick Zakharov
939311e192 Added ORCID authentication provider 2016-05-04 18:30:03 -04:00
Rick Zakharov
9229ea0ff9 Added Owin.Security.Providers.Orcid project 2016-05-04 18:30:03 -04:00
Rick Zakharov
9938255749 Added ORCID authentication provider 2016-04-25 08:08:15 -04:00
Tommy Parnell
bac08419ef fix readme 2016-04-23 00:12:39 -04:00
Tommy Parnell
c6164230ab use ruby 22 2016-04-23 00:06:59 -04:00
Tommy Parnell
a281ef0e62 remove rake from gemfile 2016-04-22 23:59:32 -04:00
Rick Zakharov
5cf1410e47 Added Owin.Security.Providers.Orcid project 2016-04-22 20:26:37 -04:00
44 changed files with 2538 additions and 274 deletions

5
.gitignore vendored
View File

@@ -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

View File

@@ -1,6 +1,4 @@
source 'http://rubygems.org'
gem 'rake'
gem 'os'
gem 'albacore'
gem 'nokogiri'

View File

@@ -21,7 +21,6 @@ DEPENDENCIES
albacore
nokogiri
os
rake
BUNDLED WITH
1.10.6
1.12.1

View File

@@ -1,9 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.24720.0
VisualStudioVersion = 14.0.25123.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,18 @@ 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
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers.Discord", "src\Owin.Security.Providers.Discord\Owin.Security.Providers.Discord.csproj", "{4BE728EB-778A-41AF-8DEA-0C7159711D44}"
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 +278,18 @@ 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
{4BE728EB-778A-41AF-8DEA-0C7159711D44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4BE728EB-778A-41AF-8DEA-0C7159711D44}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4BE728EB-778A-41AF-8DEA-0C7159711D44}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4BE728EB-778A-41AF-8DEA-0C7159711D44}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -3,278 +3,282 @@ using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Owin;
//using Owin.Security.Providers.Orcid;
//using Owin.Security.Providers.Discord;
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.UseVKontakteAuthentication("", "");
//app.UseFitbitAuthentication(new FitbitAuthenticationOptions
//{
// ClientId = "",
// ClientSecret = ""
//});
//app.UseXingAuthentication("", "");
//app.UseOnshapeAuthentication(
// appKey: "",
// appSecret: "");
//
//
//app.UseOnshapeAuthentication(new OnshapeAuthenticationOptions()
//{
// AppKey = "",
// AppSecret = "",
// CallbackPath = new PathString("/oauthRedirect"),
// Hostname = "partner.dev.onshape.com"
//});
//app.UseDoYouBuzzAuthentication("", "");
//app.("", "");
//app.UseOrcidAuthentication("","");
//app.UseVKontakteAuthentication("", "");
//app.UseXingAuthentication("", "");
//app.UseDoYouBuzzAuthentication("", "");
}
}
//app.UseDiscordAuthentication("", "");
}
}
}

View File

@@ -13,7 +13,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>OwinOAuthProvidersDemo</RootNamespace>
<AssemblyName>OwinOAuthProvidersDemo</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<MvcBuildViews>false</MvcBuildViews>
<UseIISExpress>true</UseIISExpress>
<IISExpressSSLPort>44300</IISExpressSSLPort>
@@ -21,6 +21,7 @@
<IISExpressWindowsAuthentication>disabled</IISExpressWindowsAuthentication>
<IISExpressUseClassicPipelineMode>false</IISExpressUseClassicPipelineMode>
<UseGlobalApplicationHostFile />
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -65,16 +66,16 @@
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Owin, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Owin.2.1.0\lib\net45\Microsoft.Owin.dll</HintPath>
<Reference Include="Microsoft.Owin, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Owin.Host.SystemWeb, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Owin.Host.SystemWeb.2.1.0\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Owin.Security, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Owin.Security.2.1.0\lib\net45\Microsoft.Owin.Security.dll</HintPath>
<Reference Include="Microsoft.Owin.Security, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Owin.Security.3.0.1\lib\net45\Microsoft.Owin.Security.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Owin.Security.Cookies, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
@@ -105,8 +106,8 @@
<HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
@@ -115,13 +116,13 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Drawing" />
<Reference Include="System.Web.DynamicData" />
<Reference Include="System.Web.Entity" />
<Reference Include="System.Web.ApplicationServices" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Web.Helpers, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.0\lib\net45\System.Web.Helpers.dll</HintPath>
<Private>True</Private>
@@ -150,9 +151,7 @@
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.0\lib\net45\System.Web.WebPages.Razor.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Web" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Web.Abstractions" />
<Reference Include="System.Web.Routing" />
<Reference Include="System.Xml" />
@@ -163,6 +162,7 @@
</Reference>
<Reference Include="System.Net.Http.WebRequest">
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="WebGrease, Version=1.6.5135.21930, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\WebGrease.1.6.0\lib\WebGrease.dll</HintPath>
<Private>True</Private>
@@ -248,6 +248,12 @@
<ItemGroup>
<Content Include="Scripts\jquery-2.1.1.min.map" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\src\Owin.Security.Providers.Discord\Owin.Security.Providers.Discord.csproj">
<Project>{4be728eb-778a-41af-8dea-0c7159711d44}</Project>
<Name>Owin.Security.Providers.Discord</Name>
</ProjectReference>
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

View File

@@ -1,16 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=301880
-->
<configuration>
<configSections>
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
</configSections>
<connectionStrings>
<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\aspnet-OwinOAuthProvidersDemo-2015.mdf;Initial Catalog=aspnet-OwinOAuthProvidersDemo-20131113093838;Integrated Security=True" providerName="System.Data.SqlClient" />
<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\aspnet-owinOauthDemo.mdf;Initial Catalog=aspnet-owinOauthDemo;Integrated Security=True" providerName="System.Data.SqlClient" />
</connectionStrings>
<appSettings>
<add key="webpages:Version" value="3.0.0.0" />
@@ -18,9 +17,17 @@
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>
<!--
For a description of web.config changes see http://go.microsoft.com/fwlink/?LinkId=235367.
The following attributes can be set on the <httpRuntime> tag.
<system.Web>
<httpRuntime targetFramework="4.5.2" />
</system.Web>
-->
<system.web>
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5" />
<compilation debug="true" targetFramework="4.5.2" />
<httpRuntime targetFramework="4.5" />
</system.web>
<system.webServer>
@@ -40,11 +47,11 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.1.0.0" newVersion="2.1.0.0" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.1.0.0" newVersion="2.1.0.0" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
@@ -60,7 +67,7 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />

View File

@@ -13,9 +13,9 @@
<package id="Microsoft.AspNet.Web.Optimization" version="1.1.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebPages" version="3.2.0" targetFramework="net45" />
<package id="Microsoft.jQuery.Unobtrusive.Validation" version="3.2.0" targetFramework="net45" />
<package id="Microsoft.Owin" version="2.1.0" targetFramework="net45" />
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net45" />
<package id="Microsoft.Owin.Host.SystemWeb" version="2.1.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security" version="2.1.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security" version="3.0.1" targetFramework="net45" />
<package id="Microsoft.Owin.Security.Cookies" version="2.1.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security.Facebook" version="2.1.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security.Google" version="2.1.0" targetFramework="net45" />
@@ -24,7 +24,7 @@
<package id="Microsoft.Owin.Security.Twitter" version="2.1.0" targetFramework="net45" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
<package id="Modernizr" version="2.7.2" targetFramework="net45" />
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net45" />
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net45" />
<package id="Owin" version="1.0" targetFramework="net45" />
<package id="Respond" version="1.4.2" targetFramework="net45" />
<package id="WebGrease" version="1.6.0" targetFramework="net45" />

View File

@@ -1,5 +1,4 @@
[![Build status](https://ci.appveyor.com/api/projects/status/su8q95onnarswjaq/branch/master?svg=true)](https://ci.appveyor.com/project/ByteBlast/owinoauthproviders/branch/master)
[![Build status](https://ci.appveyor.com/api/projects/status/gjlkpp86t8dw164f?svg=true)](https://ci.appveyor.com/project/tparnell8/owinoauthproviders)
#OWIN OAuth Providers
@@ -25,6 +24,7 @@ Provides a set of extra authentication providers for OWIN ([Project Katana](http
- Instagram
- LinkedIn
- Onshape
- ORCID
- PayPal
- Reddit
- Salesforce
@@ -52,17 +52,8 @@ Provides a set of extra authentication providers for OWIN ([Project Katana](http
For above listed provider implementation guide, visit Jerrie Pelser's blog - [Be a Big Rockstar](http://www.beabigrockstar.com)
## Installation
To use these providers you will need to install the ```Owin.Security.Providers``` NuGet package.
```
PM> Install-Package Owin.Security.Providers.*
```
Where * is the name of the provider you need e.g.:
```
PM> Install-Package Owin.Security.Providers.GitHub
```
I haven't published all of the providers yet as of 4/12/2016.
You may install each provider via nuget separately. They are named Owin.Security.Providers.*, or you may install the meta package `Owin.Security.Providers` which will give you all 40+ providers.
## Contributions

View File

@@ -15,7 +15,7 @@ PACKAGES = File.expand_path("packages")
TOOLS = File.expand_path("tools")
NUGET = File.expand_path("#{TOOLS}/nuget")
NUGET_EXE = File.expand_path("#{TOOLS}/nuget/nuget.exe")
@version = "2.0.0-rc1"
@version = "2.2.0"
PROJECTS = Dir.glob('src/*').select{|dir| File.directory? dir }
desc 'Retrieve things'

View File

@@ -1,10 +1,10 @@
version: 1.0.{build}
nuget:
project_feed: true
install:
- set PATH=C:\Ruby22\bin;%PATH%
- bundle install
build_script:
- ps: >-
gem install bundle
bundle
rake preflight
- rake preflight
artifacts:
- path: src\**\*.nupkg

View File

@@ -23,7 +23,7 @@
<tags>owin katana oauth LinkedIn Yahoo Google+ GitHub Reddit Instagram StackExchange SalesForce TripIt Buffer ArcGIS Dropbox Wordpress Battle.NET Yammer OpenID Steam Twitch</tags>
<dependencies>
<% for @item in @nugets %>
<dependency id="<%= @item %>" version="<%= @version %>" />
<dependency id="<%= @item %>" version="[<%= @version %>]" />
<% end %>
</dependencies>
</metadata>

View File

@@ -0,0 +1,7 @@
namespace Owin.Security.Providers.Discord
{
internal static class Constants
{
public const string DefaultAuthenticationType = "Discord";
}
}

View File

@@ -0,0 +1,29 @@
using System;
namespace Owin.Security.Providers.Discord
{
public static class DiscordAuthenticationExtensions
{
public static IAppBuilder UseDiscordAuthentication(this IAppBuilder app,
DiscordAuthenticationOptions options)
{
if (app == null)
throw new ArgumentNullException(nameof(app));
if (options == null)
throw new ArgumentNullException(nameof(options));
app.Use(typeof(DiscordAuthenticationMiddleware), app, options);
return app;
}
public static IAppBuilder UseDiscordAuthentication(this IAppBuilder app, string clientId, string clientSecret)
{
return app.UseDiscordAuthentication(new DiscordAuthenticationOptions
{
ClientId = clientId,
ClientSecret = clientSecret
});
}
}
}

View File

@@ -0,0 +1,248 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Security.Claims;
using System.Threading.Tasks;
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.Discord.Provider;
namespace Owin.Security.Providers.Discord
{
public class DiscordAuthenticationHandler : AuthenticationHandler<DiscordAuthenticationOptions>
{
private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
private const string TokenEndpoint = "https://discordapp.com/api/oauth2/token";
private const string UserInfoEndpoint = "https://discordapp.com/api/users/@me";
private readonly ILogger _logger;
private readonly HttpClient _httpClient;
public DiscordAuthenticationHandler(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>("grant_type", "authorization_code"),
new KeyValuePair<string, string>("code", code),
new KeyValuePair<string, string>("redirect_uri", redirectUri),
new KeyValuePair<string, string>("client_id", Options.ClientId),
new KeyValuePair<string, string>("client_secret", Options.ClientSecret)
};
var request = new HttpRequestMessage(HttpMethod.Post, TokenEndpoint);
request.Content = new FormUrlEncodedContent(body);
// Request the token
var tokenResponse =
await _httpClient.SendAsync(request);
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 expires = (string)response.expires_in;
var refreshToken = (string)response.refresh_token;
// Get the Discord user
var userRequest = new HttpRequestMessage(HttpMethod.Get, UserInfoEndpoint);
userRequest.Headers.Add("Authorization", "Bearer " + Uri.EscapeDataString(accessToken) + "");
var graphResponse = await _httpClient.SendAsync(userRequest, Request.CallCancelled);
graphResponse.EnsureSuccessStatusCode();
text = await graphResponse.Content.ReadAsStringAsync();
var user = JObject.Parse(text);
var context = new DiscordAuthenticatedContext(Context, user, accessToken, expires, refreshToken)
{
Identity = new ClaimsIdentity(
Options.AuthenticationType,
ClaimsIdentity.DefaultNameClaimType,
ClaimsIdentity.DefaultRoleClaimType)
};
if (!string.IsNullOrEmpty(context.Id))
{
context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.UserName, 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.Avatar))
{
context.Identity.AddClaim(new Claim("urn:discord:avatar", context.Avatar, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(context.Discriminator))
{
context.Identity.AddClaim(new Claim("urn:discord:discriminator", context.Discriminator, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(context.AccessToken))
{
context.Identity.AddClaim(new Claim("urn:discord:accesstoken", context.AccessToken, XmlSchemaString, Options.AuthenticationType));
}
context.Identity.AddClaim(new Claim("urn:discord:verified", context.Verified.ToString(), 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);
// comma separated
var scope = string.Join(" ", Options.Scope);
var state = Options.StateDataFormat.Protect(properties);
var authorizationEndpoint =
"https://discordapp.com/api/oauth2/authorize" +
"?response_type=code" +
"&client_id=" + Uri.EscapeDataString(Options.ClientId) +
"&redirect_uri=" + Uri.EscapeDataString(redirectUri) +
"&scope=" + Uri.EscapeDataString(scope) +
"&state=" + Uri.EscapeDataString(state) +
"&duration=permanent";
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 DiscordReturnEndpointContext(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;
}
}
}

View File

@@ -0,0 +1,75 @@
using System;
using System.Globalization;
using System.Net;
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.Discord.Provider;
namespace Owin.Security.Providers.Discord
{
public class DiscordAuthenticationMiddleware : AuthenticationMiddleware<DiscordAuthenticationOptions>
{
private readonly HttpClient _httpClient;
private readonly ILogger _logger;
public DiscordAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app,
DiscordAuthenticationOptions options)
: base(next, options)
{
if (string.IsNullOrWhiteSpace(Options.ClientId))
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
"Option must be provided {0}", "ClientId"));
if (string.IsNullOrWhiteSpace(Options.ClientSecret))
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
"Option must be provided {0}", "ClientSecret"));
_logger = app.CreateLogger<DiscordAuthenticationMiddleware>();
if (Options.Provider == null)
Options.Provider = new DiscordAuthenticationProvider();
if (Options.StateDataFormat == null)
{
var dataProtector = app.CreateDataProtector(
typeof(DiscordAuthenticationMiddleware).FullName,
Options.AuthenticationType, "v1");
Options.StateDataFormat = new PropertiesDataFormat(dataProtector);
}
if (string.IsNullOrEmpty(Options.SignInAsAuthenticationType))
Options.SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType();
_httpClient = new HttpClient(ResolveHttpMessageHandler())
{
Timeout = Options.BackchannelTimeout,
MaxResponseContentBufferSize = 1024 * 1024 * 10
};
}
/// <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.Discord.DiscordAuthenticationOptions" /> supplied to the constructor.
/// </returns>
protected override AuthenticationHandler<DiscordAuthenticationOptions> CreateHandler()
{
return new DiscordAuthenticationHandler(_httpClient, _logger);
}
private HttpClientHandler ResolveHttpMessageHandler()
{
return new HttpClientHandler
{
Credentials = new NetworkCredential(Options.ClientId, Options.ClientSecret)
};
}
}
}

View File

@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Owin.Security.Providers.Discord.Provider;
namespace Owin.Security.Providers.Discord
{
public class DiscordAuthenticationOptions : AuthenticationOptions
{
/// <summary>
/// Gets or sets the a pinned certificate validator to use to validate the endpoints used
/// in back channel communications belong to Discord.
/// </summary>
/// <value>
/// The pinned certificate validator.
/// </value>
/// <remarks>
/// 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.
/// </remarks>
public ICertificateValidator BackchannelCertificateValidator { get; set; }
/// <summary>
/// The HttpMessageHandler used to communicate with Discord.
/// This cannot be set at the same time as BackchannelCertificateValidator unless the value
/// can be downcast to a WebRequestHandler.
/// </summary>
public HttpMessageHandler BackchannelHttpHandler { get; set; }
/// <summary>
/// Gets or sets timeout value in milliseconds for back channel communications with Discord.
/// </summary>
/// <value>
/// The back channel timeout in milliseconds.
/// </value>
public TimeSpan BackchannelTimeout { get; set; }
/// <summary>
/// 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-Discord".
/// </summary>
public PathString CallbackPath { get; set; }
/// <summary>
/// Get or sets the text that the user can display on a sign in user interface.
/// </summary>
public string Caption
{
get { return Description.Caption; }
set { Description.Caption = value; }
}
/// <summary>
/// Gets or sets the Discord supplied Client ID
/// </summary>
public string ClientId { get; set; }
/// <summary>
/// Gets or sets the Discord supplied Client Secret
/// </summary>
public string ClientSecret { get; set; }
/// <summary>
/// Gets or sets the <see cref="IDiscordAuthenticationProvider" /> used in the authentication events
/// </summary>
public IDiscordAuthenticationProvider Provider { get; set; }
/// <summary>
/// A list of permissions to request.
/// </summary>
public IList<string> Scope { get; private set; }
/// <summary>
/// Gets or sets the name of another authentication middleware which will be responsible for actually issuing a user
/// <see cref="System.Security.Claims.ClaimsIdentity" />.
/// </summary>
public string SignInAsAuthenticationType { get; set; }
/// <summary>
/// Gets or sets the type used to secure data handled by the middleware.
/// </summary>
public ISecureDataFormat<AuthenticationProperties> StateDataFormat { get; set; }
/// <summary>
/// Initializes a new <see cref="DiscordAuthenticationOptions" />
/// </summary>
public DiscordAuthenticationOptions()
: base("Discord")
{
Caption = Constants.DefaultAuthenticationType;
CallbackPath = new PathString("/signin-discord");
AuthenticationMode = AuthenticationMode.Passive;
Scope = new List<string>
{
"identify",
"email"
};
BackchannelTimeout = TimeSpan.FromSeconds(60);
}
}
}

View File

@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{4BE728EB-778A-41AF-8DEA-0C7159711D44}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Owin.Security.Providers.Discord</RootNamespace>
<AssemblyName>Owin.Security.Providers.Discord</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Owin, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Owin.Security, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Owin.Security.3.0.1\lib\net45\Microsoft.Owin.Security.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
<HintPath>..\..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Constants.cs" />
<Compile Include="DiscordAuthenticationExtensions.cs" />
<Compile Include="DiscordAuthenticationHandler.cs" />
<Compile Include="DiscordAuthenticationMiddleware.cs" />
<Compile Include="DiscordAuthenticationOptions.cs" />
<Compile Include="Provider\IDiscordAuthenticationProvider.cs" />
<Compile Include="Provider\DiscordAuthenticatedContext.cs" />
<Compile Include="Provider\DiscordAuthenticationProvider.cs" />
<Compile Include="Provider\DiscordReturnEndpointContext.cs" />
<Compile Include="Resources.Designer.cs">
<DependentUpon>Resources.resx</DependentUpon>
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="PostBuildMacros">
<GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
<Output TaskParameter="Assemblies" ItemName="Targets" />
</GetAssemblyIdentity>
<ItemGroup>
<VersionNumber Include="@(Targets->'%(Version)')" />
</ItemGroup>
</Target>
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>
<PostBuildEventDependsOn>
$(PostBuildEventDependsOn);
PostBuildMacros;
</PostBuildEventDependsOn>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,15 @@
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Owin.Security.Providers.Discord")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Owin.Security.Providers.Discord")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("5d0f8dfb-2057-48a2-8eef-8f09645e8b4e")]
[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("2.0.0.0")]

View File

@@ -0,0 +1,111 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Globalization;
using System.Security.Claims;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Provider;
using Newtonsoft.Json.Linq;
namespace Owin.Security.Providers.Discord.Provider
{
/// <summary>
/// Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.
/// </summary>
public class DiscordAuthenticatedContext : BaseContext
{
/// <summary>
/// Initializes a <see cref="DiscordAuthenticatedContext"/>
/// </summary>
/// <param name="context">The OWIN environment</param>
/// <param name="user">The JSON-serialized user</param>
/// <param name="accessToken">Discord Access token</param>
/// <param name="expires">Seconds until expiration</param>
/// <param name="refreshToken"></param>
public DiscordAuthenticatedContext(IOwinContext context, JObject user, string accessToken, string expires, string refreshToken)
: base(context)
{
User = user;
AccessToken = accessToken;
RefreshToken = refreshToken;
int expiresValue;
if (int.TryParse(expires, NumberStyles.Integer, CultureInfo.InvariantCulture, out expiresValue))
{
ExpiresIn = TimeSpan.FromSeconds(expiresValue);
}
Id = TryGetValue(user, "id");
UserName = TryGetValue(user, "username");
Discriminator = TryGetValue(user, "discriminator");
Avatar = TryGetValue(user, "avatar");
Email = TryGetValue(user, "email");
Verified = TryGetValue(user, "verified") == "true";
}
public string RefreshToken { get; set; }
/// <summary>
/// Gets the JSON-serialized user
/// </summary>
/// <remarks>
/// Contains the Discord user
/// </remarks>
public JObject User { get; private set; }
/// <summary>
/// Gets the Discord access token
/// </summary>
public string AccessToken { get; private set; }
/// <summary>
/// Gets the Discord access token expiration time
/// </summary>
public TimeSpan? ExpiresIn { get; set; }
/// <summary>
/// Gets the Discord user ID
/// </summary>
public string Id { get; private set; }
/// <summary>
/// Gets the Discord user discriminator
/// </summary>
public string Discriminator { get; private set; }
/// <summary>
/// Gets the Discord user avatar
/// </summary>
public string Avatar { get; private set; }
/// <summary>
/// Gets the Discord user email
/// </summary>
public string Email { get; private set; }
/// <summary>
/// Gets the Discord username
/// </summary>
public string UserName { get; private set; }
/// <summary>
/// Gets whether the user is verified or not.
/// </summary>
public bool Verified { get; private set; } = false;
/// <summary>
/// Gets the <see cref="ClaimsIdentity"/> representing the user
/// </summary>
public ClaimsIdentity Identity { get; set; }
/// <summary>
/// Gets or sets a property bag for common authentication properties
/// </summary>
public AuthenticationProperties Properties { get; set; }
private static string TryGetValue(JObject user, string propertyName)
{
JToken value;
return user.TryGetValue(propertyName, out value) ? value.ToString() : null;
}
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.Threading.Tasks;
namespace Owin.Security.Providers.Discord.Provider
{
/// <summary>
/// Default <see cref="IDiscordAuthenticationProvider"/> implementation.
/// </summary>
public class DiscordAuthenticationProvider : IDiscordAuthenticationProvider
{
/// <summary>
/// Initializes a <see cref="DiscordAuthenticationProvider"/>
/// </summary>
public DiscordAuthenticationProvider()
{
OnAuthenticated = context => Task.FromResult<object>(null);
OnReturnEndpoint = context => Task.FromResult<object>(null);
}
/// <summary>
/// Gets or sets the function that is invoked when the Authenticated method is invoked.
/// </summary>
public Func<DiscordAuthenticatedContext, Task> OnAuthenticated { get; set; }
/// <summary>
/// Gets or sets the function that is invoked when the ReturnEndpoint method is invoked.
/// </summary>
public Func<DiscordReturnEndpointContext, Task> OnReturnEndpoint { get; set; }
/// <summary>
/// Invoked whenever Discord successfully authenticates a user
/// </summary>
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
public virtual Task Authenticated(DiscordAuthenticatedContext context)
{
return OnAuthenticated(context);
}
/// <summary>
/// Invoked prior to the <see cref="System.Security.Claims.ClaimsIdentity"/> being saved in a local cookie and the browser being redirected to the originally requested URL.
/// </summary>
/// <param name="context"></param>
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
public virtual Task ReturnEndpoint(DiscordReturnEndpointContext context)
{
return OnReturnEndpoint(context);
}
}
}

View File

@@ -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.Discord.Provider
{
/// <summary>
/// Provides context information to middleware providers.
/// </summary>
public class DiscordReturnEndpointContext : ReturnEndpointContext
{
/// <summary>
///
/// </summary>
/// <param name="context">OWIN environment</param>
/// <param name="ticket">The authentication ticket</param>
public DiscordReturnEndpointContext(
IOwinContext context,
AuthenticationTicket ticket)
: base(context, ticket)
{
}
}
}

View File

@@ -0,0 +1,24 @@
using System.Threading.Tasks;
namespace Owin.Security.Providers.Discord.Provider
{
/// <summary>
/// Specifies callback methods which the <see cref="DiscordAuthenticationMiddleware"></see> invokes to enable developer control over the authentication process. />
/// </summary>
public interface IDiscordAuthenticationProvider
{
/// <summary>
/// Invoked whenever Discord successfully authenticates a user
/// </summary>
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
Task Authenticated(DiscordAuthenticatedContext context);
/// <summary>
/// Invoked prior to the <see cref="System.Security.Claims.ClaimsIdentity"/> being saved in a local cookie and the browser being redirected to the originally requested URL.
/// </summary>
/// <param name="context"></param>
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
Task ReturnEndpoint(DiscordReturnEndpointContext context);
}
}

View File

@@ -0,0 +1,81 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Owin.Security.Providers.Discord {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
var temp = new global::System.Resources.ResourceManager("Owin.Security.Providers.Discord.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to The &apos;{0}&apos; option must be provided..
/// </summary>
internal static string Exception_OptionMustBeProvided {
get {
return ResourceManager.GetString("Exception_OptionMustBeProvided", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler..
/// </summary>
internal static string Exception_ValidatorHandlerMismatch {
get {
return ResourceManager.GetString("Exception_ValidatorHandlerMismatch", resourceCulture);
}
}
}
}

View File

@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Exception_OptionMustBeProvided" xml:space="preserve">
<value>The '{0}' option must be provided.</value>
</data>
<data name="Exception_ValidatorHandlerMismatch" xml:space="preserve">
<value>An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler.</value>
</data>
</root>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net452" />
<package id="Microsoft.Owin.Security" version="3.0.1" targetFramework="net452" />
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net452" />
<package id="Owin" version="1.0" targetFramework="net452" />
</packages>

View File

@@ -123,6 +123,22 @@ namespace Owin.Security.Providers.LinkedIn
context.Identity.AddClaim(new Claim(ClaimTypes.Name, context.Name, XmlSchemaString, Options.AuthenticationType));
context.Identity.AddClaim(new Claim("urn:linkedin:name", context.Name, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(context.Industry))
{
context.Identity.AddClaim(new Claim("urn:linkedin:industry", context.Industry, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(context.Positions))
{
context.Identity.AddClaim(new Claim("urn:linkedin:positions", context.Positions, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(context.Summary))
{
context.Identity.AddClaim(new Claim("urn:linkedin:summary", context.Summary, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(context.Headline))
{
context.Identity.AddClaim(new Claim("urn:linkedin:headline", context.Headline, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(context.Link))
{
context.Identity.AddClaim(new Claim("urn:linkedin:url", context.Link, XmlSchemaString, Options.AuthenticationType));

View File

@@ -119,7 +119,11 @@ namespace Owin.Security.Providers.LinkedIn
"formatted-name",
"email-address",
"public-profile-url",
"picture-url"
"picture-url",
"industry",
"headline",
"summary",
"positions"
};
BackchannelTimeout = TimeSpan.FromSeconds(60);
}

View File

@@ -7,6 +7,7 @@ using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Provider;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
namespace Owin.Security.Providers.LinkedIn
{
@@ -41,6 +42,10 @@ namespace Owin.Security.Providers.LinkedIn
Link = TryGetValue(user, "publicProfileUrl");
UserName = TryGetValue(user, "formattedName").Replace(" ", "");
Email = TryGetValue(user, "emailAddress");
Industry = TryGetValue(user, "industry");
Summary = TryGetValue(user, "summary");
Headline = TryGetValue(user, "headline");
Positions = TryGetValueAndSerialize(user, "positions");
}
/// <summary>
@@ -87,6 +92,28 @@ namespace Owin.Security.Providers.LinkedIn
/// </summary>
public string FamilyName { get; private set; }
/// <summary>
/// Describes the users membership profile
/// </summary>
public string Summary { get; private set; }
/// <summary>
/// Industry the member belongs to
/// https://developer.linkedin.com/docs/reference/industry-codes
/// </summary>
public string Industry { get; set; }
/// <summary>
/// The members headline
/// </summary>
public string Headline { get; set; }
/// <summary>
/// Member's current positions
/// https://developer.linkedin.com/docs/fields/positions
/// </summary>
public string Positions { get; set; }
public string Link { get; private set; }
/// <summary>
@@ -109,5 +136,11 @@ namespace Owin.Security.Providers.LinkedIn
JToken value;
return user.TryGetValue(propertyName, out value) ? value.ToString() : null;
}
private static string TryGetValueAndSerialize(JObject user, string propertyName)
{
JToken value;
return user.TryGetValue(propertyName, out value) ? JsonConvert.SerializeObject(value) : null;
}
}
}

View File

@@ -0,0 +1,7 @@
namespace Owin.Security.Providers.Orcid
{
internal static class Constants
{
public const string DefaultAuthenticationType = "Orcid";
}
}

View File

@@ -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; }
}
}

View File

@@ -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<OrcidProfileMessage>(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;
}
}
}

View File

@@ -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";
}
/// <summary>
/// Endpoint which is used to redirect users to request Orcid access
/// </summary>
public string AuthorizationEndpoint { get; set; }
/// <summary>
/// Endpoint which is used to exchange code for access token
/// </summary>
public string TokenEndpoint { get; set; }
/// <summary>
/// Endpoint which is used to exchange code for access token
/// </summary>
public string ApiEndpoint { get; set; }
}
}

View File

@@ -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
});
}
}
}

View File

@@ -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<OrcidAuthenticationOptions>
{
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<AuthenticationTicket> AuthenticateCoreAsync()
{
AuthenticationProperties properties = new AuthenticationProperties();
try
{
string code = null;
string state = null;
IReadableStringCollection query = Request.Query;
IList<string> 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<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", Options.ClientId),
new KeyValuePair<string, string>("client_secret", Options.ClientSecret),
new KeyValuePair<string, string>("scope", "/read-public"),
new KeyValuePair<string, string>("grant_type", "authorization_code"),
new KeyValuePair<string, string>("code", code),
new KeyValuePair<string, string>("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<dynamic>(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<object>(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<object>(null);
}
public override async Task<bool> InvokeAsync()
{
return await InvokeReplyPathAsync();
}
private async Task<bool> 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;
}
}
}

View File

@@ -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<OrcidAuthenticationOptions>
{
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<OrcidAuthenticationMiddleware>();
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,
};
}
/// <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.Orcid.OrcidAuthenticationOptions" /> supplied to the constructor.
/// </returns>
protected override AuthenticationHandler<OrcidAuthenticationOptions> 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;
}
}
}

View File

@@ -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
{
/// <summary>
/// Gets or sets the a pinned certificate validator to use to validate the endpoints used
/// in back channel communications belong to Orcid.
/// </summary>
/// <value>
/// The pinned certificate validator.
/// </value>
/// <remarks>
/// 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.
/// </remarks>
public ICertificateValidator BackchannelCertificateValidator { get; set; }
/// <summary>
/// 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.
/// </summary>
public HttpMessageHandler BackchannelHttpHandler { get; set; }
/// <summary>
/// Gets or sets timeout value in milliseconds for back channel communications with Orcid.
/// </summary>
/// <value>
/// The back channel timeout in milliseconds.
/// </value>
public TimeSpan BackchannelTimeout { get; set; }
/// <summary>
/// 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".
/// </summary>
public PathString CallbackPath { get; set; }
/// <summary>
/// Get or sets the text that the user can display on a sign in user interface.
/// </summary>
public string Caption
{
get { return Description.Caption; }
set { Description.Caption = value; }
}
/// <summary>
/// Gets or sets the Orcid supplied Client ID
/// </summary>
public string ClientId { get; set; }
/// <summary>
/// Gets or sets the Orcid supplied Client Secret
/// </summary>
public string ClientSecret { get; set; }
/// <summary>
/// Gets the sets of OAuth endpoints used to authenticate against Orcid. Overriding these endpoints allows you to use Orcid Enterprise for
/// authentication.
/// </summary>
public OrcidAuthenticationEndpoints Endpoints { get; set; }
/// <summary>
/// Gets or sets the <see cref="IOrcidAuthenticationProvider" /> used in the authentication events
/// </summary>
public IOrcidAuthenticationProvider Provider { get; set; }
/// <summary>
/// A list of permissions to request.
/// </summary>
public IList<string> Scope { get; private set; }
/// <summary>
/// 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.
/// </summary>
public string Prompt { get; set; }
/// <summary>
/// Gets or sets the name of another authentication middleware which will be responsible for actually issuing a user
/// <see cref="System.Security.Claims.ClaimsIdentity" />.
/// </summary>
public string SignInAsAuthenticationType { get; set; }
/// <summary>
/// Gets or sets the type used to secure data handled by the middleware.
/// </summary>
public ISecureDataFormat<AuthenticationProperties> StateDataFormat { get; set; }
/// <summary>
/// Initializes a new <see cref="OrcidAuthenticationOptions" />
/// </summary>
public OrcidAuthenticationOptions()
: base("Orcid")
{
Caption = Constants.DefaultAuthenticationType;
CallbackPath = new PathString("/signin-orcid");
AuthenticationMode = AuthenticationMode.Passive;
Scope = new List<string>();
BackchannelTimeout = TimeSpan.FromSeconds(60);
Endpoints = new OrcidAuthenticationEndpoints
{
AuthorizationEndpoint = OrcidAuthenticationEndpoints.Default.AuthorizationEndPoint,
TokenEndpoint = OrcidAuthenticationEndpoints.Default.TokenEndpoint,
ApiEndpoint = OrcidAuthenticationEndpoints.Default.ApiEndpoint
};
}
}
}

View File

@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{89CB4342-E23D-4E7C-89E5-C369599A5860}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Owin.Security.Providers.Orcid</RootNamespace>
<AssemblyName>Owin.Security.Providers.Orcid</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Owin, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Owin.Security, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Owin.Security.3.0.1\lib\net45\Microsoft.Owin.Security.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
<HintPath>..\..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Constants.cs" />
<Compile Include="Message\OrcidMessage.cs" />
<Compile Include="Message\OrcidMessageExtensions.cs" />
<Compile Include="OrcidAuthenticationEndpoints.cs" />
<Compile Include="OrcidAuthenticationExtensions.cs" />
<Compile Include="OrcidAuthenticationHandler.cs" />
<Compile Include="OrcidAuthenticationMiddleware.cs" />
<Compile Include="OrcidAuthenticationOptions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Provider\IOrcidAuthenticationProvider.cs" />
<Compile Include="Provider\OrcidAuthenticatedContext.cs" />
<Compile Include="Provider\OrcidAuthenticationProvider.cs" />
<Compile Include="Provider\OrcidReturnEndpointContext.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -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")]

View File

@@ -0,0 +1,24 @@
using System.Threading.Tasks;
namespace Owin.Security.Providers.Orcid
{
/// <summary>
/// Specifies callback methods which the <see cref="OrcidAuthenticationMiddleware"></see> invokes to enable developer control over the authentication process. />
/// </summary>
public interface IOrcidAuthenticationProvider
{
/// <summary>
/// Invoked whenever Orcid succesfully authenticates a user
/// </summary>
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
Task Authenticated(OrcidAuthenticatedContext context);
/// <summary>
/// Invoked prior to the <see cref="System.Security.Claims.ClaimsIdentity"/> being saved in a local cookie and the browser being redirected to the originally requested URL.
/// </summary>
/// <param name="context"></param>
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
Task ReturnEndpoint(OrcidReturnEndpointContext context);
}
}

View File

@@ -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
{
/// <summary>
/// Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.
/// </summary>
public class OrcidAuthenticatedContext : BaseContext
{
/// <summary>
/// Initializes a <see cref="VKontakteAuthenticatedContext"/>
/// </summary>
/// <param name="context">The OWIN environment</param>
/// <param name="user">The JSON-serialized user</param>
/// <param name="accessToken">VK Access token</param>
public OrcidAuthenticatedContext(IOwinContext context, JObject user, string accessToken)
: base(context)
{
User = user;
AccessToken = accessToken;
}
/// <summary>
/// Gets the JSON-serialized user
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public JObject User { get; internal set; }
/// <summary>
/// Gets the VK access token
/// </summary>
public string AccessToken { get; internal set; }
/// <summary>
/// Gets the user ID
/// </summary>
public string Id { get; internal set; }
/// <summary>
/// Gets the user's name
/// </summary>
public string UserName { get; internal set; }
/// <summary>
/// Gets the user's last name
/// </summary>
public string LastName { get; internal set; }
/// <summary>
/// Gets the user's first name
/// </summary>
public string FirstName { get; internal set; }
/// <summary>
/// Gets the user's Email
/// </summary>
public string Email { get; internal set; }
/// <summary>
/// Gets the <see cref="ClaimsIdentity"/> representing the user
/// </summary>
public ClaimsIdentity Identity { get; set; }
/// <summary>
/// Gets or sets a property bag for common authentication properties
/// </summary>
public AuthenticationProperties Properties { get; set; }
private static string TryGetValue(JObject user, string propertyName)
{
JToken value;
return user.TryGetValue(propertyName, out value) ? value.ToString() : null;
}
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.Threading.Tasks;
namespace Owin.Security.Providers.Orcid
{
/// <summary>
/// Default <see cref="IOrcidAuthenticationProvider"/> implementation.
/// </summary>
public class OrcidAuthenticationProvider : IOrcidAuthenticationProvider
{
/// <summary>
/// Initializes a <see cref="OrcidAuthenticationProvider"/>
/// </summary>
public OrcidAuthenticationProvider()
{
OnAuthenticated = context => Task.FromResult<object>(null);
OnReturnEndpoint = context => Task.FromResult<object>(null);
}
/// <summary>
/// Gets or sets the function that is invoked when the Authenticated method is invoked.
/// </summary>
public Func<OrcidAuthenticatedContext, Task> OnAuthenticated { get; set; }
/// <summary>
/// Gets or sets the function that is invoked when the ReturnEndpoint method is invoked.
/// </summary>
public Func<OrcidReturnEndpointContext, Task> OnReturnEndpoint { get; set; }
/// <summary>
/// Invoked whenever Orcid succesfully authenticates a user
/// </summary>
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
public virtual Task Authenticated(OrcidAuthenticatedContext context)
{
return OnAuthenticated(context);
}
/// <summary>
/// Invoked prior to the <see cref="System.Security.Claims.ClaimsIdentity"/> being saved in a local cookie and the browser being redirected to the originally requested URL.
/// </summary>
/// <param name="context"></param>
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
public virtual Task ReturnEndpoint(OrcidReturnEndpointContext context)
{
return OnReturnEndpoint(context);
}
}
}

View File

@@ -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
{
/// <summary>
/// Provides context information to middleware providers.
/// </summary>
public class OrcidReturnEndpointContext : ReturnEndpointContext
{
/// <summary>
///
/// </summary>
/// <param name="context">OWIN environment</param>
/// <param name="ticket">The authentication ticket</param>
public OrcidReturnEndpointContext(
IOwinContext context,
AuthenticationTicket ticket)
: base(context, ticket)
{
}
}
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net452" />
<package id="Microsoft.Owin.Security" version="3.0.1" targetFramework="net452" />
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net452" />
<package id="Owin" version="1.0" targetFramework="net452" />
</packages>