Compare commits

...

50 Commits

Author SHA1 Message Date
Tommy Parnell
b4bd9587dc bump version 2017-11-03 00:42:48 -04:00
Joseph Pisano
43ee292d0c I added an auth provider for the Podbean API. (#218)
* Added the podbean auth provider.

* Updated Readme to include podbean

* Fixed Owin nuget reference

* Removed regions from around using statements, corrected the VS version in the solution file and changed the Startup.Auth in the demo project to use spaces instead of tabs.

* Added the OAuth refresh token to the PodbeanAuthenticatedContext.
2017-11-03 00:40:41 -04:00
Tommy Parnell
b351a6bf0f bump version 2017-10-13 09:42:25 -04:00
AlastairBateman
49e1126bc7 Updating DropboxAuthenticatedContext.cs and DropboxAuthennticationHandler.cs to use the Dropbox v2 API as they're shutting down their v1 API: https://blogs.dropbox.com/developers/2017/09/api-v1-shutdown-details/ (#216)
As per issue: https://github.com/TerribleDev/OwinOAuthProviders/issues/215
2017-10-13 09:32:16 -04:00
Hoku Kim
ba08bffec3 Added LinkedInAuthenticationOptions.Proxy host for web applications running behind a proxy. This allows the middleware to authenticate for the proxy request host instead of the application request host. (#213) 2017-10-13 09:32:06 -04:00
Tommy Parnell
9bb2a75712 remove unused file 2017-07-16 11:48:21 -04:00
Tommy Parnell
60f3438ce7 strong name all the things 2017-07-16 11:48:03 -04:00
Tommy Parnell
346facbe2f bump version 2017-05-19 17:05:23 -04:00
bkrupa
934d75e9ad Added ability to force verify on twitch authentication (#202) 2017-05-19 16:52:05 -04:00
bencurthoys
feba5cc2fb PayPal TLS Fix (#206)
* PayPal TLS Fix

PayPal requires TLS 1.2 now - Added comments explaining this is the
case, and additional error handling for when the callback fails when TLS
1.0 or 1.1 is set.

* Removed Sandbox Credentials

Tch.

* Changed exception to WebException

If the PayPalAuthenticationHandler sees that an error caused by the
wrong SecurityProtocol has occurred, it throws a new WebException
instead of just Exception

* Reverted Whitespace changes to Startup.Auth.cs

Whitespace somehow got changed. I think this reverts it.
2017-05-19 16:51:53 -04:00
Guillaume
5626c02442 Added provider for Evernote (#197)
* Evernote provider is now available.
Based on Evernote SDK for .NET and the obsolete doc (ie. https://dev.evernote.com/doc/articles/authentication.php) (Step 3 is POST, not GET)

* Fix SyncrhonizationContext deadlock caused by ASP.NET site

* Evernote provider now working trought Xamarin OAuthAuthenticator and Identity Server 3

* Add claims for notestoreuri and accesstoken

* Evernote OK, before cleanup

* Cleanup

* Remove my credentials in demo project.

* Change the default URL to lower case
2017-03-04 20:30:56 -05:00
Tommy Parnell
d07c280377 bump version 2016-12-09 07:59:02 -05:00
jasondaicoder
f5ccfa5fdf added provider for wso2 (#194)
* added provider for wso2

* corrected some wrong comments

* fixed wrong pacakges reference path issues

* Provided enough comments for the extension methods
2016-12-09 07:57:58 -05:00
Kevin Boyle
5b4c7a9621 Write out the full exception with stacktrace (#195) 2016-12-09 07:57:34 -05:00
Tommy Parnell
03bca9ac2c Merge branch 'YuriPetskus-patch-1' 2016-11-21 17:29:56 -05:00
Tommy Parnell
52ab5c2571 bump version 2016-11-21 17:29:30 -05:00
Tommy Parnell
4112051063 put email behind if wall 2016-11-21 17:25:59 -05:00
Tommy Parnell
e0b97f3bb5 Merge branch 'patch-1' of git://github.com/YuriPetskus/OwinOAuthProviders into YuriPetskus-patch-1 2016-11-21 17:22:52 -05:00
Tommy Parnell
6d999ec41a Update README.md 2016-11-11 17:26:48 -05:00
Tommy Parnell
2642736548 bump version 2016-11-01 18:14:18 -04:00
MartinPaulLippert
6cfab46b9d Added LinkedInAuthentication ApplyRedirect (#187)
* Added ApplyRedirect to LinkedInAuthentication Provider
Changed LinkedInAuthenticationHandler to use ApplyRedirect
Added LinkedInApplyRedirectContext

Changes allow a developer to override the authorization endpoint
2016-11-01 18:12:31 -04:00
Tommy Parnell
2567036747 bump version 2016-10-27 08:17:57 -04:00
Tommy Parnell
2e5561befb Merge pull request #185 from diwu86/sf_scope
add id to scope and mobile_phone to context
2016-10-27 08:16:19 -04:00
Di Wu
202f215c19 add id to scope and mobile_phone to context 2016-10-26 16:48:11 -04:00
Tommy Parnell
28b4edbde9 fix readme (#181) 2016-10-06 16:07:25 -04:00
Tommy Parnell
2eb914597b 2.9 2016-10-06 15:53:55 -04:00
denis32000
ed5615cd9f Added providers for Box and Baidu (#179)
* Added project with provider for Box cloud service
* Added Baidu provider
2016-10-06 15:49:04 -04:00
Tommy Parnell
83bad8424c bump version 2016-09-27 15:34:12 -04:00
Jimmi Corbishley
788ef3a1db Add channel properties for slack (#175)
* Get incoming webhook object infomation from reponse.

* Null check for incoming webhook object
2016-09-27 15:22:48 -04:00
Tommy Parnell
b2094b9c4f bump version 2016-07-03 14:13:04 -04:00
Jack Tench
e51b69cf8f Allow custom scopes for Salesforce (#168)
* Pass list of scopes to Saleforce authorization endpoint to allow for customisation of scopes via existing configuration option.

* SalesforceAuthenticationHandler ensure Options.Scope not null
2016-07-03 10:42:27 -07:00
Tommy Parnell
c083049ce3 revert changes to the build for now 2016-07-03 13:42:35 -04:00
Tommy Parnell
d7947f010a Revert "if else yo"
This reverts commit d761e6c904.
2016-07-03 13:36:18 -04:00
Tommy Parnell
bc4767f479 Revert "fix publish syntax"
This reverts commit a5af6f4886.
2016-07-03 13:36:16 -04:00
Tommy Parnell
fb60718771 Revert "minor build bug"
This reverts commit b0825b8339.
2016-07-03 13:36:15 -04:00
Tommy Parnell
c596c7787c Revert "build things"
This reverts commit 1625f787d2.
2016-07-03 13:36:12 -04:00
Tommy Parnell
1625f787d2 build things 2016-07-02 13:25:30 -07:00
Tommy Parnell
b0825b8339 minor build bug 2016-07-01 00:49:05 -07:00
Tommy Parnell
a5af6f4886 fix publish syntax 2016-07-01 00:23:51 -07:00
Tommy Parnell
d761e6c904 if else yo 2016-06-29 17:09:18 -07:00
Tommy Parnell
ef33598e31 have appveyor publish the things 2016-06-29 17:01:37 -07:00
Tommy Parnell
a551a1149e update docs link 2016-06-16 06:56:29 -04:00
Tommy Parnell
9a4ead5c50 bump version 2016-06-11 09:19:26 -04:00
Tommy Parnell
63564a691e Merge branch 'master' of github.com:tparnell8/OwinOAuthProviders 2016-06-11 09:18:26 -04:00
Tommy Parnell
f37aea4f46 bump version 2016-06-11 09:12:57 -04:00
Paulo Pinto
45c98eb1f3 Added MyHeritage provider (#164)
* Added MyHeritage Provider

Initial commit for a MyHeritage provider

* Fixed signin case

* retarget 4.5

* Add MyHeritage to readme

* removed test references from Demo project

* fixed MyHeritage id token name
2016-06-11 09:09:40 -04:00
Tommy Parnell
4f5625c185 bump version 2016-06-09 19:30:33 -04:00
Dave Timmins
e4d504a0f4 Added refreshtoken and updated urls (#163) 2016-06-09 19:26:12 -04:00
Yuri Petskus
511a4c1287 Update VKontakteAuthenticationHandler.cs
Email support. VK send it with access_token
2016-05-24 15:53:12 +06:00
Tommy Parnell
4891cb06dc minor fixes 2016-05-20 09:04:21 -04:00
129 changed files with 6759 additions and 92 deletions

3
.gitignore vendored
View File

@@ -70,6 +70,9 @@ ipch/
# Guidance Automation Toolkit
*.gpState
# CodeRush is a .NET coding add-in
.cr/
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper

View File

@@ -23,4 +23,4 @@ DEPENDENCIES
os
BUNDLED WITH
1.12.1
1.13.7

View File

@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25123.0
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
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
@@ -95,8 +95,21 @@ 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
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers.Geni", "src\Owin.Security.Providers.Geni\Owin.Security.Providers.Geni.csproj", "{9DE25431-F935-48D7-8EB5-ACB6F918111C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers.MyHeritage", "src\Owin.Security.Providers.MyHeritage\Owin.Security.Providers.MyHeritage.csproj", "{84795078-31B5-4369-BD1B-F960165F8C71}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers.Box", "src\Owin.Security.Providers.Box\Owin.Security.Providers.Box.csproj", "{1AEF8813-E1F9-41E1-BC8D-732960595E9F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers.Baidu", "src\Owin.Security.Providers.Baidu\Owin.Security.Providers.Baidu.csproj", "{E2759807-4D7C-4288-AAC8-F5B7B4616680}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers.Evernote", "src\Owin.Security.Providers.Evernote\Owin.Security.Providers.Evernote.csproj", "{B8F05057-8B9F-44CE-B983-99CB7C2E9E5A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers.WSO2", "src\Owin.Security.Providers.WSO2\Owin.Security.Providers.WSO2.csproj", "{8FD3A9CB-E684-42C0-A8BF-7746FDD3D43C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Owin.Security.Providers.Podbean", "src\Owin.Security.Providers.Podbean\Owin.Security.Providers.Podbean.csproj", "{A7B95FD4-08AD-499F-B574-07560CC2A63F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -295,8 +308,35 @@ Global
{9DE25431-F935-48D7-8EB5-ACB6F918111C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9DE25431-F935-48D7-8EB5-ACB6F918111C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9DE25431-F935-48D7-8EB5-ACB6F918111C}.Release|Any CPU.Build.0 = Release|Any CPU
{84795078-31B5-4369-BD1B-F960165F8C71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{84795078-31B5-4369-BD1B-F960165F8C71}.Debug|Any CPU.Build.0 = Debug|Any CPU
{84795078-31B5-4369-BD1B-F960165F8C71}.Release|Any CPU.ActiveCfg = Release|Any CPU
{84795078-31B5-4369-BD1B-F960165F8C71}.Release|Any CPU.Build.0 = Release|Any CPU
{1AEF8813-E1F9-41E1-BC8D-732960595E9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1AEF8813-E1F9-41E1-BC8D-732960595E9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1AEF8813-E1F9-41E1-BC8D-732960595E9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1AEF8813-E1F9-41E1-BC8D-732960595E9F}.Release|Any CPU.Build.0 = Release|Any CPU
{E2759807-4D7C-4288-AAC8-F5B7B4616680}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E2759807-4D7C-4288-AAC8-F5B7B4616680}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E2759807-4D7C-4288-AAC8-F5B7B4616680}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E2759807-4D7C-4288-AAC8-F5B7B4616680}.Release|Any CPU.Build.0 = Release|Any CPU
{B8F05057-8B9F-44CE-B983-99CB7C2E9E5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B8F05057-8B9F-44CE-B983-99CB7C2E9E5A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B8F05057-8B9F-44CE-B983-99CB7C2E9E5A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B8F05057-8B9F-44CE-B983-99CB7C2E9E5A}.Release|Any CPU.Build.0 = Release|Any CPU
{8FD3A9CB-E684-42C0-A8BF-7746FDD3D43C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8FD3A9CB-E684-42C0-A8BF-7746FDD3D43C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8FD3A9CB-E684-42C0-A8BF-7746FDD3D43C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8FD3A9CB-E684-42C0-A8BF-7746FDD3D43C}.Release|Any CPU.Build.0 = Release|Any CPU
{A7B95FD4-08AD-499F-B574-07560CC2A63F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A7B95FD4-08AD-499F-B574-07560CC2A63F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A7B95FD4-08AD-499F-B574-07560CC2A63F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A7B95FD4-08AD-499F-B574-07560CC2A63F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {EDFDB942-6583-4AD9-A868-B354B5BF07FC}
EndGlobalSection
EndGlobal

View File

@@ -2,9 +2,8 @@
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Owin;
//using Owin.Security.Providers.Orcid;
//using Owin.Security.Providers.Discord;
using Owin.Security.Providers.Evernote;
using Owin.Security.Providers.PayPal;
namespace OwinOAuthProvidersDemo
{
@@ -168,6 +167,14 @@ namespace OwinOAuthProvidersDemo
// clientId: "",
// clientSecret: "");
//app.UseBoxAuthentication(
// appKey: "",
// appSecret: "");
//app.UseBaiduAuthentication(
// apiKey: "",
// secretKey: "");
//app.UseBattleNetAuthentication(new BattleNetAuthenticationOptions
//{
// ClientId = "",
@@ -187,10 +194,20 @@ namespace OwinOAuthProvidersDemo
// clientId: "",
// clientSecret: "");
//app.UsePayPalAuthentication(
// clientId: "",
// clientSecret: "",
// isSandbox: false);
// PayPal no longer supports Tls 1.0 or 1.1
// https://devblog.paypal.com/upcoming-security-changes-notice/
// http://stackoverflow.com/questions/34939523/the-request-was-aborted-could-not-create-ssl-tls-secure-channel-sandbox-account
// System.Net.ServicePointManager.SecurityProtocol = System.Net.ServicePointManager.SecurityProtocol | System.Net.SecurityProtocolType.Tls12;
// The Return URL must be configured in the PayPal application to https://[hostname][:port]/signin-paypal
// PayPalAuthenticationOptions payPalOptions = new PayPalAuthenticationOptions(true);
// payPalOptions.ClientId = "";
// payPalOptions.ClientSecret = "";
// payPalOptions.Scope.Add("email");
// payPalOptions.Scope.Add("profile");
// payPalOptions.Scope.Add("address");
// payPalOptions.Scope.Add("https://uri.paypal.com/services/expresscheckout");
// app.UsePayPalAuthentication(payPalOptions);
//app.UseWargamingAccountAuthentication("", WargamingAuthenticationOptions.Region.NorthAmerica);
@@ -278,8 +295,25 @@ namespace OwinOAuthProvidersDemo
//app.("", "");
//app.UseOrcidAuthentication("","");
//app.UseDiscordAuthentication("", "");
//app.UseDiscordAuthentication("", "");
//app.UseGeniAuthentication("", "");
//app.UseMyHeritageAuthentication("", "");
//app.UseWSO2Authentication("", "", "");
//app.UseEvernoteAuthentication(new EvernoteAuthenticationOptions()
//{
// IsSandBox = true,
// AppKey = "",
// AppSecret = ""
//});
//app.UsePodbeanAuthentication(new PodbeanAuthenticationOptions
//{
// AppId = "",
// AppSecret = "",
// DebugUsingRequestHeadersToBuildBaseUri = true
//});
}
}
}
}

View File

@@ -22,6 +22,7 @@
<IISExpressUseClassicPipelineMode>false</IISExpressUseClassicPipelineMode>
<UseGlobalApplicationHostFile />
<TargetFrameworkProfile />
<Use64BitIISExpress />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -245,18 +246,226 @@
<ItemGroup>
<Folder Include="App_Data\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\src\Owin.Security.Providers.Discord\Owin.Security.Providers.Discord.csproj">
<Project>{4be728eb-778a-41af-8dea-0c7159711d44}</Project>
<Name>Owin.Security.Providers.Discord</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="Scripts\jquery-2.2.3.min.map" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\base\Owin.Security.Providers.OpenIDBase\Owin.Security.Providers.OpenIDBase.csproj">
<Project>{4fd7b873-1994-4990-aa40-c37060121494}</Project>
<Name>Owin.Security.Providers.OpenIDBase</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.ArcGISOnline\Owin.Security.Providers.ArcGISOnline.csproj">
<Project>{8a49faef-d365-4d25-942c-1cad03845a5e}</Project>
<Name>Owin.Security.Providers.ArcGISOnline</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Asana\Owin.Security.Providers.Asana.csproj">
<Project>{f3e27220-1d8c-4037-94aa-7b7f4a12f351}</Project>
<Name>Owin.Security.Providers.Asana</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Backlog\Owin.Security.Providers.Backlog.csproj">
<Project>{2dc03778-9ef1-466a-83ec-7d8422decd23}</Project>
<Name>Owin.Security.Providers.Backlog</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Baidu\Owin.Security.Providers.Baidu.csproj">
<Project>{e2759807-4d7c-4288-aac8-f5b7b4616680}</Project>
<Name>Owin.Security.Providers.Baidu</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.BattleNet\Owin.Security.Providers.BattleNet.csproj">
<Project>{99a175da-ade4-436c-a272-c8ae44b7a086}</Project>
<Name>Owin.Security.Providers.BattleNet</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Bitbucket\Owin.Security.Providers.Bitbucket.csproj">
<Project>{e5212fc7-abcb-462f-9989-8e022dffe43c}</Project>
<Name>Owin.Security.Providers.Bitbucket</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Box\Owin.Security.Providers.Box.csproj">
<Project>{1aef8813-e1f9-41e1-bc8d-732960595e9f}</Project>
<Name>Owin.Security.Providers.Box</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Buffer\Owin.Security.Providers.Buffer.csproj">
<Project>{6f75fc1f-d9e9-49b3-a6ce-cfa8feea11a5}</Project>
<Name>Owin.Security.Providers.Buffer</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Cosign\Owin.Security.Providers.Cosign.csproj">
<Project>{1f1f8d6b-7219-46fa-93d3-8d3061a6cbbf}</Project>
<Name>Owin.Security.Providers.Cosign</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.DeviantArt\Owin.Security.Providers.DeviantArt.csproj">
<Project>{fabd2e54-976d-41f5-8800-dee58acc027c}</Project>
<Name>Owin.Security.Providers.DeviantArt</Name>
</ProjectReference>
<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>
<ProjectReference Include="..\src\Owin.Security.Providers.DoYouBuzz\Owin.Security.Providers.DoYouBuzz.csproj">
<Project>{4550d8bd-05a7-44f8-bbc0-c3d8e7af2912}</Project>
<Name>Owin.Security.Providers.DoYouBuzz</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Dropbox\Owin.Security.Providers.Dropbox.csproj">
<Project>{cef697b1-3651-49e5-9060-65f2e26c039c}</Project>
<Name>Owin.Security.Providers.Dropbox</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.EVEOnline\Owin.Security.Providers.EVEOnline.csproj">
<Project>{f5dc23f4-5042-4024-9e34-aca648602ba0}</Project>
<Name>Owin.Security.Providers.EVEOnline</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Evernote\Owin.Security.Providers.Evernote.csproj">
<Project>{b8f05057-8b9f-44ce-b983-99cb7c2e9e5a}</Project>
<Name>Owin.Security.Providers.Evernote</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Fitbit\Owin.Security.Providers.Fitbit.csproj">
<Project>{ca44d014-5a74-4749-a891-1f711fd3a266}</Project>
<Name>Owin.Security.Providers.Fitbit</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Flickr\Owin.Security.Providers.Flickr.csproj">
<Project>{af6cbeb8-5638-43d4-839e-c81f305960be}</Project>
<Name>Owin.Security.Providers.Flickr</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Foursquare\Owin.Security.Providers.Foursquare.csproj">
<Project>{8acd9194-1efe-4128-ac42-856d856332a4}</Project>
<Name>Owin.Security.Providers.Foursquare</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Geni\Owin.Security.Providers.Geni.csproj">
<Project>{9de25431-f935-48d7-8eb5-acb6f918111c}</Project>
<Name>Owin.Security.Providers.Geni</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.GitHub\Owin.Security.Providers.GitHub.csproj">
<Project>{803f9eb7-029c-45ac-ab81-135e60d5beae}</Project>
<Name>Owin.Security.Providers.GitHub</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Gitter\Owin.Security.Providers.Gitter.csproj">
<Project>{42ec50eb-0c51-460c-93a4-1e007bf1f323}</Project>
<Name>Owin.Security.Providers.Gitter</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.GooglePlus\Owin.Security.Providers.GooglePlus.csproj">
<Project>{d3fef959-0e0e-4f50-954c-f123a0b629dc}</Project>
<Name>Owin.Security.Providers.GooglePlus</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.HealthGraph\Owin.Security.Providers.HealthGraph.csproj">
<Project>{157bb715-29b2-4202-8a59-ccbacfcbedd3}</Project>
<Name>Owin.Security.Providers.HealthGraph</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Imgur\Owin.Security.Providers.Imgur.csproj">
<Project>{101841d3-645e-4a44-af8b-8aaa85ceea4e}</Project>
<Name>Owin.Security.Providers.Imgur</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Instagram\Owin.Security.Providers.Instagram.csproj">
<Project>{041178c4-6131-4d68-9896-ce33124d83a0}</Project>
<Name>Owin.Security.Providers.Instagram</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.LinkedIn\Owin.Security.Providers.LinkedIn.csproj">
<Project>{9fa87825-30e9-48d7-ac4a-39e8f0c2777c}</Project>
<Name>Owin.Security.Providers.LinkedIn</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.MyHeritage\Owin.Security.Providers.MyHeritage.csproj">
<Project>{84795078-31b5-4369-bd1b-f960165f8c71}</Project>
<Name>Owin.Security.Providers.MyHeritage</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Onshape\Owin.Security.Providers.Onshape.csproj">
<Project>{9fec99f8-6f45-40a2-8200-85381434c79a}</Project>
<Name>Owin.Security.Providers.Onshape</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.OpenID\Owin.Security.Providers.OpenID.csproj">
<Project>{90c152d7-9c66-4949-9998-c7ce48b593de}</Project>
<Name>Owin.Security.Providers.OpenID</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Orcid\Owin.Security.Providers.Orcid.csproj">
<Project>{89cb4342-e23d-4e7c-89e5-c369599a5860}</Project>
<Name>Owin.Security.Providers.Orcid</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.PayPal\Owin.Security.Providers.PayPal.csproj">
<Project>{f7129064-3db7-4b79-81d3-80130d664e45}</Project>
<Name>Owin.Security.Providers.PayPal</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Podbean\Owin.Security.Providers.Podbean.csproj">
<Project>{a7b95fd4-08ad-499f-b574-07560cc2a63f}</Project>
<Name>Owin.Security.Providers.Podbean</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Reddit\Owin.Security.Providers.Reddit.csproj">
<Project>{d0cd86c8-a6f9-4c6c-9bf0-eaa461e7fbad}</Project>
<Name>Owin.Security.Providers.Reddit</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Salesforce\Owin.Security.Providers.Salesforce.csproj">
<Project>{827a9d68-0dd4-4c5e-b763-8302faeedecc}</Project>
<Name>Owin.Security.Providers.Salesforce</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Shopify\Owin.Security.Providers.Shopify.csproj">
<Project>{67f12bfb-eb3a-4a86-b5dc-f4c066fdf792}</Project>
<Name>Owin.Security.Providers.Shopify</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.SlackProvider\Owin.Security.Providers.SlackProvider.csproj">
<Project>{3e6f293d-8500-428d-bdc9-27440cc91e16}</Project>
<Name>Owin.Security.Providers.SlackProvider</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.SoundCloud\Owin.Security.Providers.SoundCloud.csproj">
<Project>{2c959026-7058-4302-a6c4-dfd10a030585}</Project>
<Name>Owin.Security.Providers.SoundCloud</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Spotify\Owin.Security.Providers.Spotify.csproj">
<Project>{683b4041-a399-40ce-84b8-392f08a6805d}</Project>
<Name>Owin.Security.Providers.Spotify</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.StackExchange\Owin.Security.Providers.StackExchange.csproj">
<Project>{2c0e07ed-f26d-4ff8-8c3d-f760c09a2d5a}</Project>
<Name>Owin.Security.Providers.StackExchange</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Steam\Owin.Security.Providers.Steam.csproj">
<Project>{312c4ed7-8ca1-4723-9203-abc694dfdc7c}</Project>
<Name>Owin.Security.Providers.Steam</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.TripIt\Owin.Security.Providers.TripIt.csproj">
<Project>{b35e2616-dc00-48b4-bd58-7e23046257f1}</Project>
<Name>Owin.Security.Providers.TripIt</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Twitch\Owin.Security.Providers.Twitch.csproj">
<Project>{c3cf8734-6aac-4f59-9a3e-1cba8582cd48}</Project>
<Name>Owin.Security.Providers.Twitch</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Untappd\Owin.Security.Providers.Untappd.csproj">
<Project>{3e89eca3-f4e7-4181-b26b-8250d5151044}</Project>
<Name>Owin.Security.Providers.Untappd</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Vimeo\Owin.Security.Providers.Vimeo.csproj">
<Project>{98ecc703-d651-4ead-a55d-aa3e903ae4d7}</Project>
<Name>Owin.Security.Providers.Vimeo</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.VisualStudio\Owin.Security.Providers.VisualStudio.csproj">
<Project>{3b19fa31-ddff-427f-9d73-f860de74bbc2}</Project>
<Name>Owin.Security.Providers.VisualStudio</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.VKontakte\Owin.Security.Providers.VKontakte.csproj">
<Project>{32d70e31-3799-482a-ac7a-081ff9206fc3}</Project>
<Name>Owin.Security.Providers.VKontakte</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Wargaming\Owin.Security.Providers.Wargaming.csproj">
<Project>{aa72bfce-8495-4a4d-988d-f8d490521776}</Project>
<Name>Owin.Security.Providers.Wargaming</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.WordPress\Owin.Security.Providers.WordPress.csproj">
<Project>{0ede8223-dd5f-4db8-a98a-64b1f4591f48}</Project>
<Name>Owin.Security.Providers.WordPress</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.WSO2\Owin.Security.Providers.WSO2.csproj">
<Project>{8fd3a9cb-e684-42c0-a8bf-7746fdd3d43c}</Project>
<Name>Owin.Security.Providers.WSO2</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Xing\Owin.Security.Providers.Xing.csproj">
<Project>{d497d8bd-6ef9-4c30-b195-b0dd153418d6}</Project>
<Name>Owin.Security.Providers.Xing</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Yahoo\Owin.Security.Providers.Yahoo.csproj">
<Project>{1765bedb-9e4b-468c-baf6-06784cdced67}</Project>
<Name>Owin.Security.Providers.Yahoo</Name>
</ProjectReference>
<ProjectReference Include="..\src\Owin.Security.Providers.Yammer\Owin.Security.Providers.Yammer.csproj">
<Project>{8d029a93-e687-4ddf-82b0-700ebbf477f7}</Project>
<Name>Owin.Security.Providers.Yammer</Name>
</ProjectReference>
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

View File

@@ -25,9 +25,11 @@ Provides a set of extra authentication providers for OWIN ([Project Katana](http
- HealthGraph
- Instagram
- LinkedIn
- MyHeritage
- Onshape
- ORCID
- PayPal
- Podbean
- Reddit
- Salesforce
- Shopify
@@ -51,7 +53,9 @@ Provides a set of extra authentication providers for OWIN ([Project Katana](http
- Wargaming
## Implementation Guides
For above listed provider implementation guide, visit Jerrie Pelser's blog - [Be a Big Rockstar](http://www.beabigrockstar.com)
Take a look at the [samples project](https://github.com/TerribleDev/OwinOAuthProviders/blob/master/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs#L39).
## Installation
@@ -83,7 +87,7 @@ A big thanks goes out to all these contributors without whom this would not have
* Ben Foster (https://github.com/benfoster)
* Jonathan Peterson (https://github.com/eonasdan)
For most accurate and up to date list of contributors please see https://github.com/RockstarLabs/OwinOAuthProviders/graphs/contributors
For most accurate and up to date list of contributors please see https://github.com/TerribleDev/OwinOAuthProviders/graphs/contributors
## License

View File

@@ -9,13 +9,13 @@ require 'nokogiri'
require 'openssl'
import 'nuget.rake'
CLEAN.include(['src/**/obj', 'src/**/bin', 'tool', 'packages/**','src/**/*.nuspec', 'src/**/*.nupkg', 'tools', 'packages', '*.nupkg'])
CLEAN.include(['src/**/obj', 'Owin.Security.Providers.nuspec', 'Owin.Security.Providers-signed.nuspec' 'src/**/bin', 'tool', 'packages/**','src/**/*.nuspec', 'src/**/*.nupkg', 'tools', 'packages', '*.nupkg'])
Configuration = ENV['CONFIGURATION'] || 'Release'
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.3.0"
@version = "2.17.0"
PROJECTS = Dir.glob('src/*').select{|dir| File.directory? dir }
desc 'Retrieve things'
@@ -31,12 +31,15 @@ task :preflight => [:clean, :build, :nuspec_gen]
desc 'publish'
task :publish => [:preflight,:nuspec_gen, :nuspec_pack, :nuspec_publish]
build :compile do |t|
t.prop 'Configuration', Configuration
t.sln = 'OwinOAuthProviders.sln'
t.prop 'SignAssembly', 'true'
t.prop 'AssemblyOriginatorKeyFile', File.expand_path("keypair.snk")
end
desc "Generate nuspec files"
task :nuspec_gen do
template = ERB.new(File.read('nuspectemplate.nuspec.erb'))
@@ -51,6 +54,7 @@ task :nuspec_gen do
File.write('Owin.Security.Providers.nuspec', ERB.new(File.read('global.nuspec.erb')).result())
end
desc 'pack nuspec files'
task :nuspec_pack do
PROJECTS.each{|dir|
@@ -61,6 +65,7 @@ task :nuspec_pack do
sh "#{NUGET_EXE} pack Owin.Security.Providers.nuspec -Exclude \"**\""
end
desc 'publish nugets'
task :nuspec_publish do
PROJECTS.each{|dir|

View File

@@ -89,6 +89,10 @@
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<PropertyGroup>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\..\keypair.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<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.

View File

@@ -2,7 +2,7 @@
<package >
<metadata>
<id>Owin.Security.Providers.OpenIDBase</id>
<version>2.1</version>
<version>2.1.1</version>
<authors>Jerrie Pelser, Tommy Parnell and contributors</authors>
<owners>Tommy Parnell</owners>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>

View File

@@ -16,9 +16,7 @@
There are many individual providers, this package is a meta package that has a dependency on all of them.
</summary>
<releaseNotes>
Version 2.3
- Added Geni, and discord providers.
- Retarget to .net 4.5 from 4.5.2
View the release notes on github
</releaseNotes>
<copyright>Copyright 2013 - 2016</copyright>
<tags>owin katana oauth LinkedIn Yahoo Google+ GitHub Reddit Instagram StackExchange SalesForce TripIt Buffer ArcGIS Dropbox Wordpress Battle.NET Yammer OpenID Steam Twitch</tags>

BIN
keypair.snk Normal file

Binary file not shown.

View File

@@ -36,9 +36,9 @@ namespace :nuget do
# Make sure we get solution-level deps
#sh "#{CMD_PREFIX} #{NUGET}/nuget.exe i .nuget/packages.config -o packages"
FileList["src/**/packages.config"].each { |filepath|
sh "#{CMD_PREFIX} #{NUGET}/nuget.exe restore"
}
sh "nuget.exe restore"
end
end

View File

@@ -19,11 +19,9 @@
Also adds generic OpenID 2.0 providers as well implementations for Steam and Wargaming.
</summary>
<releaseNotes>
Version 2.3
- Added Geni, and discord providers.
- Retarget to .net 4.5 from 4.5.2
View the release notes on github
</releaseNotes>
<copyright>Copyright 2013 - 2016</copyright>
<tags>owin katana oauth LinkedIn Yahoo Google+ GitHub Reddit Instagram StackExchange SalesForce TripIt Buffer ArcGIS Dropbox Wordpress Battle.NET Yammer OpenID Steam Twitch</tags>
<tags>owin katana oauth LinkedIn Yahoo Google+ GitHub Reddit Instagram StackExchange SalesForce TripIt Buffer ArcGIS Dropbox Wordpress Battle.NET Yammer OpenID Steam Twitch Box Baidu</tags>
</metadata>
</package>

View File

@@ -81,6 +81,7 @@ namespace Owin.Security.Providers.ArcGISOnline
// Deserializes the token response
dynamic response = JsonConvert.DeserializeObject<dynamic>(text);
var accessToken = (string)response.access_token;
var refreshToken = (string)response.refresh_token;
// Get the ArcGISOnline user
var userRequest = new HttpRequestMessage(HttpMethod.Get, Options.Endpoints.UserInfoEndpoint + "?f=json&token=" + Uri.EscapeDataString(accessToken));
@@ -90,7 +91,7 @@ namespace Owin.Security.Providers.ArcGISOnline
text = await userResponse.Content.ReadAsStringAsync();
var user = JsonConvert.DeserializeObject<Provider.ArcGISOnlineUser>(text);
var context = new ArcGISOnlineAuthenticatedContext(Context, user, accessToken)
var context = new ArcGISOnlineAuthenticatedContext(Context, user, accessToken, refreshToken)
{
Identity = new ClaimsIdentity(
Options.AuthenticationType,

View File

@@ -14,7 +14,7 @@ namespace Owin.Security.Providers.ArcGISOnline
/// Endpoint which is used to redirect users to request ArcGISOnline access
/// </summary>
/// <remarks>
/// Defaults to https://www.arcgis.com/sharing/oauth2/authorize
/// Defaults to https://www.arcgis.com/sharing/rest/oauth2/authorize/
/// </remarks>
public string AuthorizationEndpoint { get; set; }
@@ -22,7 +22,7 @@ namespace Owin.Security.Providers.ArcGISOnline
/// Endpoint which is used to exchange code for access token
/// </summary>
/// <remarks>
/// Defaults to https://www.arcgis.com/sharing/oauth2/token
/// Defaults to https://www.arcgis.com/sharing/rest/oauth2/token/
/// </remarks>
public string TokenEndpoint { get; set; }
@@ -30,14 +30,14 @@ namespace Owin.Security.Providers.ArcGISOnline
/// Endpoint which is used to obtain user information after authentication
/// </summary>
/// <remarks>
/// Defaults to https://www.arcgis.com/sharing/rest/accounts/self
/// Defaults to https://www.arcgis.com/sharing/rest/community/self
/// </remarks>
public string UserInfoEndpoint { get; set; }
}
private const string AuthorizationEndPoint = "https://www.arcgis.com/sharing/oauth2/authorize";
private const string TokenEndpoint = "https://www.arcgis.com/sharing/oauth2/token";
private const string UserInfoEndpoint = "https://www.arcgis.com/sharing/rest/accounts/self";
private const string AuthorizationEndPoint = "https://www.arcgis.com/sharing/rest/oauth2/authorize/";
private const string TokenEndpoint = "https://www.arcgis.com/sharing/rest/oauth2/token/";
private const string UserInfoEndpoint = "https://www.arcgis.com/sharing/rest/community/self";
/// <summary>
/// Gets or sets the a pinned certificate validator to use to validate the endpoints used

View File

@@ -18,26 +18,32 @@ namespace Owin.Security.Providers.ArcGISOnline
/// </summary>
/// <param name="context">The OWIN environment</param>
/// <param name="user">The ArcGIS Online user</param>
/// <param name="accessToken">ArcGISOnline Access token</param>
public ArcGISOnlineAuthenticatedContext(IOwinContext context, ArcGISOnlineUser user, string accessToken)
/// <param name="accessToken">ArcGIS Online Access token</param>
/// <param name="refreshToken">ArcGIS Online Refresh token</param>
public ArcGISOnlineAuthenticatedContext(IOwinContext context, ArcGISOnlineUser user, string accessToken, string refreshToken)
: base(context)
{
AccessToken = accessToken;
Id = user.User.Username;
Name = user.User.FullName;
RefreshToken = refreshToken;
Id = user.Username;
Name = user.FullName;
Link = "https://www.arcgis.com/sharing/rest/community/users/" + Id;
UserName = Id;
Email = user.User.Email;
Email = user.Email;
}
/// <summary>
/// Gets the ArcGISOnline access token
/// Gets the ArcGIS Online access token
/// </summary>
public string AccessToken { get; private set; }
/// <summary>
/// Gets the ArcGISOnline user ID
/// Gets the ArcGIS Online refresh token
/// </summary>
public string RefreshToken { get; private set; }
/// <summary>
/// Gets the ArcGIS Online user ID
/// </summary>
public string Id { get; }
@@ -54,7 +60,7 @@ namespace Owin.Security.Providers.ArcGISOnline
public string Link { get; private set; }
/// <summary>
/// Gets the ArcGISOnline username
/// Gets the ArcGIS Online username
/// </summary>
public string UserName { get; private set; }

View File

@@ -1,12 +1,7 @@
namespace Owin.Security.Providers.ArcGISOnline.Provider
{
public class ArcGISOnlineUser
{
public User User { get; set; }
}
public class User
{
{
public string Username { get; set; }
public string FullName { get; set; }
public string Email { get; set; }

View File

@@ -0,0 +1,29 @@
using System;
namespace Owin.Security.Providers.Baidu
{
public static class BaiduAuthenticationExtensions
{
public static IAppBuilder UseBaiduAuthentication(this IAppBuilder app,
BaiduAuthenticationOptions options)
{
if (app == null)
throw new ArgumentNullException(nameof(app));
if (options == null)
throw new ArgumentNullException(nameof(options));
app.Use(typeof(BaiduAuthenticationMiddleware), app, options);
return app;
}
public static IAppBuilder UseBaiduAuthentication(this IAppBuilder app, string apiKey, string secretKey)
{
return app.UseBaiduAuthentication(new BaiduAuthenticationOptions
{
ApiKey = apiKey,
SecretKey = secretKey
});
}
}
}

View File

@@ -0,0 +1,236 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Owin;
using Microsoft.Owin.Infrastructure;
using Microsoft.Owin.Logging;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Net.Http.Headers;
namespace Owin.Security.Providers.Baidu
{
public class BaiduAuthenticationHandler : AuthenticationHandler<BaiduAuthenticationOptions>
{
private const string StateCookie = "_BaiduState";
private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
private const string TokenEndpoint = "https://openapi.baidu.com/oauth/2.0/token";
private const string UserInfoEndpoint = "https://openapi.baidu.com/rest/2.0/passport/users/getInfo";
private const string AuthEndpoint = "http://openapi.baidu.com/oauth/2.0/authorize";
private readonly ILogger _logger;
private readonly HttpClient _httpClient;
public BaiduAuthenticationHandler(HttpClient httpClient, ILogger logger)
{
_httpClient = httpClient;
_logger = logger;
}
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
AuthenticationProperties properties = null;
try
{
string code = null;
var query = Request.Query;
var values = query.GetValues("code");
if (values != null && values.Count == 1)
{
code = values[0];
}
var state = Request.Cookies[StateCookie];
properties = Options.StateDataFormat.Unprotect(state);
if (properties == null)
{
return null;
}
// OAuth2 10.12 CSRF
if (!ValidateCorrelationId(properties, _logger))
{
return new AuthenticationTicket(null, properties);
}
// Check for error
if (Request.Query.Get("error") != null)
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.ApiKey),
new KeyValuePair<string, string>("client_secret", Options.SecretKey)
};
// Request the token
var tokenResponse =
await _httpClient.PostAsync(TokenEndpoint, new FormUrlEncodedContent(body));
tokenResponse.EnsureSuccessStatusCode();
var text = await tokenResponse.Content.ReadAsStringAsync();
// Deserializes the token response
dynamic response = JsonConvert.DeserializeObject<dynamic>(text);
var accessToken = (string)response.access_token;
// Get the Baidu user
// http://developer.baidu.com/wiki/index.php?title=docs/oauth/rest/file_data_apis_list#.E8.BF.94.E5.9B.9E.E7.94.A8.E6.88.B7.E8.AF.A6.E7.BB.86.E8.B5.84.E6.96.99
var userResponse = await _httpClient.GetAsync(
UserInfoEndpoint + "?access_token=" + Uri.EscapeDataString(accessToken), Request.CallCancelled);
userResponse.EnsureSuccessStatusCode();
text = await userResponse.Content.ReadAsStringAsync();
var user = JObject.Parse(text);
var context = new BaiduAuthenticatedContext(Context, user, accessToken)
{
Identity = new ClaimsIdentity(
Options.AuthenticationType,
ClaimsIdentity.DefaultNameClaimType,
ClaimsIdentity.DefaultRoleClaimType)
};
if (!string.IsNullOrEmpty(context.Userid))
{
context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.Userid, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(context.UserName))
{
context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.UserName, 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 =
AuthEndpoint +
"?response_type=code" +
"&client_id=" + Uri.EscapeDataString(Options.ApiKey) +
"&redirect_uri=" + Uri.EscapeDataString(redirectUri) +
"&scope=" + Uri.EscapeDataString(scope) +
"&state=" + Uri.EscapeDataString(state);
var cookieOptions = new CookieOptions
{
HttpOnly = true,
Secure = Request.IsSecure
};
Response.StatusCode = 302;
Response.Cookies.Append(StateCookie, Options.StateDataFormat.Protect(properties), cookieOptions);
Response.Headers.Set("Location", 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 BaiduReturnEndpointContext(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,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.Baidu
{
public class BaiduAuthenticationMiddleware : AuthenticationMiddleware<BaiduAuthenticationOptions>
{
private readonly HttpClient _httpClient;
private readonly ILogger _logger;
public BaiduAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app,
BaiduAuthenticationOptions options)
: base(next, options)
{
if (string.IsNullOrWhiteSpace(Options.ApiKey))
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
Resources.Exception_OptionMustBeProvided, nameof(Options.ApiKey)));
if (string.IsNullOrWhiteSpace(Options.SecretKey))
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
Resources.Exception_OptionMustBeProvided, nameof(Options.SecretKey)));
_logger = app.CreateLogger<BaiduAuthenticationMiddleware>();
if (Options.Provider == null)
Options.Provider = new BaiduAuthenticationProvider();
if (Options.StateDataFormat == null)
{
var dataProtector = app.CreateDataProtector(
typeof (BaiduAuthenticationMiddleware).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.Baidu.BaiduAuthenticationOptions" /> supplied to the constructor.
/// </returns>
protected override AuthenticationHandler<BaiduAuthenticationOptions> CreateHandler()
{
return new BaiduAuthenticationHandler(_httpClient, _logger);
}
private static HttpMessageHandler ResolveHttpMessageHandler(BaiduAuthenticationOptions options)
{
var handler = options.BackchannelHttpHandler ?? new WebRequestHandler();
// If they provided a validator, apply it or fail.
if (options.BackchannelCertificateValidator == null) return handler;
// Set the cert validate callback
var webRequestHandler = handler as WebRequestHandler;
if (webRequestHandler == null)
{
throw new InvalidOperationException(Resources.Exception_ValidatorHandlerMismatch);
}
webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate;
return handler;
}
}
}

View File

@@ -0,0 +1,102 @@
using System;
using System.Net.Http;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using System.Collections.Generic;
namespace Owin.Security.Providers.Baidu
{
public class BaiduAuthenticationOptions : AuthenticationOptions
{
/// <summary>
/// Gets or sets the a pinned certificate validator to use to validate the endpoints used
/// in back channel communications belong to Baidu
/// </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 Baidu.
/// 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 Baidu.
/// </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-dropbox".
/// </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 Baidu supplied API Key
/// </summary>
public string ApiKey { get; set; }
/// <summary>
/// Gets or sets the Baidu supplied Secret Key
/// </summary>
public string SecretKey { get; set; }
/// <summary>
/// A list of permissions to request.
/// </summary>
public IList<string> Scope { get; private set; }
/// <summary>
/// Gets or sets the <see cref="IBaiduAuthenticationProvider" /> used in the authentication events
/// </summary>
public IBaiduAuthenticationProvider Provider { 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="BaiduAuthenticationOptions" />
/// </summary>
public BaiduAuthenticationOptions()
: base("Baidu")
{
Caption = Constants.DefaultAuthenticationType;
CallbackPath = new PathString("/signin-baidu");
AuthenticationMode = AuthenticationMode.Passive;
Scope = new List<string>
{
"basic"
};
BackchannelTimeout = TimeSpan.FromSeconds(60);
}
}
}

View File

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

View File

@@ -0,0 +1,102 @@
<?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>{E2759807-4D7C-4288-AAC8-F5B7B4616680}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Owin.Security.Providers.Baidu</RootNamespace>
<AssemblyName>Owin.Security.Providers.Baidu</AssemblyName>
<TargetFrameworkVersion>v4.5</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="BaiduAuthenticationExtensions.cs" />
<Compile Include="BaiduAuthenticationHandler.cs" />
<Compile Include="BaiduAuthenticationMiddleware.cs" />
<Compile Include="BaiduAuthenticationOptions.cs" />
<Compile Include="Provider\BaiduAuthenticatedContext.cs" />
<Compile Include="Provider\BaiduAuthenticationProvider.cs" />
<Compile Include="Provider\BaiduReturnEndpointContext.cs" />
<Compile Include="Provider\IBaiduAuthenticationProvider.cs" />
<Compile Include="Resources.Designer.cs">
<DependentUpon>Resources.resx</DependentUpon>
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</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.Baidu")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Owin.Security.Providers.Baidu")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("AD2018C1-FA82-4DC3-8CD4-BCC4D232A678")]
[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("2.0.0.0")]

View File

@@ -0,0 +1,77 @@
// 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;
namespace Owin.Security.Providers.Baidu
{
/// <summary>
/// Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.
/// </summary>
public class BaiduAuthenticatedContext : BaseContext
{
/// <summary>
/// Initializes a <see cref="BaiduAuthenticatedContext"/>
/// </summary>
/// <param name="context">The OWIN environment</param>
/// <param name="user">The JSON-serialized user</param>
/// <param name="accessToken">Baidu Access token</param>
public BaiduAuthenticatedContext(IOwinContext context, JObject user, string accessToken)
: base(context)
{
AccessToken = accessToken;
User = user;
Userid = TryGetValue(user, "userid");
UserName = TryGetValue(user, "username");
RealName = TryGetValue(user, "realname");
}
/// <summary>
/// Gets the JSON-serialized user
/// </summary>
/// <remarks>
/// Contains the Baidu user obtained from the endpoint https://api.dropbox.com/1/account/info
/// </remarks>
public JObject User { get; private set; }
/// <summary>
/// Gets the Baidu OAuth access token
/// </summary>
public string AccessToken { get; private set; }
/// <summary>
/// Gets the Baidu user ID
/// </summary>
public string Userid { get; private set; }
/// <summary>
/// The name of the user
/// </summary>
public string UserName { get; private set; }
/// <summary>
/// The real name of the user
/// </summary>
public string RealName { get; private 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.Baidu
{
/// <summary>
/// Default <see cref="IBaiduAuthenticationProvider"/> implementation.
/// </summary>
public class BaiduAuthenticationProvider : IBaiduAuthenticationProvider
{
/// <summary>
/// Initializes a <see cref="BaiduAuthenticationProvider"/>
/// </summary>
public BaiduAuthenticationProvider()
{
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<BaiduAuthenticatedContext, Task> OnAuthenticated { get; set; }
/// <summary>
/// Gets or sets the function that is invoked when the ReturnEndpoint method is invoked.
/// </summary>
public Func<BaiduReturnEndpointContext, Task> OnReturnEndpoint { get; set; }
/// <summary>
/// Invoked whenever Baidu 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(BaiduAuthenticatedContext 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(BaiduReturnEndpointContext 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.Baidu
{
/// <summary>
/// Provides context information to middleware providers.
/// </summary>
public class BaiduReturnEndpointContext : ReturnEndpointContext
{
/// <summary>
///
/// </summary>
/// <param name="context">OWIN environment</param>
/// <param name="ticket">The authentication ticket</param>
public BaiduReturnEndpointContext(
IOwinContext context,
AuthenticationTicket ticket)
: base(context, ticket)
{
}
}
}

View File

@@ -0,0 +1,24 @@
using System.Threading.Tasks;
namespace Owin.Security.Providers.Baidu
{
/// <summary>
/// Specifies callback methods which the <see cref="BaiduAuthenticationMiddleware"></see> invokes to enable developer control over the authentication process. />
/// </summary>
public interface IBaiduAuthenticationProvider
{
/// <summary>
/// Invoked whenever Baidu 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(BaiduAuthenticatedContext 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(BaiduReturnEndpointContext 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.Baidu {
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)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Owin.Security.Providers.Baidu.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="net45" />
<package id="Microsoft.Owin.Security" version="3.0.1" targetFramework="net45" />
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net45" />
<package id="Owin" version="1.0" targetFramework="net45" />
</packages>

View File

@@ -0,0 +1,29 @@
using System;
namespace Owin.Security.Providers.Box
{
public static class BoxAuthenticationExtensions
{
public static IAppBuilder UseBoxAuthentication(this IAppBuilder app,
BoxAuthenticationOptions options)
{
if (app == null)
throw new ArgumentNullException(nameof(app));
if (options == null)
throw new ArgumentNullException(nameof(options));
app.Use(typeof(BoxAuthenticationMiddleware), app, options);
return app;
}
public static IAppBuilder UseBoxAuthentication(this IAppBuilder app, string appKey, string appSecret)
{
return app.UseBoxAuthentication(new BoxAuthenticationOptions
{
ClientId = appKey,
ClientSecret = appSecret
});
}
}
}

View File

@@ -0,0 +1,230 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Owin;
using Microsoft.Owin.Infrastructure;
using Microsoft.Owin.Logging;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Net.Http.Headers;
namespace Owin.Security.Providers.Box
{
public class BoxAuthenticationHandler : AuthenticationHandler<BoxAuthenticationOptions>
{
private const string StateCookie = "_BoxState";
private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
private const string TokenEndpoint = "https://api.box.com/oauth2/token";
private const string UserInfoEndpoint = "https://api.box.com/2.0/users/me";
private readonly ILogger _logger;
private readonly HttpClient _httpClient;
public BoxAuthenticationHandler(HttpClient httpClient, ILogger logger)
{
_httpClient = httpClient;
_logger = logger;
}
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
AuthenticationProperties properties = null;
try
{
string code = null;
var query = Request.Query;
var values = query.GetValues("code");
if (values != null && values.Count == 1)
{
code = values[0];
}
var state = Request.Cookies[StateCookie];
properties = Options.StateDataFormat.Unprotect(state);
if (properties == null)
{
return null;
}
// OAuth2 10.12 CSRF
if (!ValidateCorrelationId(properties, _logger))
{
return new AuthenticationTicket(null, properties);
}
// Check for error
if (Request.Query.Get("error") != null)
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)
};
// Request the token
var tokenResponse =
await _httpClient.PostAsync(TokenEndpoint, new FormUrlEncodedContent(body));
tokenResponse.EnsureSuccessStatusCode();
var text = await tokenResponse.Content.ReadAsStringAsync();
// Deserializes the token response
dynamic response = JsonConvert.DeserializeObject<dynamic>(text);
var accessToken = (string)response.access_token;
// Get the Box user
// https://docs.box.com/reference#get-the-current-users-information
var userRequest = new HttpRequestMessage(HttpMethod.Get, UserInfoEndpoint);
userRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
userRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var userResponse = await _httpClient.SendAsync(userRequest, Request.CallCancelled);
userResponse.EnsureSuccessStatusCode();
text = await userResponse.Content.ReadAsStringAsync();
var user = JObject.Parse(text);
var context = new BoxAuthenticatedContext(Context, user, accessToken)
{
Identity = new ClaimsIdentity(
Options.AuthenticationType,
ClaimsIdentity.DefaultNameClaimType,
ClaimsIdentity.DefaultRoleClaimType)
};
if (!string.IsNullOrEmpty(context.Id))
{
context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.Id, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(context.Name))
{
context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.Name, XmlSchemaString, Options.AuthenticationType));
}
context.Properties = properties;
await Options.Provider.Authenticated(context);
return new AuthenticationTicket(context.Identity, context.Properties);
}
catch (Exception ex)
{
_logger.WriteError(ex.Message);
}
return new AuthenticationTicket(null, properties);
}
protected override Task ApplyResponseChallengeAsync()
{
if (Response.StatusCode != 401)
{
return Task.FromResult<object>(null);
}
var challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode);
if (challenge == null) return Task.FromResult<object>(null);
var baseUri =
Request.Scheme +
Uri.SchemeDelimiter +
Request.Host +
Request.PathBase;
var currentUri =
baseUri +
Request.Path +
Request.QueryString;
var redirectUri =
baseUri +
Options.CallbackPath;
var properties = challenge.Properties;
if (string.IsNullOrEmpty(properties.RedirectUri))
{
properties.RedirectUri = currentUri;
}
// OAuth2 10.12 CSRF
GenerateCorrelationId(properties);
var authorizationEndpoint =
"https://api.box.com/oauth2/authorize" +
"?response_type=code" +
"&client_id=" + Uri.EscapeDataString(Options.ClientId) +
"&state=authenticated" +
"&redirect_uri=" + Uri.EscapeDataString(redirectUri);
var cookieOptions = new CookieOptions
{
HttpOnly = true,
Secure = Request.IsSecure
};
Response.StatusCode = 302;
Response.Cookies.Append(StateCookie, Options.StateDataFormat.Protect(properties), cookieOptions);
Response.Headers.Set("Location", 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 BoxReturnEndpointContext(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,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.Box
{
public class BoxAuthenticationMiddleware : AuthenticationMiddleware<BoxAuthenticationOptions>
{
private readonly HttpClient _httpClient;
private readonly ILogger _logger;
public BoxAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app,
BoxAuthenticationOptions options)
: base(next, options)
{
if (string.IsNullOrWhiteSpace(Options.ClientId))
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
Resources.Exception_OptionMustBeProvided, nameof(Options.ClientId)));
if (string.IsNullOrWhiteSpace(Options.ClientSecret))
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
Resources.Exception_OptionMustBeProvided, nameof(Options.ClientSecret)));
_logger = app.CreateLogger<BoxAuthenticationMiddleware>();
if (Options.Provider == null)
Options.Provider = new BoxAuthenticationProvider();
if (Options.StateDataFormat == null)
{
var dataProtector = app.CreateDataProtector(
typeof (BoxAuthenticationMiddleware).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.Box.BoxAuthenticationOptions" /> supplied to the constructor.
/// </returns>
protected override AuthenticationHandler<BoxAuthenticationOptions> CreateHandler()
{
return new BoxAuthenticationHandler(_httpClient, _logger);
}
private static HttpMessageHandler ResolveHttpMessageHandler(BoxAuthenticationOptions options)
{
var handler = options.BackchannelHttpHandler ?? new WebRequestHandler();
// If they provided a validator, apply it or fail.
if (options.BackchannelCertificateValidator == null) return handler;
// Set the cert validate callback
var webRequestHandler = handler as WebRequestHandler;
if (webRequestHandler == null)
{
throw new InvalidOperationException(Resources.Exception_ValidatorHandlerMismatch);
}
webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate;
return handler;
}
}
}

View File

@@ -0,0 +1,92 @@
using System;
using System.Net.Http;
using Microsoft.Owin;
using Microsoft.Owin.Security;
namespace Owin.Security.Providers.Box
{
public class BoxAuthenticationOptions : AuthenticationOptions
{
/// <summary>
/// Gets or sets the a pinned certificate validator to use to validate the endpoints used
/// in back channel communications belong to Box
/// </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 Box.
/// 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 Box.
/// </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-box".
/// </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 Box supplied Client ID
/// </summary>
public string ClientId { get; set; }
/// <summary>
/// Gets or sets the Box supplied Client Secret
/// </summary>
public string ClientSecret { get; set; }
/// <summary>
/// Gets or sets the <see cref="IBoxAuthenticationProvider" /> used in the authentication events
/// </summary>
public IBoxAuthenticationProvider Provider { 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="BoxAuthenticationOptions" />
/// </summary>
public BoxAuthenticationOptions()
: base("Box")
{
Caption = Constants.DefaultAuthenticationType;
CallbackPath = new PathString("/signin-box");
AuthenticationMode = AuthenticationMode.Passive;
BackchannelTimeout = TimeSpan.FromSeconds(60);
}
}
}

View File

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

View File

@@ -0,0 +1,102 @@
<?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>{1AEF8813-E1F9-41E1-BC8D-732960595E9F}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Owin.Security.Providers.Box</RootNamespace>
<AssemblyName>Owin.Security.Providers.Box</AssemblyName>
<TargetFrameworkVersion>v4.5</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="BoxAuthenticationExtensions.cs" />
<Compile Include="BoxAuthenticationHandler.cs" />
<Compile Include="BoxAuthenticationMiddleware.cs" />
<Compile Include="BoxAuthenticationOptions.cs" />
<Compile Include="Provider\BoxAuthenticatedContext.cs" />
<Compile Include="Provider\BoxAuthenticationProvider.cs" />
<Compile Include="Provider\BoxReturnEndpointContext.cs" />
<Compile Include="Provider\IBoxAuthenticationProvider.cs" />
<Compile Include="Resources.Designer.cs">
<DependentUpon>Resources.resx</DependentUpon>
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</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.Box")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Owin.Security.Providers.Box")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("1242F9A5-AEF8-44F8-A255-F0D24DAE0A2C")]
[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("2.0.0.0")]

View File

@@ -0,0 +1,77 @@
// 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;
namespace Owin.Security.Providers.Box
{
/// <summary>
/// Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.
/// </summary>
public class BoxAuthenticatedContext : BaseContext
{
/// <summary>
/// Initializes a <see cref="BoxAuthenticatedContext"/>
/// </summary>
/// <param name="context">The OWIN environment</param>
/// <param name="user">The JSON-serialized user</param>
/// <param name="accessToken">Box Access token</param>
public BoxAuthenticatedContext(IOwinContext context, JObject user, string accessToken)
: base(context)
{
AccessToken = accessToken;
User = user;
Id = TryGetValue(user, "id");
Name = TryGetValue(user, "name");
Login = TryGetValue(user, "login");
}
/// <summary>
/// Gets the JSON-serialized user
/// </summary>
/// <remarks>
/// Contains the Box user obtained from the endpoint https://api.box.com/2.0/users/me
/// </remarks>
public JObject User { get; private set; }
/// <summary>
/// Gets the Box OAuth access token
/// </summary>
public string AccessToken { get; private set; }
/// <summary>
/// Gets the Box user ID
/// </summary>
public string Id { get; private set; }
/// <summary>
/// The name of the user
/// </summary>
public string Name { get; private set; }
/// <summary>
/// The login of the user
/// </summary>
public string Login { get; private 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.Box
{
/// <summary>
/// Default <see cref="IBoxAuthenticationProvider"/> implementation.
/// </summary>
public class BoxAuthenticationProvider : IBoxAuthenticationProvider
{
/// <summary>
/// Initializes a <see cref="BoxAuthenticationProvider"/>
/// </summary>
public BoxAuthenticationProvider()
{
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<BoxAuthenticatedContext, Task> OnAuthenticated { get; set; }
/// <summary>
/// Gets or sets the function that is invoked when the ReturnEndpoint method is invoked.
/// </summary>
public Func<BoxReturnEndpointContext, Task> OnReturnEndpoint { get; set; }
/// <summary>
/// Invoked whenever Box 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(BoxAuthenticatedContext 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(BoxReturnEndpointContext 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.Box
{
/// <summary>
/// Provides context information to middleware providers.
/// </summary>
public class BoxReturnEndpointContext : ReturnEndpointContext
{
/// <summary>
///
/// </summary>
/// <param name="context">OWIN environment</param>
/// <param name="ticket">The authentication ticket</param>
public BoxReturnEndpointContext(
IOwinContext context,
AuthenticationTicket ticket)
: base(context, ticket)
{
}
}
}

View File

@@ -0,0 +1,24 @@
using System.Threading.Tasks;
namespace Owin.Security.Providers.Box
{
/// <summary>
/// Specifies callback methods which the <see cref="BoxAuthenticationMiddleware"></see> invokes to enable developer control over the authentication process. />
/// </summary>
public interface IBoxAuthenticationProvider
{
/// <summary>
/// Invoked whenever Box 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(BoxAuthenticatedContext 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(BoxReturnEndpointContext 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.Box {
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)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Owin.Security.Providers.Box.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="net45" />
<package id="Microsoft.Owin.Security" version="3.0.1" targetFramework="net45" />
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net45" />
<package id="Owin" version="1.0" targetFramework="net45" />
</packages>

View File

@@ -17,8 +17,9 @@ namespace Owin.Security.Providers.Dropbox
{
private const string StateCookie = "_DropboxState";
private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
private const string TokenEndpoint = "https://api.dropbox.com/1/oauth2/token";
private const string UserInfoEndpoint = "https://api.dropbox.com/1/account/info";
private const string TokenEndpoint = "https://api.dropbox.com/oauth2/token";
private const string UserInfoEndpoint = "https://api.dropbox.com/2/users/get_current_account";
private readonly ILogger _logger;
private readonly HttpClient _httpClient;
@@ -65,13 +66,13 @@ namespace Owin.Security.Providers.Dropbox
var redirectUri = requestPrefix + Request.PathBase + Options.CallbackPath;
// Build up the body for the token request
var body = new List<KeyValuePair<string, string>>
var body = new Dictionary<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.AppKey),
new KeyValuePair<string, string>("client_secret", Options.AppSecret)
{ "grant_type", "authorization_code" },
{ "code", code },
{"redirect_uri", redirectUri },
{ "client_id", Options.AppKey },
{ "client_secret", Options.AppSecret }
};
// Request the token
@@ -85,8 +86,8 @@ namespace Owin.Security.Providers.Dropbox
var accessToken = (string)response.access_token;
// Get the Dropbox user
var graphResponse = await _httpClient.GetAsync(
UserInfoEndpoint + "?access_token=" + Uri.EscapeDataString(accessToken), Request.CallCancelled);
_httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
var graphResponse = await _httpClient.PostAsync(UserInfoEndpoint, null);
graphResponse.EnsureSuccessStatusCode();
text = await graphResponse.Content.ReadAsStringAsync();
var user = JObject.Parse(text);
@@ -154,7 +155,7 @@ namespace Owin.Security.Providers.Dropbox
GenerateCorrelationId(properties);
var authorizationEndpoint =
"https://www.dropbox.com/1/oauth2/authorize" +
"https://www.dropbox.com/oauth2/authorize" +
"?response_type=code" +
"&client_id=" + Uri.EscapeDataString(Options.AppKey) +
"&redirect_uri=" + Uri.EscapeDataString(redirectUri);

View File

@@ -25,7 +25,7 @@ namespace Owin.Security.Providers.Dropbox
AccessToken = accessToken;
User = user;
Id = TryGetValue(user, "uid");
Id = TryGetValue(user, "account_id");
Name = TryGetValue(user, "display_name");
}

View File

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

View File

@@ -0,0 +1,29 @@
using System;
namespace Owin.Security.Providers.Evernote
{
public static class EvernoteAuthenticationExtensions
{
public static IAppBuilder UseEvernoteAuthentication(this IAppBuilder app,
EvernoteAuthenticationOptions options)
{
if (app == null)
throw new ArgumentNullException(nameof(app));
if (options == null)
throw new ArgumentNullException(nameof(options));
app.Use(typeof(EvernoteAuthenticationMiddleware), app, options);
return app;
}
public static IAppBuilder UseEvernoteAuthentication(this IAppBuilder app, string appKey, string appSecret)
{
return app.UseEvernoteAuthentication(new EvernoteAuthenticationOptions
{
AppKey = appKey,
AppSecret = appSecret
});
}
}
}

View File

@@ -0,0 +1,379 @@
using EvernoteSDK;
using Microsoft.Owin;
using Microsoft.Owin.Infrastructure;
using Microsoft.Owin.Logging;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;
using Owin.Security.Providers.Evernote.Messages;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Net.Http;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using System.Web;
namespace Owin.Security.Providers.Evernote
{
public class EvernoteAuthenticationHandler : AuthenticationHandler<EvernoteAuthenticationOptions>
{
private const string StateCookie = "_EvernoteState";
private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
private const string RequestTokenEndpoint = "/oauth";
private const string AuthenticationEndpoint = "/OAuth.action?oauth_token=";
private const string AccessTokenEndpoint = "/oauth";
private const string ProductionBase = "https://www.evernote.com";
private const string SandBoxBase = "https://sandbox.evernote.com";
public string BaseUri => Options.IsSandBox ? SandBoxBase : ProductionBase;
private readonly ILogger _logger;
private readonly HttpClient _httpClient;
public EvernoteAuthenticationHandler(HttpClient httpClient, ILogger logger)
{
_httpClient = httpClient;
_logger = logger;
_logger.WriteInformation("Authentication Handler initialized");
}
public override async Task<bool> InvokeAsync()
{
if (Options.CallbackPath.HasValue && Options.CallbackPath == Request.Path)
{
return await InvokeReturnPathAsync();
}
return false;
}
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
AuthenticationProperties properties = null;
try
{
var query = Request.Query;
var protectedRequestToken = Request.Cookies[StateCookie];
var requestToken = Options.StateDataFormat.Unprotect(protectedRequestToken);
if (requestToken == null)
{
_logger.WriteWarning("Invalid state");
return null;
}
properties = requestToken.Properties;
var returnedToken = query.Get("oauth_token");
if (string.IsNullOrWhiteSpace(returnedToken))
{
_logger.WriteWarning("Missing oauth_token");
return new AuthenticationTicket(null, properties);
}
if (returnedToken != requestToken.Token)
{
_logger.WriteWarning("Unmatched token");
return new AuthenticationTicket(null, properties);
}
var oauthVerifier = query.Get("oauth_verifier");
if (string.IsNullOrWhiteSpace(oauthVerifier))
{
_logger.WriteWarning("Missing or blank oauth_verifier");
return new AuthenticationTicket(null, properties);
}
// Retrieve access token
var accessToken = await ObtainAccessTokenAsync(Options.AppKey, Options.AppSecret, requestToken, oauthVerifier);
// Retrieve user infos
var userName = GetUserInfos(accessToken);
var context = new EvernoteAuthenticatedContext(Context, accessToken)
{
Identity = new ClaimsIdentity(
Options.AuthenticationType,
ClaimsIdentity.DefaultNameClaimType,
ClaimsIdentity.DefaultRoleClaimType)
};
if (!string.IsNullOrEmpty(context.UserId))
{
context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.UserId, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(userName))
{
context.Identity.AddClaim(new Claim(ClaimTypes.Name, userName, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(context.NoteStoreUrl))
{
context.Identity.AddClaim(new Claim("urn:evernote:notestoreuri", context.NoteStoreUrl, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(context.AccessToken))
{
context.Identity.AddClaim(new Claim("urn:evernote:accesstoken", context.AccessToken, XmlSchemaString, Options.AuthenticationType));
}
context.Properties = requestToken.Properties;
Response.Cookies.Delete(StateCookie);
await Options.Provider.Authenticated(context);
return new AuthenticationTicket(context.Identity, context.Properties);
}
catch (Exception ex)
{
_logger.WriteError("Authentication failed", ex);
return new AuthenticationTicket(null, properties);
}
}
private string GetUserInfos(AccessToken accessToken)
{
ENSession.SetSharedSessionDeveloperToken(accessToken.Token, accessToken.NoteStoreUrl);
var session = new ENSession();
session.PerformPostAuthentication();
return session.UserDisplayName;
}
protected override async Task ApplyResponseChallengeAsync()
{
_logger.WriteInformation("Applying response challenge");
if (Response.StatusCode != 401)
{
return;
}
var challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode);
if (challenge == null) return;
var baseUri =
Request.Scheme +
Uri.SchemeDelimiter +
Request.Host +
Request.PathBase;
var callBackUri = baseUri + Options.CallbackPath;
var properties = challenge.Properties;
if (string.IsNullOrEmpty(properties.RedirectUri))
{
properties.RedirectUri = baseUri + Request.Path + Request.QueryString;
}
_logger.WriteInformation("Requesting Token...");
var requestToken = await ObtainRequestTokenAsync(Options.AppKey, Options.AppSecret, callBackUri, properties);
_logger.WriteInformation("Token request successfull. Token: " + requestToken.Token);
var authorizationEndpoint = BaseUri + AuthenticationEndpoint + requestToken.Token;
if (requestToken.CallbackConfirmed)
{
var cookieOptions = new CookieOptions
{
HttpOnly = true,
Secure = Request.IsSecure
};
Response.StatusCode = 302;
Response.Cookies.Append(StateCookie, Options.StateDataFormat.Protect(requestToken), cookieOptions);
Response.Headers.Set("Location", authorizationEndpoint);
}
else
{
_logger.WriteError("requestToken CallbackConfirmed!=true");
}
}
private async Task<RequestToken> ObtainRequestTokenAsync(string appKey, string appSecret, string callBackUri, AuthenticationProperties properties)
{
string normalizedUrl;
string normalizedRequestParameters;
var oauthBase = new OAuthBase();
var url = new Uri(BaseUri + RequestTokenEndpoint);
var signature = oauthBase.GenerateSignature(
url,
appKey,
appSecret,
null,
null,
"GET",
oauthBase.GenerateTimeStamp(),
oauthBase.GenerateNonce(),
callBackUri, out normalizedUrl, out normalizedRequestParameters);
normalizedRequestParameters = normalizedRequestParameters + "&oauth_signature=" + HttpUtility.UrlEncode(signature);
if (normalizedRequestParameters.Length > 0)
normalizedUrl += "?";
var authorizationEndpoint = normalizedUrl + normalizedRequestParameters;
_logger.WriteInformation("Url =" + authorizationEndpoint);
string query = await WebRequestAsync(HttpMethod.Get, authorizationEndpoint, null);
if (query?.Length > 0)
{
NameValueCollection queryString = HttpUtility.ParseQueryString(query);
if (queryString["oauth_token"] != null)
{
_logger.WriteInformation("Retrieving data...");
return new RequestToken
{
Token = Uri.UnescapeDataString(queryString["oauth_token"]),
CallbackConfirmed = true,
Properties = properties
};
}
}
return new RequestToken();
}
private async Task<bool> InvokeReturnPathAsync()
{
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 EvernoteReturnEndpointContext(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;
}
private async Task<AccessToken> ObtainAccessTokenAsync(string appKey, string appSecret, RequestToken token, string verifier)
{
string normalizedUrl;
string normalizedRequestParameters;
var oauthBase = new OAuthBase();
var url = new Uri(BaseUri + AccessTokenEndpoint);
var signature = oauthBase.GenerateSignature(
url,
appKey,
appSecret,
token.Token,
verifier,
"POST",
oauthBase.GenerateTimeStamp(),
oauthBase.GenerateNonce(),
"",
out normalizedUrl, out normalizedRequestParameters);
var postData = normalizedRequestParameters + "&oauth_signature=" + HttpUtility.UrlEncode(signature);
var authorizationParts = new SortedDictionary<string, string>();
foreach (var key in postData.Split('&'))
{
authorizationParts.Add(key.Split('=')[0], key.Split('=')[1]);
}
var authorizationHeaderBuilder = new StringBuilder();
authorizationHeaderBuilder.Append("OAuth ");
foreach (var authorizationPart in authorizationParts)
{
authorizationHeaderBuilder.AppendFormat(
"{0}=\"{1}\", ", authorizationPart.Key, Uri.EscapeDataString(authorizationPart.Value));
}
authorizationHeaderBuilder.Length = authorizationHeaderBuilder.Length - 2;
string query = await WebRequestAsync(HttpMethod.Post, normalizedUrl, authorizationHeaderBuilder.ToString());
if (query.Length > 0)
{
var responseParameters = HttpUtility.ParseQueryString(query);
if (responseParameters["oauth_token"] != null)
{
return new AccessToken
{
Token = Uri.UnescapeDataString(responseParameters["oauth_token"]),
Shard = Uri.UnescapeDataString(responseParameters["edam_shard"]),
UserId = Uri.UnescapeDataString(responseParameters["edam_userId"]),
NoteStoreUrl = Uri.UnescapeDataString(responseParameters["edam_noteStoreUrl"]),
WebApiUrlPrefix = Uri.UnescapeDataString(responseParameters["edam_webApiUrlPrefix"]),
};
}
}
return new AccessToken();
}
private Task<string> WebRequestAsync(HttpMethod method, string url, string postData)
{
try
{
var request = new HttpRequestMessage(method, url);
if (method == HttpMethod.Post)
request.Headers.Add("Authorization", postData);
_logger.WriteInformation("Send request...");
var response = _httpClient.SendAsync(request, Request.CallCancelled).Result;
_logger.WriteInformation("Request ended");
response.EnsureSuccessStatusCode();
return response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
_logger.WriteError(ex.Message, ex);
}
return null;
}
}
}

View File

@@ -0,0 +1,87 @@
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.DataHandler.Encoder;
using Microsoft.Owin.Security.DataProtection;
using Microsoft.Owin.Security.Infrastructure;
using Owin.Security.Providers.Evernote.Messages;
namespace Owin.Security.Providers.Evernote
{
public class EvernoteAuthenticationMiddleware : AuthenticationMiddleware<EvernoteAuthenticationOptions>
{
private readonly HttpClient _httpClient;
private readonly ILogger _logger;
public EvernoteAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app,
EvernoteAuthenticationOptions options)
: base(next, options)
{
if (string.IsNullOrWhiteSpace(Options.AppKey))
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
Resources.Exception_OptionMustBeProvided, "AppKey"));
if (string.IsNullOrWhiteSpace(Options.AppSecret))
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
Resources.Exception_OptionMustBeProvided, "AppSecret"));
_logger = app.CreateLogger<EvernoteAuthenticationMiddleware>();
if (Options.Provider == null)
Options.Provider = new EvernoteAuthenticationProvider();
if (Options.StateDataFormat == null)
{
var dataProtector = app.CreateDataProtector(
typeof (EvernoteAuthenticationMiddleware).FullName,
Options.AuthenticationType, "v1");
Options.StateDataFormat = new SecureDataFormat<RequestToken>(
Serializers.RequestToken,
dataProtector,
TextEncodings.Base64Url);
}
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.Evernote.EvernoteAuthenticationOptions" /> supplied to the constructor.
/// </returns>
protected override AuthenticationHandler<EvernoteAuthenticationOptions> CreateHandler()
{
return new EvernoteAuthenticationHandler(_httpClient, _logger);
}
private static HttpMessageHandler ResolveHttpMessageHandler(EvernoteAuthenticationOptions options)
{
var handler = options.BackchannelHttpHandler ?? new WebRequestHandler();
// If they provided a validator, apply it or fail.
if (options.BackchannelCertificateValidator == null) return handler;
// Set the cert validate callback
var webRequestHandler = handler as WebRequestHandler;
if (webRequestHandler == null)
{
throw new InvalidOperationException(Resources.Exception_ValidatorHandlerMismatch);
}
webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate;
return handler;
}
}
}

View File

@@ -0,0 +1,98 @@
using System;
using System.Net.Http;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Owin.Security.Providers.Evernote.Messages;
namespace Owin.Security.Providers.Evernote
{
public class EvernoteAuthenticationOptions : AuthenticationOptions
{
/// <summary>
/// Gets or sets the a pinned certificate validator to use to validate the endpoints used
/// in back channel communications belong to Evernote
/// </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 Evernote.
/// 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 Evernote.
/// </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-Evernote".
/// </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 Evernote supplied Application Key
/// </summary>
public string AppKey { get; set; }
/// <summary>
/// Gets or sets the Evernote supplied Application Secret
/// </summary>
public string AppSecret { get; set; }
/// <summary>
/// Gets or sets the Evernote sandbox mode
/// </summary>
public bool IsSandBox { get; set; }
/// <summary>
/// Gets or sets the <see cref="IEvernoteAuthenticationProvider" /> used in the authentication events
/// </summary>
public IEvernoteAuthenticationProvider Provider { 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<RequestToken> StateDataFormat { get; set; }
/// <summary>
/// Initializes a new <see cref="EvernoteAuthenticationOptions" />
/// </summary>
public EvernoteAuthenticationOptions()
: base("Evernote")
{
Caption = Constants.DefaultAuthenticationType;
CallbackPath = new PathString("/signin-evernote");
AuthenticationMode = AuthenticationMode.Passive;
BackchannelTimeout = TimeSpan.FromSeconds(60);
}
}
}

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.
namespace Owin.Security.Providers.Evernote.Messages
{
/// <summary>
/// Evernote access token
/// </summary>
public class AccessToken : RequestToken
{
/// <summary>
/// Gets or sets the Evernote User ID
/// </summary>
public string UserId { get; set; }
/// <summary>
/// Gets or sets the Evernote User Name
/// </summary>
public string UserName { get; set; }
public string Shard { get; set; }
public string NoteStoreUrl { get; set; }
public string WebApiUrlPrefix { get; set; }
}
}

View File

@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using Microsoft.Owin.Security;
namespace Owin.Security.Providers.Evernote.Messages
{
/// <summary>
/// Yahoo request token
/// </summary>
public class RequestToken
{
/// <summary>
/// Gets or sets the Yahoo token
/// </summary>
public string Token { get; set; }
public bool CallbackConfirmed { get; set; }
/// <summary>
/// Gets or sets a property bag for common authentication properties
/// </summary>
public AuthenticationProperties Properties { get; set; }
}
}

View File

@@ -0,0 +1,103 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Microsoft.Owin.Security.DataHandler.Serializer;
namespace Owin.Security.Providers.Evernote.Messages
{
/// <summary>
/// Serializes and deserializes Yahoo request and access tokens so that they can be used by other application components.
/// </summary>
public class RequestTokenSerializer : IDataSerializer<RequestToken>
{
private const int FormatVersion = 1;
/// <summary>
/// Serialize a request token
/// </summary>
/// <param name="model">The token to serialize</param>
/// <returns>A byte array containing the serialized token</returns>
[SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "Dispose is idempotent")]
public virtual byte[] Serialize(RequestToken model)
{
using (var memory = new MemoryStream())
{
using (var writer = new BinaryWriter(memory))
{
Write(writer, model);
writer.Flush();
return memory.ToArray();
}
}
}
/// <summary>
/// Deserializes a request token
/// </summary>
/// <param name="data">A byte array containing the serialized token</param>
/// <returns>The Yahoo request token</returns>
[SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "Dispose is idempotent")]
public virtual RequestToken Deserialize(byte[] data)
{
using (var memory = new MemoryStream(data))
{
using (var reader = new BinaryReader(memory))
{
return Read(reader);
}
}
}
/// <summary>
/// Writes a Yahoo request token as a series of bytes. Used by the <see cref="Serialize"/> method.
/// </summary>
/// <param name="writer">The writer to use in writing the token</param>
/// <param name="token">The token to write</param>
public static void Write(BinaryWriter writer, RequestToken token)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
writer.Write(FormatVersion);
writer.Write(token.Token);
writer.Write(token.CallbackConfirmed);
PropertiesSerializer.Write(writer, token.Properties);
}
/// <summary>
/// Reads a Yahoo request token from a series of bytes. Used by the <see cref="Deserialize"/> method.
/// </summary>
/// <param name="reader">The reader to use in reading the token bytes</param>
/// <returns>The token</returns>
public static RequestToken Read(BinaryReader reader)
{
if (reader == null)
{
throw new ArgumentNullException(nameof(reader));
}
if (reader.ReadInt32() != FormatVersion)
{
return null;
}
var token = reader.ReadString();
var callbackConfirmed = reader.ReadBoolean();
var properties = PropertiesSerializer.Read(reader);
if (properties == null)
{
return null;
}
return new RequestToken { Token = token, CallbackConfirmed = callbackConfirmed, Properties = properties };
}
}
}

View File

@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using Microsoft.Owin.Security.DataHandler.Serializer;
namespace Owin.Security.Providers.Evernote.Messages
{
/// <summary>
/// Provides access to a request token serializer
/// </summary>
public static class Serializers
{
static Serializers()
{
RequestToken = new RequestTokenSerializer();
}
/// <summary>
/// Gets or sets a statically-avaliable serializer object. The value for this property will be <see cref="RequestTokenSerializer"/> by default.
/// </summary>
public static IDataSerializer<RequestToken> RequestToken { get; set; }
}
}

View File

@@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Web;
namespace Owin.Security.Providers.Evernote
{
internal sealed class OAuthBase
{
private readonly Random _random = new Random();
private readonly string _unreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
private const string OAuthConsumerKey = "oauth_consumer_key";
private const string OAuthCallbackKey = "oauth_callback";
private const string OAuthSignatureMethodKey = "oauth_signature_method";
private const string OAuthTimestampKey = "oauth_timestamp";
private const string OAuthNonceKey = "oauth_nonce";
private const string OAuthTokenKey = "oauth_token";
private const string OAuthVerifierKey = "oauth_verifier";
private const string Hmacsha1SignatureType = "HMAC-SHA1";
private const string PlainTextSignatureType = "PLAINTEXT";
private string ComputeHash(HashAlgorithm hashAlgorithm, string data)
{
if (hashAlgorithm == null)
throw new ArgumentNullException("hashAlgorithm");
if (string.IsNullOrEmpty(data))
throw new ArgumentNullException("data");
byte[] bytes = Encoding.ASCII.GetBytes(data);
return Convert.ToBase64String(hashAlgorithm.ComputeHash(bytes));
}
private List<QueryParameter> GetQueryParameters(string parameters)
{
if (parameters.StartsWith("?"))
parameters = parameters.Remove(0, 1);
List<QueryParameter> queryParameterList = new List<QueryParameter>();
if (!string.IsNullOrEmpty(parameters))
{
string str = parameters;
char[] chArray = { '&' };
foreach (string name in str.Split(chArray))
{
if (!string.IsNullOrEmpty(name) && !name.StartsWith("oauth_"))
{
if (name.IndexOf('=') > -1)
{
string[] strArray = name.Split('=');
queryParameterList.Add(new QueryParameter(strArray[0], strArray[1]));
}
else
queryParameterList.Add(new QueryParameter(name, string.Empty));
}
}
}
return queryParameterList;
}
private string UrlEncode(string value)
{
var stringBuilder = new StringBuilder();
foreach (char ch in value)
{
if (_unreservedChars.IndexOf(ch) != -1)
stringBuilder.Append(ch);
else
stringBuilder.Append(37 + $"{(int) ch:X2}");
}
return stringBuilder.ToString();
}
private string NormalizeRequestParameters(IList<QueryParameter> parameters)
{
StringBuilder stringBuilder = new StringBuilder();
for (int index = 0; index < parameters.Count; ++index)
{
QueryParameter queryParameter = parameters[index];
stringBuilder.AppendFormat("{0}={1}", queryParameter.Name, queryParameter.Value);
if (index < parameters.Count - 1)
stringBuilder.Append("&");
}
return stringBuilder.ToString();
}
private string GenerateSignatureBase(Uri url, string consumerKey, string token, string verifier, string httpMethod, string timeStamp, string nonce, string callback, string signatureType, out string normalizedUrl, out string normalizedRequestParameters)
{
if (token == null)
token = string.Empty;
if (string.IsNullOrEmpty(consumerKey))
throw new ArgumentNullException("consumerKey");
if (string.IsNullOrEmpty(httpMethod))
throw new ArgumentNullException("httpMethod");
if (string.IsNullOrEmpty(signatureType))
throw new ArgumentNullException("signatureType");
List<QueryParameter> queryParameters = GetQueryParameters(url.Query);
queryParameters.Add(new QueryParameter(OAuthConsumerKey, consumerKey));
queryParameters.Add(new QueryParameter(OAuthSignatureMethodKey, signatureType));
queryParameters.Add(new QueryParameter(OAuthTimestampKey, timeStamp));
queryParameters.Add(new QueryParameter(OAuthNonceKey, nonce));
if (!string.IsNullOrEmpty(callback))
queryParameters.Add(new QueryParameter(OAuthCallbackKey, callback));
if (!string.IsNullOrEmpty(token))
queryParameters.Add(new QueryParameter(OAuthTokenKey, token));
if (!string.IsNullOrEmpty(verifier))
queryParameters.Add(new QueryParameter(OAuthVerifierKey, verifier));
normalizedUrl = $"{url.Scheme}://{url.Host}";
normalizedUrl += url.AbsolutePath;
normalizedRequestParameters = NormalizeRequestParameters(queryParameters);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendFormat("{0}&", httpMethod.ToUpperInvariant());
stringBuilder.AppendFormat("{0}&", UrlEncode(normalizedUrl));
stringBuilder.AppendFormat("{0}", UrlEncode(normalizedRequestParameters));
return stringBuilder.ToString();
}
private string GenerateSignatureUsingHash(string signatureBase, HashAlgorithm hash)
{
return ComputeHash(hash, signatureBase);
}
public string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token, string verifier, string httpMethod, string timeStamp, string nonce, string callback, out string normalizedUrl, out string normalizedRequestParameters)
{
return GenerateSignature(url, consumerKey, consumerSecret, token, verifier, httpMethod, timeStamp, nonce, callback, SignatureTypes.Plaintext, out normalizedUrl, out normalizedRequestParameters);
}
public string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token, string verifier, string httpMethod, string timeStamp, string nonce, string callback, SignatureTypes signatureType, out string normalizedUrl, out string normalizedRequestParameters)
{
normalizedUrl = null;
normalizedRequestParameters = null;
switch (signatureType)
{
case SignatureTypes.Hmacsha1:
var signatureBase = GenerateSignatureBase(url, consumerKey, token, verifier, httpMethod, timeStamp, nonce, callback, Hmacsha1SignatureType, out normalizedUrl, out normalizedRequestParameters);
var hmacshA1 = new HMACSHA1
{
Key = Encoding.ASCII.GetBytes($"{UrlEncode(consumerSecret)}&{UrlEncode(verifier)}")
};
return GenerateSignatureUsingHash(signatureBase, hmacshA1);
case SignatureTypes.Plaintext:
GenerateSignatureBase(url, consumerKey, token, verifier, httpMethod, timeStamp, nonce, callback, PlainTextSignatureType, out normalizedUrl, out normalizedRequestParameters);
return HttpUtility.UrlEncode($"{consumerSecret}&{verifier}");
case SignatureTypes.Rsasha1:
throw new NotImplementedException();
default:
throw new ArgumentException(@"Unknown signature type", nameof(signatureType));
}
}
public string GenerateTimeStamp()
{
return Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds).ToString();
}
public string GenerateNonce()
{
return _random.Next(123400, 9999999).ToString();
}
private class QueryParameter
{
public string Name { get; }
public string Value { get; }
public QueryParameter(string name, string value)
{
Name = name;
Value = value;
}
}
}
}

View File

@@ -0,0 +1,134 @@
<?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>{B8F05057-8B9F-44CE-B983-99CB7C2E9E5A}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Owin.Security.Providers.Evernote</RootNamespace>
<AssemblyName>Owin.Security.Providers.Evernote</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</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="Accessibility" />
<Reference Include="CsQuery, Version=1.3.3.249, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\CsQuery.1.3.4\lib\net40\CsQuery.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="en-html2enml, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\evernote-cloud-sdk-windows.1.0.8\lib\net40\en-html2enml.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Evernote, Version=1.0.5377.16918, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\evernote-cloud-sdk-windows.1.0.8\lib\net40\Evernote.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="EvernoteOAuthNet, Version=2.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\evernote-cloud-sdk-windows.1.0.8\lib\net40\EvernoteOAuthNet.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="EvernoteSDK, Version=1.0.12.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\evernote-cloud-sdk-windows.1.0.8\lib\net40\EvernoteSDK.dll</HintPath>
<Private>True</Private>
</Reference>
<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=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.9.0.1\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="PreMailer.Net, Version=1.2.7.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\PreMailer.Net.1.2.7\lib\net40\PreMailer.Net.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SgmlReaderDll, Version=1.8.11.0, Culture=neutral, PublicKeyToken=46b2db9ca481831b, processorArchitecture=MSIL">
<HintPath>..\..\packages\SgmlReader.1.8.11\lib\4.0\SgmlReaderDll.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" />
<Reference Include="Thrift, Version=0.8.0.16918, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\evernote-cloud-sdk-windows.1.0.8\lib\net40\Thrift.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Constants.cs" />
<Compile Include="EvernoteAuthenticationExtensions.cs" />
<Compile Include="EvernoteAuthenticationHandler.cs" />
<Compile Include="EvernoteAuthenticationMiddleware.cs" />
<Compile Include="EvernoteAuthenticationOptions.cs" />
<Compile Include="Messages\AccessToken.cs" />
<Compile Include="Messages\RequestToken.cs" />
<Compile Include="Messages\RequestTokenSerializer.cs" />
<Compile Include="Messages\Serializers.cs" />
<Compile Include="OAuthBase.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Provider\EvernoteAuthenticatedContext.cs" />
<Compile Include="Provider\EvernoteAuthenticationProvider.cs" />
<Compile Include="Provider\EvernoteReturnEndpointContext.cs" />
<Compile Include="Provider\IEvernoteAuthenticationProvider.cs" />
<Compile Include="Resources.Designer.cs">
<DependentUpon>Resources.resx</DependentUpon>
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
</Compile>
<Compile Include="SignatureTypes.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</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.Evernote")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Owin.Security.Providers.Evernote")]
[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("b8f05057-8b9f-44ce-b983-99cb7c2e9e5a")]
// 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,57 @@
// 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 Owin.Security.Providers.Evernote.Messages;
namespace Owin.Security.Providers.Evernote
{
/// <summary>
/// Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.
/// </summary>
public class EvernoteAuthenticatedContext : BaseContext
{
/// <summary>
/// Initializes a <see cref="EvernoteAuthenticatedContext"/>
/// </summary>
/// <param name="context">The OWIN environment</param>
/// <param name="accessToken">Evernote access toke</param>
public EvernoteAuthenticatedContext(IOwinContext context, AccessToken accessToken)
: base(context)
{
UserId = accessToken.UserId;
UserName = accessToken.UserName;
AccessToken = accessToken.Token;
NoteStoreUrl = accessToken.NoteStoreUrl;
}
/// <summary>
/// Gets the Evernote user ID
/// </summary>
public string UserId { get; private set; }
/// <summary>
/// Gets the Evernote username
/// </summary>
public string UserName { get; set; }
/// <summary>
/// Gets the Evernote access token
/// </summary>
public string AccessToken { get; private set; }
public string NoteStoreUrl { get; 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; }
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.Threading.Tasks;
namespace Owin.Security.Providers.Evernote
{
/// <summary>
/// Default <see cref="IEvernoteAuthenticationProvider"/> implementation.
/// </summary>
public class EvernoteAuthenticationProvider : IEvernoteAuthenticationProvider
{
/// <summary>
/// Initializes a <see cref="EvernoteAuthenticationProvider"/>
/// </summary>
public EvernoteAuthenticationProvider()
{
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<EvernoteAuthenticatedContext, Task> OnAuthenticated { get; set; }
/// <summary>
/// Gets or sets the function that is invoked when the ReturnEndpoint method is invoked.
/// </summary>
public Func<EvernoteReturnEndpointContext, Task> OnReturnEndpoint { get; set; }
/// <summary>
/// Invoked whenever Evernote 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(EvernoteAuthenticatedContext 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(EvernoteReturnEndpointContext 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.Evernote
{
/// <summary>
/// Provides context information to middleware providers.
/// </summary>
public class EvernoteReturnEndpointContext : ReturnEndpointContext
{
/// <summary>
///
/// </summary>
/// <param name="context">OWIN environment</param>
/// <param name="ticket">The authentication ticket</param>
public EvernoteReturnEndpointContext(
IOwinContext context,
AuthenticationTicket ticket)
: base(context, ticket)
{
}
}
}

View File

@@ -0,0 +1,24 @@
using System.Threading.Tasks;
namespace Owin.Security.Providers.Evernote
{
/// <summary>
/// Specifies callback methods which the <see cref="EvernoteAuthenticationMiddleware"></see> invokes to enable developer control over the authentication process. />
/// </summary>
public interface IEvernoteAuthenticationProvider
{
/// <summary>
/// Invoked whenever Evernote 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(EvernoteAuthenticatedContext 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(EvernoteReturnEndpointContext 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.Evernote {
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)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Owin.Security.Providers.Evernote.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,9 @@
namespace Owin.Security.Providers.Evernote
{
public enum SignatureTypes
{
Hmacsha1,
Plaintext,
Rsasha1
}
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CsQuery" version="1.3.4" targetFramework="net45" />
<package id="evernote-cloud-sdk-windows" version="1.0.8" targetFramework="net45" />
<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="9.0.1" targetFramework="net452" />
<package id="Owin" version="1.0" targetFramework="net452" />
<package id="PreMailer.Net" version="1.2.7" targetFramework="net45" />
<package id="SgmlReader" version="1.8.11" targetFramework="net45" />
</packages>

View File

@@ -61,7 +61,7 @@ namespace Owin.Security.Providers.LinkedIn
return new AuthenticationTicket(null, properties);
}
var requestPrefix = Request.Scheme + "://" + Request.Host;
var requestPrefix = Request.Scheme + "://" + this.GetHostName();
var redirectUri = requestPrefix + Request.PathBase + Options.CallbackPath;
// Build up the body for the token request
@@ -173,7 +173,7 @@ namespace Owin.Security.Providers.LinkedIn
var baseUri =
Request.Scheme +
Uri.SchemeDelimiter +
Request.Host +
this.GetHostName() +
Request.PathBase;
var currentUri =
@@ -213,7 +213,11 @@ namespace Owin.Security.Providers.LinkedIn
"&scope=" + Uri.EscapeDataString(scope) +
"&state=" + Uri.EscapeDataString(state);
Response.Redirect(authorizationEndpoint);
var redirectContext = new LinkedInApplyRedirectContext(
Context, Options,
properties, authorizationEndpoint);
Options.Provider.ApplyRedirect(redirectContext);
return Task.FromResult<object>(null);
}
@@ -267,5 +271,15 @@ namespace Owin.Security.Providers.LinkedIn
return context.IsRequestCompleted;
}
/// <summary>
/// Gets proxy host name from <see cref="LinkedInAuthenticationOptions"/> if it is set.
/// If proxy host name is not set, gets application request host name.
/// </summary>
/// <returns>Host name.</returns>
private string GetHostName()
{
return string.IsNullOrWhiteSpace(Options.ProxyHost) ? Request.Host.ToString() : Options.ProxyHost;
}
}
}

View File

@@ -43,6 +43,16 @@ namespace Owin.Security.Providers.LinkedIn
/// </summary>
public PathString CallbackPath { get; set; }
/// <summary>
/// Gets or sets the middleware host name.
/// The middleware processes the <see cref="CallbackPath"/> on this host name instead of the application's request host.
/// If this is not set, the application's request host will be used.
/// </summary>
/// <remarks>
/// Use this property when running behind a proxy.
/// </remarks>
public string ProxyHost { get; set; }
/// <summary>
/// Get or sets the text that the user can display on a sign in user interface.
/// </summary>

View File

@@ -64,6 +64,7 @@
<Compile Include="LinkedInAuthenticationMiddleware.cs" />
<Compile Include="LinkedInAuthenticationOptions.cs" />
<Compile Include="Provider\ILinkedInAuthenticationProvider.cs" />
<Compile Include="Provider\LinkedInApplyRedirectContext.cs" />
<Compile Include="Provider\LinkedInAuthenticatedContext.cs" />
<Compile Include="Provider\LinkedInAuthenticationProvider.cs" />
<Compile Include="Provider\LinkedInReturnEndpointContext.cs" />

View File

@@ -20,5 +20,11 @@ namespace Owin.Security.Providers.LinkedIn
/// <param name="context"></param>
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
Task ReturnEndpoint(LinkedInReturnEndpointContext context);
/// <summary>
/// Called when a Challenge causes a redirect to authorize endpoint in the LinkedIn middleware
/// </summary>
/// <param name="context">Contains redirect URI and <see cref="AuthenticationProperties"/> of the challenge </param>
void ApplyRedirect(LinkedInApplyRedirectContext context);
}
}

View File

@@ -0,0 +1,38 @@
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Provider;
namespace Owin.Security.Providers.LinkedIn
{
/// <summary>
/// Context passed when a Challenge causes a redirect to authorize endpoint in the LinkedIn middleware
/// </summary>
public class LinkedInApplyRedirectContext : BaseContext<LinkedInAuthenticationOptions>
{
/// <summary>
/// Creates a new context object.
/// </summary>
/// <param name="context">The OWIN request context</param>
/// <param name="options">The LinkedIn middleware options</param>
/// <param name="properties">The authenticaiton properties of the challenge</param>
/// <param name="redirectUri">The initial redirect URI</param>
public LinkedInApplyRedirectContext(IOwinContext context, LinkedInAuthenticationOptions options,
AuthenticationProperties properties, string redirectUri)
: base(context, options)
{
RedirectUri = redirectUri;
Properties = properties;
}
/// <summary>
/// Gets the URI used for the redirect operation.
/// </summary>
public string RedirectUri { get; private set; }
/// <summary>
/// Gets the authentication properties of the challenge
/// </summary>
public AuthenticationProperties Properties { get; private set; }
}
}

View File

@@ -15,6 +15,8 @@ namespace Owin.Security.Providers.LinkedIn
{
OnAuthenticated = context => Task.FromResult<object>(null);
OnReturnEndpoint = context => Task.FromResult<object>(null);
OnApplyRedirect = context =>
context.Response.Redirect(context.RedirectUri);
}
/// <summary>
@@ -27,6 +29,11 @@ namespace Owin.Security.Providers.LinkedIn
/// </summary>
public Func<LinkedInReturnEndpointContext, Task> OnReturnEndpoint { get; set; }
/// <summary>
/// Gets or sets the delegate that is invoked when the ApplyRedirect method is invoked.
/// </summary>
public Action<LinkedInApplyRedirectContext> OnApplyRedirect { get; set; }
/// <summary>
/// Invoked whenever LinkedIn successfully authenticates a user
/// </summary>
@@ -46,5 +53,14 @@ namespace Owin.Security.Providers.LinkedIn
{
return OnReturnEndpoint(context);
}
/// <summary>
/// Called when a Challenge causes a redirect to authorize endpoint in the LinkedIn middleware
/// </summary>
/// <param name="context">Contains redirect URI and <see cref="AuthenticationProperties"/> of the challenge </param>
public virtual void ApplyRedirect(LinkedInApplyRedirectContext context)
{
OnApplyRedirect(context);
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,222 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Owin.Infrastructure;
using Microsoft.Owin.Logging;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler.Encoder;
using Microsoft.Owin.Security.Infrastructure;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Owin.Security.Providers.MyHeritage.Provider;
namespace Owin.Security.Providers.MyHeritage
{
public class MyHeritageAuthenticationHandler : AuthenticationHandler<MyHeritageAuthenticationOptions>
{
private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
private readonly ILogger _logger;
private readonly HttpClient _httpClient;
public MyHeritageAuthenticationHandler(HttpClient httpClient, ILogger logger)
{
_httpClient = httpClient;
_logger = logger;
}
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
AuthenticationProperties properties = null;
try
{
string code = null;
string state = null;
var query = Request.Query;
var values = query.GetValues("code");
if (values != null && values.Count == 1)
{
code = values[0];
}
values = query.GetValues("state");
if (values != null && values.Count == 1)
{
state = values[0];
}
properties = Options.StateDataFormat.Unprotect(state);
if (properties == null)
{
return null;
}
// OAuth2 10.12 CSRF
if (!ValidateCorrelationId(properties, _logger))
{
return new AuthenticationTicket(null, properties);
}
var requestPrefix = Request.Scheme + "://" + Request.Host;
var redirectUri = requestPrefix + Request.PathBase + Options.CallbackPath;
// Build up the body for the token request
var body = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("code", code),
new KeyValuePair<string, string>("grant_type", "authorization_code"),
new KeyValuePair<string, string>("client_id", Options.ClientId),
new KeyValuePair<string, string>("client_secret", Options.ClientSecret),
new KeyValuePair<string, string>("redirect_uri", redirectUri)
};
// Request the token
var tokenResponse =
await _httpClient.PostAsync(Options.Endpoints.TokenEndpoint, new FormUrlEncodedContent(body));
tokenResponse.EnsureSuccessStatusCode();
var text = await tokenResponse.Content.ReadAsStringAsync();
// Deserializes the token response
dynamic response = JsonConvert.DeserializeObject<dynamic>(text);
var accessToken = (string)response.access_token;
var refreshToken = (string)response.refresh_token;
// Get the MyHeritage user
var userInfoResponse = await _httpClient.GetAsync(
Options.Endpoints.UserEndpoint + "?bearer_token=" + Uri.EscapeDataString(accessToken), Request.CallCancelled);
userInfoResponse.EnsureSuccessStatusCode();
text = await userInfoResponse.Content.ReadAsStringAsync();
var user = JObject.Parse(text);
var context = new MyHeritageAuthenticatedContext(Context, user, accessToken, refreshToken)
{
Identity = new ClaimsIdentity(
Options.AuthenticationType,
ClaimsIdentity.DefaultNameClaimType,
ClaimsIdentity.DefaultRoleClaimType)
};
if (!string.IsNullOrEmpty(context.Id))
{
context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.Id, XmlSchemaString, Options.AuthenticationType));
}
if (!string.IsNullOrEmpty(context.Name))
{
context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.Name, XmlSchemaString, Options.AuthenticationType));
}
context.Properties = properties;
await Options.Provider.Authenticated(context);
return new AuthenticationTicket(context.Identity, context.Properties);
}
catch (Exception ex)
{
_logger.WriteError(ex.Message);
}
return new AuthenticationTicket(null, properties);
}
protected override Task ApplyResponseChallengeAsync()
{
if (Response.StatusCode != 401)
{
return Task.FromResult<object>(null);
}
var challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode);
if (challenge == null) return Task.FromResult<object>(null);
var baseUri =
Request.Scheme +
Uri.SchemeDelimiter +
Request.Host +
Request.PathBase;
var currentUri =
baseUri +
Request.Path +
Request.QueryString;
var redirectUri =
baseUri +
Options.CallbackPath;
var properties = challenge.Properties;
if (string.IsNullOrEmpty(properties.RedirectUri))
{
properties.RedirectUri = currentUri;
}
// OAuth2 10.12 CSRF
GenerateCorrelationId(properties);
var state = Options.StateDataFormat.Protect(properties);
var authorizationEndpoint =
Options.Endpoints.AuthorizationEndpoint +
"?client_id=" + Uri.EscapeDataString(Options.ClientId) +
"&response_type=" + Uri.EscapeDataString("code") +
"&redirect_uri=" + Uri.EscapeDataString(redirectUri) +
"&state=" + Uri.EscapeDataString(state);
Response.Redirect(authorizationEndpoint);
return Task.FromResult<object>(null);
}
public override async Task<bool> InvokeAsync()
{
return await InvokeReplyPathAsync();
}
private async Task<bool> InvokeReplyPathAsync()
{
if (!Options.CallbackPath.HasValue || Options.CallbackPath != Request.Path) return false;
// TODO: error responses
var ticket = await AuthenticateAsync();
if (ticket == null)
{
_logger.WriteWarning("Invalid return state, unable to redirect.");
Response.StatusCode = 500;
return true;
}
var context = new MyHeritageReturnEndpointContext(Context, ticket)
{
SignInAsAuthenticationType = Options.SignInAsAuthenticationType,
RedirectUri = ticket.Properties.RedirectUri
};
await Options.Provider.ReturnEndpoint(context);
if (context.SignInAsAuthenticationType != null &&
context.Identity != null)
{
var grantIdentity = context.Identity;
if (!string.Equals(grantIdentity.AuthenticationType, context.SignInAsAuthenticationType, StringComparison.Ordinal))
{
grantIdentity = new ClaimsIdentity(grantIdentity.Claims, context.SignInAsAuthenticationType, grantIdentity.NameClaimType, grantIdentity.RoleClaimType);
}
Context.Authentication.SignIn(context.Properties, grantIdentity);
}
if (context.IsRequestCompleted || context.RedirectUri == null) return context.IsRequestCompleted;
var redirectUri = context.RedirectUri;
if (context.Identity == null)
{
// add a redirect hint that sign-in failed in some way
redirectUri = WebUtilities.AddQueryString(redirectUri, "error", "access_denied");
}
Response.Redirect(redirectUri);
context.RequestCompleted();
return context.IsRequestCompleted;
}
}
}

View File

@@ -0,0 +1,68 @@
using System;
using System.Globalization;
using System.Net.Http;
using Microsoft.Owin;
using Microsoft.Owin.Logging;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler;
using Microsoft.Owin.Security.DataProtection;
using Microsoft.Owin.Security.Infrastructure;
using Owin.Security.Providers.MyHeritage.Provider;
namespace Owin.Security.Providers.MyHeritage
{
public class MyHeritageAuthenticationMiddleware : AuthenticationMiddleware<MyHeritageAuthenticationOptions>
{
private readonly HttpClient _httpClient;
private readonly ILogger _logger;
public MyHeritageAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app,
MyHeritageAuthenticationOptions options)
: base(next, options)
{
if (string.IsNullOrWhiteSpace(Options.ClientId))
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
Resources.Exception_OptionMustBeProvided, "ClientId"));
if (string.IsNullOrWhiteSpace(Options.ClientSecret))
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
Resources.Exception_OptionMustBeProvided, "ClientSecret"));
_logger = app.CreateLogger<MyHeritageAuthenticationMiddleware>();
if (Options.Provider == null)
Options.Provider = new MyHeritageAuthenticationProvider();
if (Options.StateDataFormat == null)
{
var dataProtector = app.CreateDataProtector(
typeof (MyHeritageAuthenticationMiddleware).FullName,
Options.AuthenticationType, "v1");
Options.StateDataFormat = new PropertiesDataFormat(dataProtector);
}
if (string.IsNullOrEmpty(Options.SignInAsAuthenticationType))
Options.SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType();
_httpClient = new HttpClient(new WebRequestHandler())
{
Timeout = Options.BackchannelTimeout,
MaxResponseContentBufferSize = 1024*1024*10,
};
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft Owin MyHeritage middleware");
_httpClient.DefaultRequestHeaders.ExpectContinue = false;
}
/// <summary>
/// Provides the <see cref="T:Microsoft.Owin.Security.Infrastructure.AuthenticationHandler" /> object for processing
/// authentication-related requests.
/// </summary>
/// <returns>
/// An <see cref="T:Microsoft.Owin.Security.Infrastructure.AuthenticationHandler" /> configured with the
/// <see cref="T:Owin.Security.Providers.MyHeritage.MyHeritageAuthenticationOptions" /> supplied to the constructor.
/// </returns>
protected override AuthenticationHandler<MyHeritageAuthenticationOptions> CreateHandler()
{
return new MyHeritageAuthenticationHandler(_httpClient, _logger);
}
}
}

View File

@@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Owin.Security.Providers.MyHeritage.Provider;
namespace Owin.Security.Providers.MyHeritage
{
public class MyHeritageAuthenticationOptions : AuthenticationOptions
{
public class MyHeritageAuthenticationEndpoints
{
/// <summary>
/// Endpoint which is used to redirect users to request MyHeritage access
/// </summary>
/// <remarks>
/// Defaults to https://accounts.myheritage.com/oauth2/authorize
/// </remarks>
public string AuthorizationEndpoint { get; set; }
/// <summary>
/// Endpoint which is used to exchange code for access token
/// </summary>
/// <remarks>
/// Defaults to https://accounts.myheritage.com/oauth2/token
/// </remarks>
public string TokenEndpoint { get; set; }
/// <summary>
/// Endpoint which is used to get information on the user
/// </summary>
/// <remarks>
/// Defaults to https://familygraph.myheritage.com/me
/// </remarks>
public string UserEndpoint { get; set; }
}
private const string AuthorizationEndPoint = "https://accounts.myheritage.com/oauth2/authorize";
private const string TokenEndpoint = "https://accounts.myheritage.com/oauth2/token";
private const string UserEndpoint = "https://familygraph.myheritage.com/me";
/// <summary>
/// Gets or sets timeout value in milliseconds for back channel communications with MyHeritage.
/// </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-myheritage".
/// </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 MyHeritage supplied Client Id
/// </summary>
public string ClientId { get; set; }
/// <summary>
/// Gets or sets the MyHeritage supplied Client Secret
/// </summary>
public string ClientSecret { get; set; }
/// <summary>
/// Gets the sets of OAuth endpoints used to authenticate against MyHeritage. Overriding these endpoints allows you to use MyHeritage Enterprise for
/// authentication.
/// </summary>
public MyHeritageAuthenticationEndpoints Endpoints { get; set; }
/// <summary>
/// Gets or sets the <see cref="IMyHeritageAuthenticationProvider" /> used in the authentication events
/// </summary>
public IMyHeritageAuthenticationProvider Provider { 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="MyHeritageAuthenticationOptions" />
/// </summary>
public MyHeritageAuthenticationOptions()
: base("MyHeritage")
{
Caption = Constants.DefaultAuthenticationType;
CallbackPath = new PathString("/signin-myheritage");
AuthenticationMode = AuthenticationMode.Passive;
BackchannelTimeout = TimeSpan.FromSeconds(60);
Endpoints = new MyHeritageAuthenticationEndpoints
{
AuthorizationEndpoint = AuthorizationEndPoint,
TokenEndpoint = TokenEndpoint,
UserEndpoint = UserEndpoint,
};
}
}
}

View File

@@ -0,0 +1,104 @@
<?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>{84795078-31B5-4369-BD1B-F960165F8C71}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Owin.Security.Providers.MyHeritage</RootNamespace>
<AssemblyName>Owin.Security.Providers.MyHeritage</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</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="Constants.cs" />
<Compile Include="MyHeritageAuthenticationExtensions.cs" />
<Compile Include="MyHeritageAuthenticationHandler.cs" />
<Compile Include="MyHeritageAuthenticationMiddleware.cs" />
<Compile Include="MyHeritageAuthenticationOptions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Provider\MyHeritageAuthenticatedContext.cs" />
<Compile Include="Provider\MyHeritageAuthenticationProvider.cs" />
<Compile Include="Provider\MyHeritageReturnEndpointContext.cs" />
<Compile Include="Provider\IMyHeritageAuthenticationProvider.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,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.MyHeritage")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Owin.Security.Providers.MyHeritage")]
[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("84795078-31b5-4369-bd1b-f960165f8c71")]
// 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.MyHeritage.Provider
{
/// <summary>
/// Specifies callback methods which the <see cref="MyHeritageAuthenticationMiddleware"></see> invokes to enable developer control over the authentication process. />
/// </summary>
public interface IMyHeritageAuthenticationProvider
{
/// <summary>
/// Invoked whenever MyHeritage 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(MyHeritageAuthenticatedContext 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(MyHeritageReturnEndpointContext context);
}
}

View File

@@ -0,0 +1,72 @@
// 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;
namespace Owin.Security.Providers.MyHeritage.Provider
{
/// <summary>
/// Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.
/// </summary>
public class MyHeritageAuthenticatedContext : BaseContext
{
/// <summary>
/// Initializes a <see cref="MyHeritageAuthenticatedContext"/>
/// </summary>
/// <param name="context">The OWIN environment</param>
/// <param name="user">The MyHeritage user information</param>
/// <param name="accessToken">MyHeritage Access token</param>
/// <param name="refreshToken">MyHeritage Refresh token</param>
public MyHeritageAuthenticatedContext(IOwinContext context, JObject user, string accessToken, string refreshToken)
: base(context)
{
AccessToken = accessToken;
RefreshToken = refreshToken;
User = user;
Name = user.SelectToken("name").ToString();
Id = user.SelectToken("id").ToString();
}
/// <summary>
/// Gets the user json object that was retrieved from MyHeritage
/// during the authorization process.
/// </summary>
public JObject User { get; private set; }
/// <summary>
/// Gets the user name extracted from the MyHeritage API during
/// the authorization process.
/// </summary>
public string Name { get; private set; }
/// <summary>
/// Gets the user id extracted from the MyHeritageAPI during the
/// authorization process.
/// </summary>
public string Id { get; private set; }
/// <summary>
/// Gets the MyHeritage access token
/// </summary>
public string AccessToken { get; private set; }
/// <summary>
/// Gets the MyHeritage refresh token
/// </summary>
public string RefreshToken { get; private 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; }
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.Threading.Tasks;
namespace Owin.Security.Providers.MyHeritage.Provider
{
/// <summary>
/// Default <see cref="IMyHeritageAuthenticationProvider"/> implementation.
/// </summary>
public class MyHeritageAuthenticationProvider : IMyHeritageAuthenticationProvider
{
/// <summary>
/// Initializes a <see cref="MyHeritageAuthenticationProvider"/>
/// </summary>
public MyHeritageAuthenticationProvider()
{
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<MyHeritageAuthenticatedContext, Task> OnAuthenticated { get; set; }
/// <summary>
/// Gets or sets the function that is invoked when the ReturnEndpoint method is invoked.
/// </summary>
public Func<MyHeritageReturnEndpointContext, Task> OnReturnEndpoint { get; set; }
/// <summary>
/// Invoked whenever MyHeritage 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(MyHeritageAuthenticatedContext 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(MyHeritageReturnEndpointContext 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.MyHeritage.Provider
{
/// <summary>
/// Provides context information to middleware providers.
/// </summary>
public class MyHeritageReturnEndpointContext : ReturnEndpointContext
{
/// <summary>
///
/// </summary>
/// <param name="context">OWIN environment</param>
/// <param name="ticket">The authentication ticket</param>
public MyHeritageReturnEndpointContext(
IOwinContext context,
AuthenticationTicket ticket)
: base(context, ticket)
{
}
}
}

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.MyHeritage {
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)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Owin.Security.Providers.MyHeritage.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

@@ -46,9 +46,8 @@
<HintPath>..\..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Owin.Security.Providers.OpenIDBase, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Owin.Security.Providers.OpenIDBase.2.1\lib\net45\Owin.Security.Providers.OpenIDBase.dll</HintPath>
<Private>True</Private>
<Reference Include="Owin.Security.Providers.OpenIDBase, Version=2.0.0.0, Culture=neutral, PublicKeyToken=1328b8c16b108c43, processorArchitecture=MSIL">
<HintPath>..\..\packages\Owin.Security.Providers.OpenIDBase.2.1.1\lib\net45\Owin.Security.Providers.OpenIDBase.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />

View File

@@ -4,5 +4,5 @@
<package id="Microsoft.Owin.Security" version="3.0.1" targetFramework="net45" />
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net45" />
<package id="Owin" version="1.0" targetFramework="net45" />
<package id="Owin.Security.Providers.OpenIDBase" version="2.1" targetFramework="net45" />
<package id="Owin.Security.Providers.OpenIDBase" version="2.1.1" targetFramework="net45" />
</packages>

View File

@@ -81,9 +81,25 @@ namespace Owin.Security.Providers.PayPal
new KeyValuePair<string, string>("code", code),
new KeyValuePair<string, string>("redirect_uri", redirectUri),
});
var tokenResponse = await _httpClient.SendAsync(requestMessage);
tokenResponse.EnsureSuccessStatusCode();
var text = await tokenResponse.Content.ReadAsStringAsync();
string text = "";
try
{
var tokenResponse = await _httpClient.SendAsync(requestMessage);
tokenResponse.EnsureSuccessStatusCode();
text = await tokenResponse.Content.ReadAsStringAsync();
}
catch (HttpRequestException ex)
{
if (ex.InnerException is System.Net.WebException && ex.InnerException.Message.Contains("TLS"))
{
if (!System.Net.ServicePointManager.SecurityProtocol.HasFlag(System.Net.SecurityProtocolType.Tls12))
{
throw new System.Net.WebException("PayPal requires TLS v1.2. TLS v1.0 and v1.1 connections will be refused. Set System.Net.ServicePointManager.SecurityProtocol = System.Net.ServicePointManager.SecurityProtocol | System.Net.SecurityProtocolType.Tls12", ex.InnerException);
}
}
throw;
}
// Deserializes the token response
var response = JsonConvert.DeserializeObject<dynamic>(text);

View File

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

View File

@@ -0,0 +1,98 @@
<?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>{A7B95FD4-08AD-499F-B574-07560CC2A63F}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Owin.Security.Providers.Podbean</RootNamespace>
<AssemblyName>Owin.Security.Providers.Podbean</AssemblyName>
<TargetFrameworkVersion>v4.5</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>
</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>
</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>
</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>
</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="PodbeanAuthenticationExtensions.cs" />
<Compile Include="PodbeanAuthenticationHandler.cs" />
<Compile Include="PodbeanAuthenticationMiddleware.cs" />
<Compile Include="PodbeanAuthenticationOptions.cs" />
<Compile Include="Constants.cs" />
<Compile Include="Provider\PodbeanAuthenticatedContext.cs" />
<Compile Include="Provider\PodbeanAuthenticationProvider.cs" />
<Compile Include="Provider\PodbeanReturnEndpointContext.cs" />
<Compile Include="Provider\IPodbeanAuthenticationProvider.cs" />
<Compile Include="Resources.Designer.cs">
<DependentUpon>Resources.resx</DependentUpon>
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</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,33 @@
#region
using System;
#endregion
namespace Owin.Security.Providers.Podbean
{
public static class PodbeanAuthenticationExtensions
{
public static IAppBuilder UsePodbeanAuthentication(this IAppBuilder app,
PodbeanAuthenticationOptions options)
{
if (app == null)
throw new ArgumentNullException(nameof(app));
if (options == null)
throw new ArgumentNullException(nameof(options));
app.Use(typeof(PodbeanAuthenticationMiddleware), app, options);
return app;
}
public static IAppBuilder UsePodbeanAuthentication(this IAppBuilder app, string appId, string appSecret)
{
return app.UsePodbeanAuthentication(new PodbeanAuthenticationOptions
{
AppId = appId,
AppSecret = appSecret
});
}
}
}

View File

@@ -0,0 +1,246 @@
#region
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 Microsoft.Owin;
using System.Net.Http.Headers;
#endregion
namespace Owin.Security.Providers.Podbean
{
public class PodbeanAuthenticationHandler : AuthenticationHandler<PodbeanAuthenticationOptions>
{
private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
private const string TokenEndpoint = "https://api.podbean.com/v1/oauth/token";
private const string PodcastIdEndpoint = "https://api.podbean.com/v1/oauth/debugToken";
private const string PodcastEndpoint = "https://api.podbean.com/v1/podcast";
private readonly HttpClient _httpClient;
private readonly ILogger _logger;
public PodbeanAuthenticationHandler(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 = GetBaseUri(Request);
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)
};
// Request the token
var tokenRequest = new HttpRequestMessage(HttpMethod.Post, TokenEndpoint);
tokenRequest.Headers.Authorization =
new AuthenticationHeaderValue("Basic", Base64Encode($"{Options.AppId}:{Options.AppSecret}"));
tokenRequest.Content = new FormUrlEncodedContent(body);
var tokenResponse = await _httpClient.SendAsync(tokenRequest, Request.CallCancelled);
tokenResponse.EnsureSuccessStatusCode();
var text = await tokenResponse.Content.ReadAsStringAsync();
// Deserializes the token response
dynamic token = JsonConvert.DeserializeObject<dynamic>(text);
var accessToken = (string)token.access_token;
var refreshToken = (string)token.refresh_token;
var expires = (string)token.expires_in;
// Get the Podbean podcast
var podcastResponse = await _httpClient.GetAsync(
$"{PodcastEndpoint}?access_token={Uri.EscapeDataString(accessToken)}", Request.CallCancelled);
podcastResponse.EnsureSuccessStatusCode();
text = await podcastResponse.Content.ReadAsStringAsync();
var podcast = JObject.Parse(text)["podcast"].ToObject<Podcast>();
// Get the Podbean podcast id
var podcastIdRequest = new HttpRequestMessage(HttpMethod.Get, $"{PodcastIdEndpoint}?access_token={Uri.EscapeDataString(accessToken)}");
podcastIdRequest.Headers.Authorization =
new AuthenticationHeaderValue("Basic", Base64Encode($"{Options.AppId}:{Options.AppSecret}"));
var podcastIdResponse = await _httpClient.SendAsync(podcastIdRequest, Request.CallCancelled);
podcastIdResponse.EnsureSuccessStatusCode();
text = await podcastIdResponse.Content.ReadAsStringAsync();
var podcastId = JsonConvert.DeserializeObject<dynamic>(text);
podcast.Id = (string)podcastId.podcast_id;
var context = new PodbeanAuthenticatedContext(Context, podcast, accessToken, refreshToken, expires)
{
Identity = new ClaimsIdentity(
Options.AuthenticationType,
ClaimsIdentity.DefaultNameClaimType,
ClaimsIdentity.DefaultRoleClaimType)
};
if (!string.IsNullOrEmpty(context.Id))
context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.Id, XmlSchemaString,
Options.AuthenticationType));
if (!string.IsNullOrEmpty(context.Name))
context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.Name,
XmlSchemaString, Options.AuthenticationType));
context.Properties = properties;
await Options.Provider.Authenticated(context);
return new AuthenticationTicket(context.Identity, context.Properties);
}
catch (Exception ex)
{
_logger.WriteError(ex.Message);
}
return new AuthenticationTicket(null, properties);
}
protected override Task ApplyResponseChallengeAsync()
{
if (Response.StatusCode != 401)
return Task.FromResult<object>(null);
var challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode);
if (challenge == null) return Task.FromResult<object>(null);
var baseUri = GetBaseUri(Request);
var currentUri =
baseUri +
Request.Path +
Request.QueryString;
var redirectUri =
baseUri +
Options.CallbackPath;
var properties = challenge.Properties;
if (string.IsNullOrEmpty(properties.RedirectUri))
properties.RedirectUri = currentUri;
// OAuth2 10.12 CSRF
GenerateCorrelationId(properties);
var state = Options.StateDataFormat.Protect(properties);
var scope = string.Join(" ", Options.Scope);
var authorizationEndpoint =
"https://api.podbean.com/v1/dialog/oauth" +
"?response_type=code" +
"&client_id=" + Uri.EscapeDataString(Options.AppId) +
"&redirect_uri=" + Uri.EscapeDataString(redirectUri) +
"&scope=" + Uri.EscapeDataString(scope) +
"&state=" + Uri.EscapeDataString(state);
Response.Redirect(authorizationEndpoint);
return Task.FromResult<object>(null);
}
public override async Task<bool> InvokeAsync()
{
return await InvokeReplyPathAsync();
}
private async Task<bool> InvokeReplyPathAsync()
{
if (!Options.CallbackPath.HasValue || Options.CallbackPath != Request.Path) return false;
// TODO: error responses
var ticket = await AuthenticateAsync();
if (ticket == null)
{
_logger.WriteWarning("Invalid return state, unable to redirect.");
Response.StatusCode = 500;
return true;
}
var context = new PodbeanReturnEndpointContext(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)
redirectUri = WebUtilities.AddQueryString(redirectUri, "error", "access_denied");
Response.Redirect(redirectUri);
context.RequestCompleted();
return context.IsRequestCompleted;
}
private static string Base64Encode(string plainText)
{
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return System.Convert.ToBase64String(plainTextBytes);
}
private string GetBaseUri(IOwinRequest request)
{
if (Options.DebugUsingRequestHeadersToBuildBaseUri &&
request.Headers["X-Original-Host"] != null &&
request.Headers["X-Forwarded-Proto"] != null)
{
return request.Headers["X-Forwarded-Proto"] + Uri.SchemeDelimiter + request.Headers["X-Original-Host"];
}
var baseUri =
request.Scheme +
Uri.SchemeDelimiter +
request.Host +
request.PathBase;
return baseUri;
}
}
}

View File

@@ -0,0 +1,84 @@
#region
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;
#endregion
namespace Owin.Security.Providers.Podbean
{
public class PodbeanAuthenticationMiddleware : AuthenticationMiddleware<PodbeanAuthenticationOptions>
{
private readonly HttpClient _httpClient;
private readonly ILogger _logger;
public PodbeanAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app,
PodbeanAuthenticationOptions options)
: base(next, options)
{
if (string.IsNullOrWhiteSpace(Options.AppId))
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
Resources.Exception_OptionMustBeProvided, "AppId"));
if (string.IsNullOrWhiteSpace(Options.AppSecret))
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
Resources.Exception_OptionMustBeProvided, "AppSecret"));
_logger = app.CreateLogger<PodbeanAuthenticationMiddleware>();
if (Options.Provider == null)
Options.Provider = new PodbeanAuthenticationProvider();
if (Options.StateDataFormat == null)
{
var dataProtector = app.CreateDataProtector(
typeof(PodbeanAuthenticationMiddleware).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.Podbean.PodbeanAuthenticationOptions" /> supplied to the constructor.
/// </returns>
protected override AuthenticationHandler<PodbeanAuthenticationOptions> CreateHandler()
{
return new PodbeanAuthenticationHandler(_httpClient, _logger);
}
private static HttpMessageHandler ResolveHttpMessageHandler(PodbeanAuthenticationOptions options)
{
var handler = options.BackchannelHttpHandler ?? new WebRequestHandler();
// If they provided a validator, apply it or fail.
if (options.BackchannelCertificateValidator == null) return handler;
// Set the cert validate callback
var webRequestHandler = handler as WebRequestHandler;
if (webRequestHandler == null)
throw new InvalidOperationException(Resources.Exception_ValidatorHandlerMismatch);
webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate;
return handler;
}
}
}

View File

@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using Microsoft.Owin;
using Microsoft.Owin.Security;
namespace Owin.Security.Providers.Podbean
{
public class PodbeanAuthenticationOptions : AuthenticationOptions
{
/// <summary>
/// Initializes a new <see cref="PodbeanAuthenticationOptions" />
/// </summary>
public PodbeanAuthenticationOptions()
: base("Podbean")
{
Caption = Constants.DefaultAuthenticationType;
CallbackPath = new PathString("/signin-podbean");
AuthenticationMode = AuthenticationMode.Passive;
Scope = new List<string>
{
"podcast_read"
};
BackchannelTimeout = TimeSpan.FromSeconds(60);
}
/// <summary>
/// Gets or sets the a pinned certificate validator to use to validate the endpoints used
/// in back channel communications belong to Podbean
/// </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 Podbean.
/// 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 Podbean.
/// </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-Podbean".
/// </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 Podbean supplied App ID
/// </summary>
public string AppId { get; set; }
/// <summary>
/// Gets or sets the Podbean supplied App Secret
/// </summary>
public string AppSecret { get; set; }
/// <summary>
/// Set this value to true to debug locally using a service such as https://ngrok.io.
/// Podbean doesn't allow you to redirect to localhost. Ngrok and similar services
/// set the X-Original-Host and X-Forwarded-Proto headers to build the base Uri for
/// redirects back to localhost.
/// </summary>
public bool DebugUsingRequestHeadersToBuildBaseUri { get; set; }
/// <summary>
/// A list of permissions to request.
/// </summary>
public IList<string> Scope { get; private set; }
/// <summary>
/// Gets or sets the <see cref="IPodbeanAuthenticationProvider" /> used in the authentication events
/// </summary>
public IPodbeanAuthenticationProvider Provider { 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; }
}
}

View File

@@ -0,0 +1,15 @@
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Owin.Security.Providers.Podbean")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Owin.Security.Providers.Podbean")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("38815307-3360-4f54-9204-03fae50160bc")]
[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("2.0.0.0")]

View File

@@ -0,0 +1,33 @@
#region
using System.Threading.Tasks;
#endregion
namespace Owin.Security.Providers.Podbean
{
/// <summary>
/// Specifies callback methods which the <see cref="PodbeanAuthenticationMiddleware"></see> invokes to enable developer
/// control over the authentication process. />
/// </summary>
public interface IPodbeanAuthenticationProvider
{
/// <summary>
/// Invoked whenever Podbean 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(PodbeanAuthenticatedContext 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(PodbeanReturnEndpointContext context);
}
}

View File

@@ -0,0 +1,130 @@
#region
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;
using Newtonsoft.Json;
#endregion
namespace Owin.Security.Providers.Podbean
{
/// <summary>
/// Contains information about the login session as well as the user
/// <see cref="System.Security.Claims.ClaimsIdentity" />.
/// </summary>
public class PodbeanAuthenticatedContext : BaseContext
{
/// <summary>
/// Initializes a <see cref="PodbeanAuthenticatedContext" />
/// </summary>
/// <param name="context">The OWIN environment</param>
/// <param name="podcast">The <see cref="Podcast"/></param>
/// <param name="accessToken">Podbean Access token</param>
/// <param name="refreshToken">Podbean Refresh token</param>
/// <param name="expires">Seconds until expiration</param>
public PodbeanAuthenticatedContext(IOwinContext context, Podcast podcast, string accessToken, string refreshToken, string expires)
: base(context)
{
Podcast = podcast;
Id = podcast.Id;
Name = podcast.Title;
AccessToken = accessToken;
RefreshToken = refreshToken;
int expiresValue;
if (int.TryParse(expires, NumberStyles.Integer, CultureInfo.InvariantCulture, out expiresValue))
ExpiresIn = TimeSpan.FromSeconds(expiresValue);
}
/// <summary>
/// Gets the JSON-serialized user
/// </summary>
/// <remarks>
/// Contains the Podbean user obtained from the endpoint https://api.Podbeanapp.com/1/user.json
/// </remarks>
public Podcast Podcast { get; }
/// <summary>
/// Gets the Podbean OAuth access token
/// </summary>
public string AccessToken { get; }
/// <summary>
/// Gets the Podbean OAuth refresh token
/// </summary>
public string RefreshToken { get; }
/// <summary>
/// Gets the Podbean access token expiration time
/// </summary>
public TimeSpan? ExpiresIn { get; set; }
/// <summary>
/// Gets the Podbean Podcast ID
/// </summary>
public string Id { get; }
/// <summary>
/// The name of the user
/// </summary>
public string Name { get; }
/// <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; }
}
public class Podcast
{
/// <summary>
/// The Id of the <see cref="Podcast"/>
/// </summary>
public string Id { get; set; }
/// <summary>
/// The title of the <see cref="Podcast"/>
/// </summary>
public string Title { get; set; }
/// <summary>
/// The description of the <see cref="Podcast"/>
/// </summary>
[JsonProperty(PropertyName = "desc")]
public string Description { get; set; }
/// <summary>
/// A url to an image representing the logo of the <see cref="Podcast"/>
/// </summary>
public string Logo { get; set; }
/// <summary>
/// A url to the <see cref="Podcast"/>'s website
/// </summary>
public string Website { get; set; }
/// <summary>
/// The name of the category of the <see cref="Podcast"/>
/// </summary>
[JsonProperty(PropertyName = "category_name")]
public string CategoryName { get; set; }
/// <summary>
/// The episode types of the <see cref="Podcast"/>.
/// The possible value is a combination of public, premium or private
/// </summary>
[JsonProperty(PropertyName = "allow_episode_type")]
public string[] AllowEpisodeTypes { get; set; }
}
}

View File

@@ -0,0 +1,58 @@
#region
using System;
using System.Threading.Tasks;
#endregion
namespace Owin.Security.Providers.Podbean
{
/// <summary>
/// Default <see cref="IPodbeanAuthenticationProvider" /> implementation.
/// </summary>
public class PodbeanAuthenticationProvider : IPodbeanAuthenticationProvider
{
/// <summary>
/// Initializes a <see cref="PodbeanAuthenticationProvider" />
/// </summary>
public PodbeanAuthenticationProvider()
{
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<PodbeanAuthenticatedContext, Task> OnAuthenticated { get; set; }
/// <summary>
/// Gets or sets the function that is invoked when the ReturnEndpoint method is invoked.
/// </summary>
public Func<PodbeanReturnEndpointContext, Task> OnReturnEndpoint { get; set; }
/// <summary>
/// Invoked whenever Podbean 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(PodbeanAuthenticatedContext 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(PodbeanReturnEndpointContext context)
{
return OnReturnEndpoint(context);
}
}
}

Some files were not shown because too many files have changed in this diff Show More