Repository navigation
Expand file tree
/
Copy pathFacebookHandler.cs
More file actions
134 lines (114 loc) · 5.52 KB
/
Copy pathFacebookHandler.cs
File metadata and controls
134 lines (114 loc) · 5.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright (c) .NET Foundation. 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.Net.Http;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Authentication.OAuth;
using Microsoft.AspNet.Http.Authentication;
using Microsoft.AspNet.Http.Extensions;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.WebUtilities;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNet.Authentication.Facebook
{
internal class FacebookHandler : OAuthHandler<FacebookOptions>
{
public FacebookHandler(HttpClient httpClient)
: base(httpClient)
{
}
protected override async Task<OAuthTokenResponse> ExchangeCodeAsync(string code, string redirectUri)
{
var queryBuilder = new QueryBuilder()
{
{ "grant_type", "authorization_code" },
{ "code", code },
{ "redirect_uri", redirectUri },
{ "client_id", Options.AppId },
{ "client_secret", Options.AppSecret },
};
var response = await Backchannel.GetAsync(Options.TokenEndpoint + queryBuilder.ToString(), Context.RequestAborted);
response.EnsureSuccessStatusCode();
var form = new FormCollection(FormReader.ReadForm(await response.Content.ReadAsStringAsync()));
var payload = new JObject();
foreach (string key in form.Keys)
{
payload.Add(string.Equals(key, "expires", StringComparison.OrdinalIgnoreCase) ? "expires_in" : key, (string)form[key]);
}
// The refresh token is not available.
return new OAuthTokenResponse(payload);
}
protected override async Task<AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens)
{
var endpoint = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, "access_token", tokens.AccessToken);
if (Options.SendAppSecretProof)
{
endpoint = QueryHelpers.AddQueryString(endpoint, "appsecret_proof", GenerateAppSecretProof(tokens.AccessToken));
}
var response = await Backchannel.GetAsync(endpoint, Context.RequestAborted);
response.EnsureSuccessStatusCode();
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
var context = new OAuthCreatingTicketContext(Context, Options, Backchannel, tokens, payload)
{
Properties = properties,
Principal = new ClaimsPrincipal(identity)
};
var identifier = FacebookHelper.GetId(payload);
if (!string.IsNullOrEmpty(identifier))
{
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, identifier, ClaimValueTypes.String, Options.ClaimsIssuer));
}
var userName = FacebookHelper.GetUserName(payload);
if (!string.IsNullOrEmpty(userName))
{
identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, userName, ClaimValueTypes.String, Options.ClaimsIssuer));
}
var email = FacebookHelper.GetEmail(payload);
if (!string.IsNullOrEmpty(email))
{
identity.AddClaim(new Claim(ClaimTypes.Email, email, ClaimValueTypes.String, Options.ClaimsIssuer));
}
var name = FacebookHelper.GetName(payload);
if (!string.IsNullOrEmpty(name))
{
identity.AddClaim(new Claim("urn:facebook:name", name, ClaimValueTypes.String, Options.ClaimsIssuer));
// Many Facebook accounts do not set the UserName field. Fall back to the Name field instead.
if (string.IsNullOrEmpty(userName))
{
identity.AddClaim(new Claim(identity.NameClaimType, name, ClaimValueTypes.String, Options.ClaimsIssuer));
}
}
var link = FacebookHelper.GetLink(payload);
if (!string.IsNullOrEmpty(link))
{
identity.AddClaim(new Claim("urn:facebook:link", link, ClaimValueTypes.String, Options.ClaimsIssuer));
}
await Options.Events.CreatingTicket(context);
return new AuthenticationTicket(context.Principal, context.Properties, context.Options.AuthenticationScheme);
}
private string GenerateAppSecretProof(string accessToken)
{
using (var algorithm = new HMACSHA256(Encoding.ASCII.GetBytes(Options.AppSecret)))
{
var hash = algorithm.ComputeHash(Encoding.ASCII.GetBytes(accessToken));
var builder = new StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
builder.Append(hash[i].ToString("x2", CultureInfo.InvariantCulture));
}
return builder.ToString();
}
}
protected override string FormatScope()
{
// Facebook deviates from the OAuth spec here. They require comma separated instead of space separated.
// https://developers.facebook.com/docs/reference/dialogs/oauth
// http://tools.ietf.org/html/rfc6749#section-3.3
return string.Join(",", Options.Scope);
}
}
}