Port Facebook middleware from Katana.
This commit is contained in:
parent
fabdb3b9df
commit
919fa0c195
27
Security.sln
27
Security.sln
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
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.21916.0
|
VisualStudioVersion = 14.0.22013.1
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4D2B6A51-2F9F-44F5-8131-EA5CAC053652}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4D2B6A51-2F9F-44F5-8131-EA5CAC053652}"
|
||||||
EndProject
|
EndProject
|
||||||
|
|
@ -22,6 +22,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||||
global.json = global.json
|
global.json = global.json
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Security.Facebook", "src\Microsoft.AspNet.Security.Facebook\Microsoft.AspNet.Security.Facebook.kproj", "{3984651C-FD44-4394-8793-3D14EE348C04}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SocialSample", "samples\SocialSample\SocialSample.kproj", "{8C73D216-332D-41D8-BFD0-45BC4BC36552}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
|
@ -73,6 +76,26 @@ Global
|
||||||
{8DA26CD1-1302-4CFD-9270-9FA1B7C6138B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
{8DA26CD1-1302-4CFD-9270-9FA1B7C6138B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
{8DA26CD1-1302-4CFD-9270-9FA1B7C6138B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
{8DA26CD1-1302-4CFD-9270-9FA1B7C6138B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
{8DA26CD1-1302-4CFD-9270-9FA1B7C6138B}.Release|x86.ActiveCfg = Release|Any CPU
|
{8DA26CD1-1302-4CFD-9270-9FA1B7C6138B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{3984651C-FD44-4394-8793-3D14EE348C04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{3984651C-FD44-4394-8793-3D14EE348C04}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{3984651C-FD44-4394-8793-3D14EE348C04}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{3984651C-FD44-4394-8793-3D14EE348C04}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{3984651C-FD44-4394-8793-3D14EE348C04}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{3984651C-FD44-4394-8793-3D14EE348C04}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{3984651C-FD44-4394-8793-3D14EE348C04}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{3984651C-FD44-4394-8793-3D14EE348C04}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{3984651C-FD44-4394-8793-3D14EE348C04}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{3984651C-FD44-4394-8793-3D14EE348C04}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{8C73D216-332D-41D8-BFD0-45BC4BC36552}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8C73D216-332D-41D8-BFD0-45BC4BC36552}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8C73D216-332D-41D8-BFD0-45BC4BC36552}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{8C73D216-332D-41D8-BFD0-45BC4BC36552}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{8C73D216-332D-41D8-BFD0-45BC4BC36552}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{8C73D216-332D-41D8-BFD0-45BC4BC36552}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8C73D216-332D-41D8-BFD0-45BC4BC36552}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8C73D216-332D-41D8-BFD0-45BC4BC36552}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{8C73D216-332D-41D8-BFD0-45BC4BC36552}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{8C73D216-332D-41D8-BFD0-45BC4BC36552}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
@ -82,5 +105,7 @@ Global
|
||||||
{15F1211B-B695-4A1C-B730-1AC58FC91090} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
|
{15F1211B-B695-4A1C-B730-1AC58FC91090} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
|
||||||
{558C2C2A-AED8-49DE-BB60-D5F8AE06C714} = {F8C0AA27-F3FB-4286-8E4C-47EF86B539FF}
|
{558C2C2A-AED8-49DE-BB60-D5F8AE06C714} = {F8C0AA27-F3FB-4286-8E4C-47EF86B539FF}
|
||||||
{8DA26CD1-1302-4CFD-9270-9FA1B7C6138B} = {7BF11F3A-60B6-4796-B504-579C67FFBA34}
|
{8DA26CD1-1302-4CFD-9270-9FA1B7C6138B} = {7BF11F3A-60B6-4796-B504-579C67FFBA34}
|
||||||
|
{3984651C-FD44-4394-8793-3D14EE348C04} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
|
||||||
|
{8C73D216-332D-41D8-BFD0-45BC4BC36552} = {F8C0AA27-F3FB-4286-8E4C-47EF86B539FF}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.AspNet.Hosting": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Http": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Diagnostics": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Security": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Security.Cookies": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Security.Facebook": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
|
||||||
|
"Microsoft.Framework.DependencyInjection": "1.0.0-*"
|
||||||
|
},
|
||||||
|
"commands": { "web": "Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:12345" },
|
||||||
|
"frameworks": {
|
||||||
|
"net45": {
|
||||||
|
},
|
||||||
|
"k10": {
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.0.10.0",
|
||||||
|
"System.Console": "4.0.0.0",
|
||||||
|
"System.Diagnostics.Debug": "4.0.10.0",
|
||||||
|
"System.Diagnostics.Tools": "4.0.0.0",
|
||||||
|
"System.Globalization": "4.0.10.0",
|
||||||
|
"System.IO": "4.0.0.0",
|
||||||
|
"System.Linq": "4.0.0.0",
|
||||||
|
"System.Resources.ResourceManager": "4.0.0.0",
|
||||||
|
"System.Runtime": "4.0.20.0",
|
||||||
|
"System.Runtime.Extensions": "4.0.10.0",
|
||||||
|
"System.Runtime.InteropServices": "4.0.20.0",
|
||||||
|
"System.Threading.Tasks": "4.0.10.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="__ToolsVersion__" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">12.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>8c73d216-332d-41d8-bfd0-45bc4bc36552</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="$(OutputType) == 'Console'">
|
||||||
|
<DebuggerFlavor>ConsoleDebugger</DebuggerFlavor>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="$(OutputType) == 'Web'">
|
||||||
|
<DebuggerFlavor>WebDebugger</DebuggerFlavor>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Project.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Startup.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Security.Cookies;
|
||||||
|
using Microsoft.AspNet.Security.Facebook;
|
||||||
|
|
||||||
|
namespace CookieSample
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
public void Configure(IBuilder app)
|
||||||
|
{
|
||||||
|
app.UseErrorPage();
|
||||||
|
|
||||||
|
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
|
||||||
|
|
||||||
|
app.UseCookieAuthentication(new CookieAuthenticationOptions()
|
||||||
|
{
|
||||||
|
});
|
||||||
|
|
||||||
|
app.UseFacebookAuthentication(new FacebookAuthenticationOptions()
|
||||||
|
{
|
||||||
|
AppId = "569522623154478",
|
||||||
|
AppSecret = "a124463c4719c94b4228d9a240e5dc1a",
|
||||||
|
});
|
||||||
|
|
||||||
|
app.Run(async context =>
|
||||||
|
{
|
||||||
|
if (!context.User.Identity.IsAuthenticated)
|
||||||
|
{
|
||||||
|
context.Response.Challenge("Facebook");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Response.ContentType = "text/plain";
|
||||||
|
await context.Response.WriteAsync("Hello " + context.User.Identity.Name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,7 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
|
||||||
using Microsoft.AspNet.Http;
|
|
||||||
using Microsoft.AspNet.Security.Cookies;
|
using Microsoft.AspNet.Security.Cookies;
|
||||||
using Microsoft.AspNet.Security.DataProtection;
|
|
||||||
using Microsoft.Framework.DependencyInjection;
|
|
||||||
using Microsoft.Framework.Logging;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Builder
|
namespace Microsoft.AspNet.Builder
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
"Microsoft.AspNet.HttpFeature": "1.0.0-*",
|
"Microsoft.AspNet.HttpFeature": "1.0.0-*",
|
||||||
"Microsoft.AspNet.PipelineCore": "1.0.0-*",
|
"Microsoft.AspNet.PipelineCore": "1.0.0-*",
|
||||||
"Microsoft.AspNet.RequestContainer": "1.0.0-*",
|
"Microsoft.AspNet.RequestContainer": "1.0.0-*",
|
||||||
"Microsoft.AspNet.Security": "",
|
"Microsoft.AspNet.Security": "1.0.0-*",
|
||||||
"Microsoft.AspNet.Security.DataProtection": "1.0.0-*",
|
"Microsoft.AspNet.Security.DataProtection": "1.0.0-*",
|
||||||
"Microsoft.Framework.DependencyInjection": "1.0.0-*",
|
"Microsoft.Framework.DependencyInjection": "1.0.0-*",
|
||||||
"Microsoft.Framework.Logging": "1.0.0-*",
|
"Microsoft.Framework.Logging": "1.0.0-*",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Security.Facebook
|
||||||
|
{
|
||||||
|
public static class FacebookAuthenticationDefaults
|
||||||
|
{
|
||||||
|
public const string AuthenticationType = "Facebook";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Security.Facebook;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Builder
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for using <see cref="FacebookAuthenticationMiddleware"/>
|
||||||
|
/// </summary>
|
||||||
|
public static class FacebookAuthenticationExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Authenticate users using Facebook
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="app">The <see cref="IBuilder"/> passed to the configure method</param>
|
||||||
|
/// <param name="appId">The appId assigned by Facebook</param>
|
||||||
|
/// <param name="appSecret">The appSecret assigned by Facebook</param>
|
||||||
|
/// <returns>The updated <see cref="IBuilder"/></returns>
|
||||||
|
public static IBuilder UseFacebookAuthentication([NotNull] this IBuilder app, [NotNull] string appId, [NotNull] string appSecret)
|
||||||
|
{
|
||||||
|
return app.UseFacebookAuthentication(new FacebookAuthenticationOptions()
|
||||||
|
{
|
||||||
|
AppId = appId,
|
||||||
|
AppSecret = appSecret,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Authenticate users using Facebook
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="app">The <see cref="IBuilder"/> passed to the configure method</param>
|
||||||
|
/// <param name="options">Middleware configuration options</param>
|
||||||
|
/// <returns>The updated <see cref="IBuilder"/></returns>
|
||||||
|
public static IBuilder UseFacebookAuthentication([NotNull] this IBuilder app, [NotNull] FacebookAuthenticationOptions options)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(options.SignInAsAuthenticationType))
|
||||||
|
{
|
||||||
|
options.SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType();
|
||||||
|
}
|
||||||
|
return app.UseMiddleware<FacebookAuthenticationMiddleware>(options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,293 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Http.Security;
|
||||||
|
using Microsoft.AspNet.Security.Infrastructure;
|
||||||
|
using Microsoft.AspNet.WebUtilities;
|
||||||
|
using Microsoft.Framework.Logging;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Security.Facebook
|
||||||
|
{
|
||||||
|
internal class FacebookAuthenticationHandler : AuthenticationHandler<FacebookAuthenticationOptions>
|
||||||
|
{
|
||||||
|
private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
|
||||||
|
private const string TokenEndpoint = "https://graph.facebook.com/oauth/access_token";
|
||||||
|
private const string GraphApiEndpoint = "https://graph.facebook.com/me";
|
||||||
|
private const string AuthorizationEndpoint = "https://www.facebook.com/dialog/oauth";
|
||||||
|
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
|
||||||
|
public FacebookAuthenticationHandler(HttpClient httpClient, ILogger logger)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override AuthenticationTicket AuthenticateCore()
|
||||||
|
{
|
||||||
|
return AuthenticateCoreAsync().Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
|
||||||
|
{
|
||||||
|
AuthenticationProperties properties = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string code = null;
|
||||||
|
string state = null;
|
||||||
|
|
||||||
|
IReadableStringCollection query = Request.Query;
|
||||||
|
|
||||||
|
IList<string> values = query.GetValues("error");
|
||||||
|
if (values != null && values.Count >= 1)
|
||||||
|
{
|
||||||
|
_logger.WriteVerbose("Remote server returned an error: " + Request.QueryString);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code == null)
|
||||||
|
{
|
||||||
|
// Null if the remote server returns an error.
|
||||||
|
return new AuthenticationTicket(null, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
string requestPrefix = Request.Scheme + "://" + Request.Host;
|
||||||
|
string redirectUri = requestPrefix + Request.PathBase + Options.CallbackPath;
|
||||||
|
|
||||||
|
string tokenRequest = "grant_type=authorization_code" +
|
||||||
|
"&code=" + Uri.EscapeDataString(code) +
|
||||||
|
"&redirect_uri=" + Uri.EscapeDataString(redirectUri) +
|
||||||
|
"&client_id=" + Uri.EscapeDataString(Options.AppId) +
|
||||||
|
"&client_secret=" + Uri.EscapeDataString(Options.AppSecret);
|
||||||
|
|
||||||
|
var tokenResponse = await _httpClient.GetAsync(TokenEndpoint + "?" + tokenRequest, Context.RequestAborted);
|
||||||
|
tokenResponse.EnsureSuccessStatusCode();
|
||||||
|
string text = await tokenResponse.Content.ReadAsStringAsync();
|
||||||
|
IFormCollection form = FormHelpers.ParseForm(text);
|
||||||
|
|
||||||
|
string accessToken = form["access_token"];
|
||||||
|
string expires = form["expires"];
|
||||||
|
string graphAddress = GraphApiEndpoint + "?access_token=" + Uri.EscapeDataString(accessToken);
|
||||||
|
if (Options.SendAppSecretProof)
|
||||||
|
{
|
||||||
|
graphAddress += "&appsecret_proof=" + GenerateAppSecretProof(accessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
var graphResponse = await _httpClient.GetAsync(graphAddress, Context.RequestAborted);
|
||||||
|
graphResponse.EnsureSuccessStatusCode();
|
||||||
|
text = await graphResponse.Content.ReadAsStringAsync();
|
||||||
|
JObject user = JObject.Parse(text);
|
||||||
|
|
||||||
|
var context = new FacebookAuthenticatedContext(Context, user, accessToken, expires);
|
||||||
|
context.Identity = new ClaimsIdentity(
|
||||||
|
Options.AuthenticationType,
|
||||||
|
ClaimsIdentity.DefaultNameClaimType,
|
||||||
|
ClaimsIdentity.DefaultRoleClaimType);
|
||||||
|
if (!string.IsNullOrEmpty(context.Id))
|
||||||
|
{
|
||||||
|
context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.Id, XmlSchemaString, Options.AuthenticationType));
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(context.UserName))
|
||||||
|
{
|
||||||
|
context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.UserName, XmlSchemaString, Options.AuthenticationType));
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(context.Email))
|
||||||
|
{
|
||||||
|
context.Identity.AddClaim(new Claim(ClaimTypes.Email, context.Email, XmlSchemaString, Options.AuthenticationType));
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(context.Name))
|
||||||
|
{
|
||||||
|
context.Identity.AddClaim(new Claim("urn:facebook:name", context.Name, XmlSchemaString, Options.AuthenticationType));
|
||||||
|
|
||||||
|
// Many Facebook accounts do not set the UserName field. Fall back to the Name field instead.
|
||||||
|
if (string.IsNullOrEmpty(context.UserName))
|
||||||
|
{
|
||||||
|
context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.Name, XmlSchemaString, Options.AuthenticationType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(context.Link))
|
||||||
|
{
|
||||||
|
context.Identity.AddClaim(new Claim("urn:facebook:link", context.Link, XmlSchemaString, Options.AuthenticationType));
|
||||||
|
}
|
||||||
|
context.Properties = properties;
|
||||||
|
|
||||||
|
await Options.Notifications.Authenticated(context);
|
||||||
|
|
||||||
|
return new AuthenticationTicket(context.Identity, context.Properties);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.WriteError("Authentication failed", ex);
|
||||||
|
return new AuthenticationTicket(null, properties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ApplyResponseChallenge()
|
||||||
|
{
|
||||||
|
if (Response.StatusCode != 401)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Active middleware should redirect on 401 even if there wasn't an explicit challenge.
|
||||||
|
if (ChallengeContext == null && Options.AuthenticationMode == AuthenticationMode.Passive)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string baseUri =
|
||||||
|
Request.Scheme +
|
||||||
|
"://" +
|
||||||
|
Request.Host +
|
||||||
|
Request.PathBase;
|
||||||
|
|
||||||
|
string currentUri =
|
||||||
|
baseUri +
|
||||||
|
Request.Path +
|
||||||
|
Request.QueryString;
|
||||||
|
|
||||||
|
string redirectUri =
|
||||||
|
baseUri +
|
||||||
|
Options.CallbackPath;
|
||||||
|
|
||||||
|
AuthenticationProperties properties;
|
||||||
|
if (ChallengeContext == null)
|
||||||
|
{
|
||||||
|
properties = new AuthenticationProperties();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
properties = new AuthenticationProperties(ChallengeContext.Properties);
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(properties.RedirectUri))
|
||||||
|
{
|
||||||
|
properties.RedirectUri = currentUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
// OAuth2 10.12 CSRF
|
||||||
|
GenerateCorrelationId(properties);
|
||||||
|
|
||||||
|
// comma separated
|
||||||
|
string scope = string.Join(",", Options.Scope);
|
||||||
|
|
||||||
|
string state = Options.StateDataFormat.Protect(properties);
|
||||||
|
|
||||||
|
string authorizationEndpoint =
|
||||||
|
AuthorizationEndpoint +
|
||||||
|
"?response_type=code" +
|
||||||
|
"&client_id=" + Uri.EscapeDataString(Options.AppId) +
|
||||||
|
"&redirect_uri=" + Uri.EscapeDataString(redirectUri) +
|
||||||
|
"&scope=" + Uri.EscapeDataString(scope) +
|
||||||
|
"&state=" + Uri.EscapeDataString(state);
|
||||||
|
|
||||||
|
var redirectContext = new FacebookApplyRedirectContext(Context, Options, properties, authorizationEndpoint);
|
||||||
|
Options.Notifications.ApplyRedirect(redirectContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ApplyResponseGrant()
|
||||||
|
{
|
||||||
|
// N/A
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<bool> InvokeAsync()
|
||||||
|
{
|
||||||
|
return await InvokeReplyPathAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> InvokeReplyPathAsync()
|
||||||
|
{
|
||||||
|
if (Options.CallbackPath.HasValue && Options.CallbackPath == Request.Path)
|
||||||
|
{
|
||||||
|
// TODO: error responses
|
||||||
|
|
||||||
|
AuthenticationTicket ticket = await AuthenticateAsync();
|
||||||
|
if (ticket == null)
|
||||||
|
{
|
||||||
|
_logger.WriteWarning("Invalid return state, unable to redirect.");
|
||||||
|
Response.StatusCode = 500;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var context = new FacebookReturnEndpointContext(Context, ticket);
|
||||||
|
context.SignInAsAuthenticationType = Options.SignInAsAuthenticationType;
|
||||||
|
context.RedirectUri = ticket.Properties.RedirectUri;
|
||||||
|
|
||||||
|
await Options.Notifications.ReturnEndpoint(context);
|
||||||
|
|
||||||
|
if (context.SignInAsAuthenticationType != null &&
|
||||||
|
context.Identity != null)
|
||||||
|
{
|
||||||
|
ClaimsIdentity grantIdentity = context.Identity;
|
||||||
|
if (!string.Equals(grantIdentity.AuthenticationType, context.SignInAsAuthenticationType, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
grantIdentity = new ClaimsIdentity(grantIdentity.Claims, context.SignInAsAuthenticationType, grantIdentity.NameClaimType, grantIdentity.RoleClaimType);
|
||||||
|
}
|
||||||
|
Context.Response.SignIn(context.Properties, grantIdentity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context.IsRequestCompleted && context.RedirectUri != null)
|
||||||
|
{
|
||||||
|
string redirectUri = context.RedirectUri;
|
||||||
|
if (context.Identity == null)
|
||||||
|
{
|
||||||
|
// add a redirect hint that sign-in failed in some way
|
||||||
|
redirectUri = QueryHelpers.AddQueryString(redirectUri, "error", "access_denied");
|
||||||
|
}
|
||||||
|
Response.Redirect(redirectUri);
|
||||||
|
context.RequestCompleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.IsRequestCompleted;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GenerateAppSecretProof(string accessToken)
|
||||||
|
{
|
||||||
|
using (HMACSHA256 algorithm = new HMACSHA256(Encoding.ASCII.GetBytes(Options.AppSecret)))
|
||||||
|
{
|
||||||
|
byte[] hash = algorithm.ComputeHash(Encoding.ASCII.GetBytes(accessToken));
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for (int i = 0; i < hash.Length; i++)
|
||||||
|
{
|
||||||
|
builder.Append(hash[i].ToString("x2", CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Net.Http;
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.AspNet.Security.DataHandler;
|
||||||
|
using Microsoft.AspNet.Security.DataProtection;
|
||||||
|
using Microsoft.AspNet.Security.Infrastructure;
|
||||||
|
using Microsoft.Framework.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Security.Facebook
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ASP.NET middleware for authenticating users using Facebook
|
||||||
|
/// </summary>
|
||||||
|
[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "Middleware is not disposable.")]
|
||||||
|
public class FacebookAuthenticationMiddleware : AuthenticationMiddleware<FacebookAuthenticationOptions>
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a <see cref="FacebookAuthenticationMiddleware"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="next">The next middleware in the application pipeline to invoke</param>
|
||||||
|
/// <param name="options">Configuration options for the middleware</param>
|
||||||
|
public FacebookAuthenticationMiddleware(
|
||||||
|
RequestDelegate next,
|
||||||
|
IDataProtectionProvider dataProtectionProvider,
|
||||||
|
ILoggerFactory loggerFactory,
|
||||||
|
IServiceProvider services,
|
||||||
|
FacebookAuthenticationOptions options)
|
||||||
|
: base(next, options)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(Options.AppId))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, "AppId"));
|
||||||
|
}
|
||||||
|
if (string.IsNullOrWhiteSpace(Options.AppSecret))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_OptionMustBeProvided, "AppSecret"));
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger = loggerFactory.Create(typeof(FacebookAuthenticationMiddleware).FullName);
|
||||||
|
|
||||||
|
if (Options.Notifications == null)
|
||||||
|
{
|
||||||
|
Options.Notifications = new FacebookAuthenticationNotifications();
|
||||||
|
}
|
||||||
|
if (Options.StateDataFormat == null)
|
||||||
|
{
|
||||||
|
IDataProtector dataProtector = DataProtectionHelpers.CreateDataProtector(dataProtectionProvider,
|
||||||
|
typeof(FacebookAuthenticationMiddleware).FullName, options.AuthenticationType, "v1");
|
||||||
|
Options.StateDataFormat = new PropertiesDataFormat(dataProtector);
|
||||||
|
}
|
||||||
|
|
||||||
|
_httpClient = new HttpClient(ResolveHttpMessageHandler(Options));
|
||||||
|
_httpClient.Timeout = Options.BackchannelTimeout;
|
||||||
|
_httpClient.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides the <see cref="AuthenticationHandler"/> object for processing authentication-related requests.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An <see cref="AuthenticationHandler"/> configured with the <see cref="FacebookAuthenticationOptions"/> supplied to the constructor.</returns>
|
||||||
|
protected override AuthenticationHandler<FacebookAuthenticationOptions> CreateHandler()
|
||||||
|
{
|
||||||
|
return new FacebookAuthenticationHandler(_httpClient, _logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Managed by caller")]
|
||||||
|
private static HttpMessageHandler ResolveHttpMessageHandler(FacebookAuthenticationOptions options)
|
||||||
|
{
|
||||||
|
HttpMessageHandler handler = options.BackchannelHttpHandler ??
|
||||||
|
#if NET45
|
||||||
|
new WebRequestHandler();
|
||||||
|
// If they provided a validator, apply it or fail.
|
||||||
|
if (options.BackchannelCertificateValidator != null)
|
||||||
|
{
|
||||||
|
// Set the cert validate callback
|
||||||
|
var webRequestHandler = handler as WebRequestHandler;
|
||||||
|
if (webRequestHandler == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(Resources.Exception_ValidatorHandlerMismatch);
|
||||||
|
}
|
||||||
|
webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
new WinHttpHandler();
|
||||||
|
#endif
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Net.Http;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Http.Security;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Security.Facebook
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configuration options for <see cref="FacebookAuthenticationMiddleware"/>
|
||||||
|
/// </summary>
|
||||||
|
public class FacebookAuthenticationOptions : AuthenticationOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new <see cref="FacebookAuthenticationOptions"/>
|
||||||
|
/// </summary>
|
||||||
|
[SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters",
|
||||||
|
MessageId = "Microsoft.AspNet.Security.Facebook.FacebookAuthenticationOptions.set_Caption(System.String)", Justification = "Not localizable.")]
|
||||||
|
public FacebookAuthenticationOptions()
|
||||||
|
: base(FacebookAuthenticationDefaults.AuthenticationType)
|
||||||
|
{
|
||||||
|
Caption = FacebookAuthenticationDefaults.AuthenticationType;
|
||||||
|
CallbackPath = new PathString("/signin-facebook");
|
||||||
|
AuthenticationMode = AuthenticationMode.Passive;
|
||||||
|
Scope = new List<string>();
|
||||||
|
BackchannelTimeout = TimeSpan.FromSeconds(60);
|
||||||
|
SendAppSecretProof = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Facebook-assigned appId
|
||||||
|
/// </summary>
|
||||||
|
public string AppId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Facebook-assigned app secret
|
||||||
|
/// </summary>
|
||||||
|
public string AppSecret { get; set; }
|
||||||
|
#if NET45
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the a pinned certificate validator to use to validate the endpoints used
|
||||||
|
/// in back channel communications belong to Facebook.
|
||||||
|
/// </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; }
|
||||||
|
#endif
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets timeout value in milliseconds for back channel communications with Facebook.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// The back channel timeout in milliseconds.
|
||||||
|
/// </value>
|
||||||
|
public TimeSpan BackchannelTimeout { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The HttpMessageHandler used to communicate with Facebook.
|
||||||
|
/// 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>
|
||||||
|
/// 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>
|
||||||
|
/// 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-facebook".
|
||||||
|
/// </summary>
|
||||||
|
public PathString CallbackPath { 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 <see cref="IFacebookAuthenticationNotifications"/> used to handle authentication events.
|
||||||
|
/// </summary>
|
||||||
|
public IFacebookAuthenticationNotifications Notifications { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the type used to secure data handled by the middleware.
|
||||||
|
/// </summary>
|
||||||
|
public ISecureDataFormat<AuthenticationProperties> StateDataFormat { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A list of permissions to request.
|
||||||
|
/// </summary>
|
||||||
|
public IList<string> Scope { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets if the appsecret_proof should be generated and sent with Facebook API calls.
|
||||||
|
/// This is enabled by default.
|
||||||
|
/// </summary>
|
||||||
|
public bool SendAppSecretProof { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="__ToolsVersion__" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">12.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>3984651c-fd44-4394-8793-3d14ee348c04</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Security.Facebook
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
|
||||||
|
internal sealed class NotNullAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Http.Security;
|
||||||
|
using Microsoft.AspNet.Security.Notifications;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Security.Facebook
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Context passed when a Challenge causes a redirect to authorize endpoint in the Facebook middleware
|
||||||
|
/// </summary>
|
||||||
|
public class FacebookApplyRedirectContext : BaseContext<FacebookAuthenticationOptions>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new context object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The http request context</param>
|
||||||
|
/// <param name="options">The Facebook middleware options</param>
|
||||||
|
/// <param name="properties">The authenticaiton properties of the challenge</param>
|
||||||
|
/// <param name="redirectUri">The initial redirect URI</param>
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "3#",
|
||||||
|
Justification = "Represents header value")]
|
||||||
|
public FacebookApplyRedirectContext(HttpContext context, FacebookAuthenticationOptions options,
|
||||||
|
AuthenticationProperties properties, string redirectUri)
|
||||||
|
: base(context, options)
|
||||||
|
{
|
||||||
|
RedirectUri = redirectUri;
|
||||||
|
Properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the URI used for the redirect operation.
|
||||||
|
/// </summary>
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "Represents header value")]
|
||||||
|
public string RedirectUri { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the authentication properties of the challenge
|
||||||
|
/// </summary>
|
||||||
|
public AuthenticationProperties Properties { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Http.Security;
|
||||||
|
using Microsoft.AspNet.Security.Notifications;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Security.Facebook
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class FacebookAuthenticatedContext : BaseContext
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a <see cref="FacebookAuthenticatedContext"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The http environment</param>
|
||||||
|
/// <param name="user">The JSON-serialized user</param>
|
||||||
|
/// <param name="accessToken">Facebook Access token</param>
|
||||||
|
/// <param name="expires">Seconds until expiration</param>
|
||||||
|
public FacebookAuthenticatedContext(HttpContext context, JObject user, string accessToken, string expires)
|
||||||
|
: base(context)
|
||||||
|
{
|
||||||
|
User = user;
|
||||||
|
AccessToken = accessToken;
|
||||||
|
|
||||||
|
int expiresValue;
|
||||||
|
if (Int32.TryParse(expires, NumberStyles.Integer, CultureInfo.InvariantCulture, out expiresValue))
|
||||||
|
{
|
||||||
|
ExpiresIn = TimeSpan.FromSeconds(expiresValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
Id = TryGetValue(user, "id");
|
||||||
|
Name = TryGetValue(user, "name");
|
||||||
|
Link = TryGetValue(user, "link");
|
||||||
|
UserName = TryGetValue(user, "username");
|
||||||
|
Email = TryGetValue(user, "email");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the JSON-serialized user
|
||||||
|
/// </summary>
|
||||||
|
public JObject User { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Facebook access token
|
||||||
|
/// </summary>
|
||||||
|
public string AccessToken { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Facebook access token expiration time
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan? ExpiresIn { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Facebook user ID
|
||||||
|
/// </summary>
|
||||||
|
public string Id { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the user's name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; private set; }
|
||||||
|
|
||||||
|
public string Link { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Facebook username
|
||||||
|
/// </summary>
|
||||||
|
public string UserName { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Facebook email
|
||||||
|
/// </summary>
|
||||||
|
public string Email { 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Security.Facebook
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Default <see cref="IFacebookAuthenticationProvider"/> implementation.
|
||||||
|
/// </summary>
|
||||||
|
public class FacebookAuthenticationNotifications : IFacebookAuthenticationNotifications
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a <see cref="FacebookAuthenticationNotifications"/>
|
||||||
|
/// </summary>
|
||||||
|
public FacebookAuthenticationNotifications()
|
||||||
|
{
|
||||||
|
OnAuthenticated = context => Task.FromResult<object>(null);
|
||||||
|
OnReturnEndpoint = context => Task.FromResult<object>(null);
|
||||||
|
OnApplyRedirect = context =>
|
||||||
|
context.Response.Redirect(context.RedirectUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the function that is invoked when the Authenticated method is invoked.
|
||||||
|
/// </summary>
|
||||||
|
public Func<FacebookAuthenticatedContext, Task> OnAuthenticated { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the function that is invoked when the ReturnEndpoint method is invoked.
|
||||||
|
/// </summary>
|
||||||
|
public Func<FacebookReturnEndpointContext, Task> OnReturnEndpoint { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the delegate that is invoked when the ApplyRedirect method is invoked.
|
||||||
|
/// </summary>
|
||||||
|
public Action<FacebookApplyRedirectContext> OnApplyRedirect { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked whenever Facebook succesfully authenticates a user
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||||
|
public virtual Task Authenticated(FacebookAuthenticatedContext 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(FacebookReturnEndpointContext context)
|
||||||
|
{
|
||||||
|
return OnReturnEndpoint(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when a Challenge causes a redirect to authorize endpoint in the Facebook middleware
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Contains redirect URI and <see cref="AuthenticationProperties"/> of the challenge </param>
|
||||||
|
public virtual void ApplyRedirect(FacebookApplyRedirectContext context)
|
||||||
|
{
|
||||||
|
OnApplyRedirect(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Security.Notifications;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Security.Facebook
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides context information to middleware providers.
|
||||||
|
/// </summary>
|
||||||
|
public class FacebookReturnEndpointContext : ReturnEndpointContext
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new context object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The http environment</param>
|
||||||
|
/// <param name="ticket">The authentication ticket</param>
|
||||||
|
public FacebookReturnEndpointContext(
|
||||||
|
HttpContext context,
|
||||||
|
AuthenticationTicket ticket)
|
||||||
|
: base(context, ticket)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Security.Facebook
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies callback methods which the <see cref="FacebookAuthenticationMiddleware"></see> invokes to enable developer control over the authentication process. />
|
||||||
|
/// </summary>
|
||||||
|
public interface IFacebookAuthenticationNotifications
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked whenever Facebook succesfully authenticates a user
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> representing the completed operation.</returns>
|
||||||
|
Task Authenticated(FacebookAuthenticatedContext 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(FacebookReturnEndpointContext context);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when a Challenge causes a redirect to authorize endpoint in the Facebook middleware
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Contains redirect URI and <see cref="AuthenticationProperties"/> of the challenge </param>
|
||||||
|
void ApplyRedirect(FacebookApplyRedirectContext context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0-*",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.AspNet.Http": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.RequestContainer": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Security": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Security.DataProtection": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.WebUtilities": "1.0.0-*",
|
||||||
|
"Microsoft.Framework.Logging": "1.0.0-*",
|
||||||
|
"Newtonsoft.Json": "5.0.8",
|
||||||
|
"System.Net.Http": "4.0.0.0"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net45": {
|
||||||
|
"dependencies": {
|
||||||
|
"System.Net.Http.WebRequest": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"k10": {
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.0.10.0",
|
||||||
|
"System.ComponentModel": "4.0.0.0",
|
||||||
|
"System.Console": "4.0.0.0",
|
||||||
|
"System.Diagnostics.Debug": "4.0.10.0",
|
||||||
|
"System.Diagnostics.Tools": "4.0.0.0",
|
||||||
|
"System.Globalization": "4.0.10.0",
|
||||||
|
"System.IO": "4.0.0.0",
|
||||||
|
"System.IO.Compression": "4.0.0.0",
|
||||||
|
"System.Linq": "4.0.0.0",
|
||||||
|
"System.Net.Http.WinHttpHandler": "4.0.0.0",
|
||||||
|
"System.Reflection": "4.0.10.0",
|
||||||
|
"System.Resources.ResourceManager": "4.0.0.0",
|
||||||
|
"System.Runtime": "4.0.20.0",
|
||||||
|
"System.Runtime.Extensions": "4.0.10.0",
|
||||||
|
"System.Runtime.InteropServices": "4.0.20.0",
|
||||||
|
"System.Security.Claims": "1.0.0-*",
|
||||||
|
"System.Security.Cryptography.Hashing.Algorithms": "4.0.0.0",
|
||||||
|
"System.Security.Principal": "4.0.0.0",
|
||||||
|
"System.Threading": "4.0.0.0",
|
||||||
|
"System.Threading.Tasks": "4.0.10.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.32559
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Security.Facebook {
|
||||||
|
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("Microsoft.AspNet.Security.Facebook.Resources", System.Reflection.IntrospectionExtensions.GetTypeInfo(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 '{0}' 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -1,16 +1,15 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
/* TODO:
|
|
||||||
using System;
|
using System;
|
||||||
using Owin;
|
using Microsoft.AspNet.Security;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Security
|
namespace Microsoft.AspNet.Builder
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides extensions methods for app.Property values that are only needed by implementations of authentication middleware.
|
/// Provides extensions methods for app.Property values that are only needed by implementations of authentication middleware.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class AppBuilderSecurityExtensions
|
public static class BuilderSecurityExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the previously set AuthenticationType that external sign in middleware should use when the
|
/// Returns the previously set AuthenticationType that external sign in middleware should use when the
|
||||||
|
|
@ -18,7 +17,7 @@ namespace Microsoft.AspNet.Security
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="app">App builder passed to the application startup code</param>
|
/// <param name="app">App builder passed to the application startup code</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static string GetDefaultSignInAsAuthenticationType([NotNull] this IAppBuilder app)
|
public static string GetDefaultSignInAsAuthenticationType([NotNull] this IBuilder app)
|
||||||
{
|
{
|
||||||
object value;
|
object value;
|
||||||
if (app.Properties.TryGetValue(Constants.DefaultSignInAsAuthenticationType, out value))
|
if (app.Properties.TryGetValue(Constants.DefaultSignInAsAuthenticationType, out value))
|
||||||
|
|
@ -38,10 +37,9 @@ namespace Microsoft.AspNet.Security
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="app">App builder passed to the application startup code</param>
|
/// <param name="app">App builder passed to the application startup code</param>
|
||||||
/// <param name="authenticationType">AuthenticationType that external middleware should sign in as.</param>
|
/// <param name="authenticationType">AuthenticationType that external middleware should sign in as.</param>
|
||||||
public static void SetDefaultSignInAsAuthenticationType([NotNull] this IAppBuilder app, [NotNull] string authenticationType)
|
public static void SetDefaultSignInAsAuthenticationType([NotNull] this IBuilder app, [NotNull] string authenticationType)
|
||||||
{
|
{
|
||||||
app.Properties[Constants.DefaultSignInAsAuthenticationType] = authenticationType;
|
app.Properties[Constants.DefaultSignInAsAuthenticationType] = authenticationType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
@ -12,6 +12,6 @@ namespace Microsoft.AspNet.Security
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used by middleware extension methods to coordinate the default value Options property SignInAsAuthenticationType
|
/// Used by middleware extension methods to coordinate the default value Options property SignInAsAuthenticationType
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string DefaultSignInAsAuthenticationType = "Microsoft.AspNet.Security.Constants.DefaultSignInAsAuthenticationType";
|
internal const string DefaultSignInAsAuthenticationType = "Microsoft.AspNet.Security.DefaultSignInAsAuthenticationType";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
|
||||||
using Microsoft.AspNet.Http.Security;
|
using Microsoft.AspNet.Http.Security;
|
||||||
using Microsoft.AspNet.Security.DataHandler.Encoder;
|
using Microsoft.AspNet.Security.DataHandler.Encoder;
|
||||||
using Microsoft.AspNet.Security.DataHandler.Serializer;
|
using Microsoft.AspNet.Security.DataHandler.Serializer;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Http.Security;
|
||||||
|
using Microsoft.AspNet.Security.Cookies;
|
||||||
|
using Microsoft.AspNet.TestHost;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Shouldly;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Security.Facebook
|
||||||
|
{
|
||||||
|
public class FacebookMiddlewareTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task ChallengeWillTriggerApplyRedirectEvent()
|
||||||
|
{
|
||||||
|
var options = new FacebookAuthenticationOptions()
|
||||||
|
{
|
||||||
|
AppId = "Test App Id",
|
||||||
|
AppSecret = "Test App Secret",
|
||||||
|
Notifications = new FacebookAuthenticationNotifications
|
||||||
|
{
|
||||||
|
OnApplyRedirect = context =>
|
||||||
|
{
|
||||||
|
context.Response.Redirect(context.RedirectUri + "&custom=test");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var server = CreateServer(
|
||||||
|
app => app.UseFacebookAuthentication(options),
|
||||||
|
context =>
|
||||||
|
{
|
||||||
|
context.Response.Challenge("Facebook");
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
var transaction = await SendAsync(server, "http://example.com/challenge");
|
||||||
|
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||||
|
var query = transaction.Response.Headers.Location.Query;
|
||||||
|
query.ShouldContain("custom=test");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ChallengeWillTriggerRedirection()
|
||||||
|
{
|
||||||
|
var server = CreateServer(
|
||||||
|
app => app.UseFacebookAuthentication("Test App Id", "Test App Secret"),
|
||||||
|
context =>
|
||||||
|
{
|
||||||
|
context.Response.Challenge("Facebook");
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
var transaction = await SendAsync(server, "http://example.com/challenge");
|
||||||
|
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||||
|
var location = transaction.Response.Headers.Location.AbsoluteUri;
|
||||||
|
location.ShouldContain("https://www.facebook.com/dialog/oauth");
|
||||||
|
location.ShouldContain("?response_type=code");
|
||||||
|
location.ShouldContain("&client_id=");
|
||||||
|
location.ShouldContain("&redirect_uri=");
|
||||||
|
location.ShouldContain("&scope=");
|
||||||
|
location.ShouldContain("&state=");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TestServer CreateServer(Action<IBuilder> configure, Func<HttpContext, bool> handler)
|
||||||
|
{
|
||||||
|
return TestServer.Create(app =>
|
||||||
|
{
|
||||||
|
app.SetDefaultSignInAsAuthenticationType("External");
|
||||||
|
app.UseCookieAuthentication(new CookieAuthenticationOptions()
|
||||||
|
{
|
||||||
|
AuthenticationType = "External"
|
||||||
|
});
|
||||||
|
if (configure != null)
|
||||||
|
{
|
||||||
|
configure(app);
|
||||||
|
}
|
||||||
|
app.Use(async (context, next) =>
|
||||||
|
{
|
||||||
|
if (handler == null || !handler(context))
|
||||||
|
{
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<Transaction> SendAsync(TestServer server, string uri, string cookieHeader = null)
|
||||||
|
{
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, uri);
|
||||||
|
if (!string.IsNullOrEmpty(cookieHeader))
|
||||||
|
{
|
||||||
|
request.Headers.Add("Cookie", cookieHeader);
|
||||||
|
}
|
||||||
|
var transaction = new Transaction
|
||||||
|
{
|
||||||
|
Request = request,
|
||||||
|
Response = await server.CreateClient().SendAsync(request),
|
||||||
|
};
|
||||||
|
if (transaction.Response.Headers.Contains("Set-Cookie"))
|
||||||
|
{
|
||||||
|
transaction.SetCookie = transaction.Response.Headers.GetValues("Set-Cookie").ToList();
|
||||||
|
}
|
||||||
|
transaction.ResponseText = await transaction.Response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
return transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Transaction
|
||||||
|
{
|
||||||
|
public HttpRequestMessage Request { get; set; }
|
||||||
|
public HttpResponseMessage Response { get; set; }
|
||||||
|
public IList<string> SetCookie { get; set; }
|
||||||
|
public string ResponseText { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,9 +3,12 @@
|
||||||
"warningsAsErrors": true
|
"warningsAsErrors": true
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"Microsoft.AspNet.Http": "1.0.0-*",
|
||||||
"Microsoft.AspNet.Security" : "",
|
"Microsoft.AspNet.Security" : "",
|
||||||
"Microsoft.AspNet.Security.Cookies" : "",
|
"Microsoft.AspNet.Security.Cookies" : "",
|
||||||
|
"Microsoft.AspNet.Security.Facebook" : "",
|
||||||
"Microsoft.AspNet.TestHost": "1.0.0-*",
|
"Microsoft.AspNet.TestHost": "1.0.0-*",
|
||||||
|
"Microsoft.Framework.DependencyInjection": "1.0.0-*",
|
||||||
"System.Net.Http": "4.0.0.0",
|
"System.Net.Http": "4.0.0.0",
|
||||||
"Moq": "4.2.1312.1622",
|
"Moq": "4.2.1312.1622",
|
||||||
"Xunit.KRunner": "1.0.0-*"
|
"Xunit.KRunner": "1.0.0-*"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue