Compare commits

...

24 Commits

Author SHA1 Message Date
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
Tommy Parnell
4891cb06dc minor fixes 2016-05-20 09:04:21 -04:00
57 changed files with 2988 additions and 46 deletions

3
.gitignore vendored
View File

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

View File

@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14 # Visual Studio 14
VisualStudioVersion = 14.0.25123.0 VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.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}" 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 EndProject
@@ -95,8 +95,15 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OwinOAuthProvidersDemo", "OwinOAuthProvidersDemo\OwinOAuthProvidersDemo.csproj", "{5A438007-0C90-4DAC-BAA1-54A32164067F}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OwinOAuthProvidersDemo", "OwinOAuthProvidersDemo\OwinOAuthProvidersDemo.csproj", "{5A438007-0C90-4DAC-BAA1-54A32164067F}"
EndProject 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}" 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}" 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 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
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -295,6 +302,18 @@ Global
{9DE25431-F935-48D7-8EB5-ACB6F918111C}.Debug|Any CPU.Build.0 = Debug|Any CPU {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.ActiveCfg = Release|Any CPU
{9DE25431-F935-48D7-8EB5-ACB6F918111C}.Release|Any CPU.Build.0 = 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
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@@ -3,9 +3,6 @@ using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies; using Microsoft.Owin.Security.Cookies;
using Owin; using Owin;
//using Owin.Security.Providers.Orcid;
//using Owin.Security.Providers.Discord;
namespace OwinOAuthProvidersDemo namespace OwinOAuthProvidersDemo
{ {
public partial class Startup public partial class Startup
@@ -168,6 +165,14 @@ namespace OwinOAuthProvidersDemo
// clientId: "", // clientId: "",
// clientSecret: ""); // clientSecret: "");
//app.UseBoxAuthentication(
// appKey: "",
// appSecret: "");
//app.UseBaiduAuthentication(
// apiKey: "",
// secretKey: "");
//app.UseBattleNetAuthentication(new BattleNetAuthenticationOptions //app.UseBattleNetAuthentication(new BattleNetAuthenticationOptions
//{ //{
// ClientId = "", // ClientId = "",
@@ -278,8 +283,9 @@ namespace OwinOAuthProvidersDemo
//app.("", ""); //app.("", "");
//app.UseOrcidAuthentication("",""); //app.UseOrcidAuthentication("","");
//app.UseDiscordAuthentication("", ""); //app.UseDiscordAuthentication("", "");
//app.UseGeniAuthentication("", ""); //app.UseGeniAuthentication("", "");
//app.UseMyHeritageAuthentication("", "");
} }
} }
} }

View File

@@ -245,12 +245,6 @@
<ItemGroup> <ItemGroup>
<Folder Include="App_Data\" /> <Folder Include="App_Data\" />
</ItemGroup> </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> <ItemGroup>
<Content Include="packages.config" /> <Content Include="packages.config" />
</ItemGroup> </ItemGroup>

View File

@@ -25,6 +25,7 @@ Provides a set of extra authentication providers for OWIN ([Project Katana](http
- HealthGraph - HealthGraph
- Instagram - Instagram
- LinkedIn - LinkedIn
- MyHeritage
- Onshape - Onshape
- ORCID - ORCID
- PayPal - PayPal
@@ -51,7 +52,7 @@ Provides a set of extra authentication providers for OWIN ([Project Katana](http
- Wargaming - Wargaming
## Implementation Guides ## Implementation Guides
For above listed provider implementation guide, visit Jerrie Pelser's blog - [Be a Big Rockstar](http://www.beabigrockstar.com) For above listed provider implementation guide, visit Jerrie Pelser's blog - [oauthforaspnet](http://www.oauthforaspnet.com)
## Installation ## Installation

View File

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

View File

@@ -36,9 +36,9 @@ namespace :nuget do
# Make sure we get solution-level deps # Make sure we get solution-level deps
#sh "#{CMD_PREFIX} #{NUGET}/nuget.exe i .nuget/packages.config -o packages" #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
end end

View File

@@ -19,11 +19,9 @@
Also adds generic OpenID 2.0 providers as well implementations for Steam and Wargaming. Also adds generic OpenID 2.0 providers as well implementations for Steam and Wargaming.
</summary> </summary>
<releaseNotes> <releaseNotes>
Version 2.3 - Added Box, and Baidu providers
- Added Geni, and discord providers.
- Retarget to .net 4.5 from 4.5.2
</releaseNotes> </releaseNotes>
<copyright>Copyright 2013 - 2016</copyright> <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> </metadata>
</package> </package>

View File

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

View File

@@ -14,7 +14,7 @@ namespace Owin.Security.Providers.ArcGISOnline
/// Endpoint which is used to redirect users to request ArcGISOnline access /// Endpoint which is used to redirect users to request ArcGISOnline access
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Defaults to https://www.arcgis.com/sharing/oauth2/authorize /// Defaults to https://www.arcgis.com/sharing/rest/oauth2/authorize/
/// </remarks> /// </remarks>
public string AuthorizationEndpoint { get; set; } public string AuthorizationEndpoint { get; set; }
@@ -22,7 +22,7 @@ namespace Owin.Security.Providers.ArcGISOnline
/// Endpoint which is used to exchange code for access token /// Endpoint which is used to exchange code for access token
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Defaults to https://www.arcgis.com/sharing/oauth2/token /// Defaults to https://www.arcgis.com/sharing/rest/oauth2/token/
/// </remarks> /// </remarks>
public string TokenEndpoint { get; set; } public string TokenEndpoint { get; set; }
@@ -30,14 +30,14 @@ namespace Owin.Security.Providers.ArcGISOnline
/// Endpoint which is used to obtain user information after authentication /// Endpoint which is used to obtain user information after authentication
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Defaults to https://www.arcgis.com/sharing/rest/accounts/self /// Defaults to https://www.arcgis.com/sharing/rest/community/self
/// </remarks> /// </remarks>
public string UserInfoEndpoint { get; set; } public string UserInfoEndpoint { get; set; }
} }
private const string AuthorizationEndPoint = "https://www.arcgis.com/sharing/oauth2/authorize"; private const string AuthorizationEndPoint = "https://www.arcgis.com/sharing/rest/oauth2/authorize/";
private const string TokenEndpoint = "https://www.arcgis.com/sharing/oauth2/token"; private const string TokenEndpoint = "https://www.arcgis.com/sharing/rest/oauth2/token/";
private const string UserInfoEndpoint = "https://www.arcgis.com/sharing/rest/accounts/self"; private const string UserInfoEndpoint = "https://www.arcgis.com/sharing/rest/community/self";
/// <summary> /// <summary>
/// Gets or sets the a pinned certificate validator to use to validate the endpoints used /// 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> /// </summary>
/// <param name="context">The OWIN environment</param> /// <param name="context">The OWIN environment</param>
/// <param name="user">The ArcGIS Online user</param> /// <param name="user">The ArcGIS Online user</param>
/// <param name="accessToken">ArcGISOnline Access token</param> /// <param name="accessToken">ArcGIS Online Access token</param>
public ArcGISOnlineAuthenticatedContext(IOwinContext context, ArcGISOnlineUser user, string accessToken) /// <param name="refreshToken">ArcGIS Online Refresh token</param>
public ArcGISOnlineAuthenticatedContext(IOwinContext context, ArcGISOnlineUser user, string accessToken, string refreshToken)
: base(context) : base(context)
{ {
AccessToken = accessToken; AccessToken = accessToken;
RefreshToken = refreshToken;
Id = user.User.Username; Id = user.Username;
Name = user.User.FullName; Name = user.FullName;
Link = "https://www.arcgis.com/sharing/rest/community/users/" + Id; Link = "https://www.arcgis.com/sharing/rest/community/users/" + Id;
UserName = Id; UserName = Id;
Email = user.User.Email; Email = user.Email;
} }
/// <summary> /// <summary>
/// Gets the ArcGISOnline access token /// Gets the ArcGIS Online access token
/// </summary> /// </summary>
public string AccessToken { get; private set; } public string AccessToken { get; private set; }
/// <summary> /// <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> /// </summary>
public string Id { get; } public string Id { get; }
@@ -54,7 +60,7 @@ namespace Owin.Security.Providers.ArcGISOnline
public string Link { get; private set; } public string Link { get; private set; }
/// <summary> /// <summary>
/// Gets the ArcGISOnline username /// Gets the ArcGIS Online username
/// </summary> /// </summary>
public string UserName { get; private set; } public string UserName { get; private set; }

View File

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

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

@@ -187,7 +187,12 @@ namespace Owin.Security.Providers.Salesforce
var state = Options.StateDataFormat.Protect(properties); var state = Options.StateDataFormat.Protect(properties);
var authorizationEndpoint = var authorizationEndpoint =
$"{Options.Endpoints.AuthorizationEndpoint}?response_type={"code"}&client_id={Options.ClientId}&redirect_uri={HttpUtility.UrlEncode(redirectUri)}&display={"page"}&immediate={false}&state={Uri.EscapeDataString(state)}&scope={""}"; $"{Options.Endpoints.AuthorizationEndpoint}?response_type={"code"}&client_id={Options.ClientId}&redirect_uri={HttpUtility.UrlEncode(redirectUri)}&display={"page"}&immediate={false}&state={Uri.EscapeDataString(state)}";
if (Options.Scope != null && Options.Scope.Count > 0)
{
authorizationEndpoint += $"&scope={string.Join(" ", Options.Scope)}";
}
if (Options.Prompt != null) if (Options.Prompt != null)
{ {

View File

@@ -16,7 +16,8 @@ namespace Owin.Security.Providers.Slack.Provider
/// <param name="user">The JSON-serialized user</param> /// <param name="user">The JSON-serialized user</param>
/// <param name="accessToken">Slack Access token</param> /// <param name="accessToken">Slack Access token</param>
/// <param name="scope">Indicates access level of application</param> /// <param name="scope">Indicates access level of application</param>
public SlackAuthenticatedContext(IOwinContext context, JObject user, string accessToken, string scope) /// <param name="incomingWebhook">Contains channel infomation about the choosen default channel</param>
public SlackAuthenticatedContext(IOwinContext context, JObject user, string accessToken, string scope, JObject incomingWebhook)
: base(context) : base(context)
{ {
User = user; User = user;
@@ -28,6 +29,14 @@ namespace Owin.Security.Providers.Slack.Provider
TeamId = TryGetValue(user, "team_id"); TeamId = TryGetValue(user, "team_id");
TeamName = TryGetValue(user, "team"); TeamName = TryGetValue(user, "team");
TeamUrl = TryGetValue(user, "url"); TeamUrl = TryGetValue(user, "url");
if (incomingWebhook != null)
{
ChannelId = TryGetValue(incomingWebhook, "channel_id");
ChannelName = TryGetValue(incomingWebhook, "channel");
ConfigurationUrl = TryGetValue(incomingWebhook, "configuration_url");
HooksUrl = TryGetValue(incomingWebhook, "url");
}
} }
/// <summary> /// <summary>
@@ -73,6 +82,27 @@ namespace Owin.Security.Providers.Slack.Provider
/// </summary> /// </summary>
public string UserName { get; private set; } public string UserName { get; private set; }
/// <summary>
/// Gets the name of the channel choose at oauth time
/// </summary>
public string ChannelName { get; private set; }
/// <summary>
/// Gets the channel ID of the channel choosen at oauth time
/// </summary>
public string ChannelId { get; private set; }
/// <summary>
/// Gets the confiquration url
/// </summary>
public string ConfigurationUrl { get; private set; }
/// <summary>
/// Gets the hook url
/// </summary>
public string HooksUrl { get; private set; }
/// <summary> /// <summary>
/// Gets the <see cref="ClaimsIdentity"/> representing the user /// Gets the <see cref="ClaimsIdentity"/> representing the user
/// </summary> /// </summary>

View File

@@ -95,7 +95,7 @@ namespace Owin.Security.Providers.Slack
dynamic response = JsonConvert.DeserializeObject<dynamic>(text); dynamic response = JsonConvert.DeserializeObject<dynamic>(text);
var accessToken = (string)response.access_token; var accessToken = (string)response.access_token;
var scope = (string)response.scope; var scope = (string)response.scope;
var incomingWebhookChannelInfo = response.incoming_webhook;
// Get the Slack user // Get the Slack user
var userRequest = new HttpRequestMessage(HttpMethod.Get, UserInfoEndpoint + "?token=" + Uri.EscapeDataString(accessToken)); var userRequest = new HttpRequestMessage(HttpMethod.Get, UserInfoEndpoint + "?token=" + Uri.EscapeDataString(accessToken));
@@ -104,7 +104,7 @@ namespace Owin.Security.Providers.Slack
text = await userResponse.Content.ReadAsStringAsync(); text = await userResponse.Content.ReadAsStringAsync();
var user = JObject.Parse(text); var user = JObject.Parse(text);
var context = new SlackAuthenticatedContext(Context, user, accessToken, scope) var context = new SlackAuthenticatedContext(Context, user, accessToken, scope, incomingWebhookChannelInfo)
{ {
Identity = new ClaimsIdentity( Identity = new ClaimsIdentity(
Options.AuthenticationType, Options.AuthenticationType,
@@ -131,6 +131,10 @@ namespace Owin.Security.Providers.Slack
{ {
context.Identity.AddClaim(new Claim(ClaimTypes.Webpage, context.TeamUrl, XmlSchemaString, Options.AuthenticationType)); context.Identity.AddClaim(new Claim(ClaimTypes.Webpage, context.TeamUrl, XmlSchemaString, Options.AuthenticationType));
} }
if (!string.IsNullOrEmpty(context.ChannelId))
{
context.Identity.AddClaim(new Claim("urn:slack:channel_id", context.ChannelId, XmlSchemaString, Options.AuthenticationType));
}
context.Properties = properties; context.Properties = properties;
await Options.Provider.Authenticated(context); await Options.Provider.Authenticated(context);