FacebookApplication.VerifyAuthentication(_httpContext, GenerateLocalCallbackUri()) return 在 Facebook 上为空
FacebookApplication.VerifyAuthentication(_httpContext, GenerateLocalCallbackUri()) return null on Facebook
我已经使用 nopcommerce 开发了一个 mvc 5 应用程序,我使用外部回调使用 facebook 登录,它工作正常,但现在它不工作,我找不到实际问题。并使用下面的代码
this.FacebookApplication.VerifyAuthentication(_httpContext, GenerateLocalCallbackUri());
它总是返回 null 并且身份验证状态失败我在网上搜索并执行了所有操作并遵循了这些步骤但我仍然无法使用 facebook 登录。
我的代码在FacebookProviderAuthorizer.cs
中是这样的
private AuthorizeState VerifyAuthentication(string returnUrl)
{
var authResult = DotNetOpenAuth.AspNet.Clients.FacebookApplication.VerifyAuthentication(_httpContext, GenerateLocalCallbackUri());
if (authResult.IsSuccessful)
{
}
}
然后写回调方法
private Uri GenerateLocalCallbackUri()
{
string url = string.Format("{0}plugins/externalauthFacebook/logincallback/", _webHelper.GetStoreLocation());
return new Uri(url);
}
然后生成服务登录url
private Uri GenerateServiceLoginUrl()
{
//code copied from DotNetOpenAuth.AspNet.Clients.FacebookClient file
var builder = new UriBuilder("https://www.facebook.com/dialog/oauth");
var args = new Dictionary<string, string>();
args.Add("client_id", _facebookExternalAuthSettings.ClientKeyIdentifier);
args.Add("redirect_uri", GenerateLocalCallbackUri().AbsoluteUri);
args.Add("response_type", "token");
args.Add("scope", "email");
AppendQueryArgs(builder, args);
return builder.Uri;
}
我们 运行 在 2017 年 3 月 27 日星期一遇到了同样的问题,当时 Facebook 停止了对其 Graph API v2.2 的支持。
我们还使用了最初通过 Nuget 安装的 DotNetOpenAuth。源代码可在下面的 link 处找到:
https://github.com/DotNetOpenAuth/DotNetOpenAuth
具体来说,我们发现我们的代码使用了 4.3 b运行ch,其中包含 DotNetOpenAuth.AspNet.DLL 的源代码。检查源代码后,我们发现问题出在 DotNetOpenAuth.AspNet\Clients\OAuth2\FacebookClient.cs 中的这段代码,位于 QueryAccessToken 方法中:
using (WebClient client = new WebClient()) {
string data = client.DownloadString(builder.Uri);
if (string.IsNullOrEmpty(data)) {
return null;
}
var parsedQueryString = HttpUtility.ParseQueryString(data);
return parsedQueryString["access_token"];
}
具体而言,问题在于 ParseQueryString 调用。从 API 的 v2.3 开始,数据不再作为 HTML 查询字符串返回,而是以标准 JSON 格式返回。
为了解决这个问题,我们创建了自己的自定义 class 继承 OAuth2Client 并从 FacebookClient.cs 导入了大部分相同的代码。然后,我们将上面的代码片段替换为解析 JSON 响应以提取 access_token 和 returns 的代码。您可以在同一 FacebookClient class 的 GetUserData 方法中查看如何执行此操作的示例:
FacebookGraphData graphData;
var request =
WebRequest.Create(
"https://graph.facebook.com/me?access_token=" +
MessagingUtilities.EscapeUriDataStringRfc3986(accessToken));
using (var response = request.GetResponse()) {
using (var responseStream = response.GetResponseStream()) {
graphData = JsonHelper.Deserialize<FacebookGraphData>(responseStream);
}
}
唯一的其他更改是注册我们的自定义 class 代替 FacebookClient class,因此 OAuth 回调使用它来处理来自 Facebook API 的 post .一旦我们这样做了,一切又恢复顺利了。
基于 Steve 的 post,我创建了一个 "FriendlyFacebookClient" 来替代 FacebookClient,复制了一些内部方法,并将 QueryAccessToken 替换为以下内容:
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
UriBuilder builder = new UriBuilder("https://graph.facebook.com/oauth/access_token");
AppendQueryArgs(builder, (IEnumerable<KeyValuePair<string, string>>)new Dictionary<string, string>()
{
{ "client_id", this.appId},
{ "redirect_uri", FriendlyFacebookClient.NormalizeHexEncoding(returnUrl.AbsoluteUri)},
{ "client_secret", this.appSecret },
{ "code", authorizationCode },
{ "scope", "email" }
});
using (WebClient webClient = new WebClient())
{
var response = webClient.DownloadString(builder.Uri);
var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(response);
return data["access_token"];
}
}
根据@SteveTerry 的建议,我们需要更新 FacebookClient 中的 QueryAccessToken 函数 class。不幸的是 "FacebookClient" 是密封的 class 所以我们不能继承和覆盖。因此,您选择哪种方式取决于您。最终结果应该是这样的:
这个函数的旧代码是:
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode) {
// Note: Facebook doesn't like us to url-encode the redirect_uri value
var builder = new UriBuilder(TokenEndpoint);
builder.AppendQueryArgs(
new Dictionary<string, string> {
{ "client_id", this.appId },
{ "redirect_uri", NormalizeHexEncoding(returnUrl.AbsoluteUri) },
{ "client_secret", this.appSecret },
{ "code", authorizationCode },
{ "scope", "email" },
});
using (webclient client = new webclient()) {
string data = client.downloadstring(builder.uri);
if (string.isnullorempty(data)) {
return null;
}
var parsedquerystring = httputility.parsequerystring(data);
return parsedquerystring["access_token"];
}
}
为了支持新版fb api应该是这样的:
/// <summary>
/// Contains access_token of a Facebook user.
/// </summary>
[DataContract]
[EditorBrowsable(EditorBrowsableState.Never)]
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Facebook", Justification = "Brand name")]
public class FacebookAccessTokenData
{
#region Public Properties
/// <summary>
///
/// </summary>
[DataMember(Name = "access_token")]
public string AccessToken { get; set; }
/// <summary>
///
/// </summary>
[DataMember(Name = "token_type")]
public string TokenType { get; set; }
/// <summary>
///
/// </summary>
[DataMember(Name = "expires_in")]
public string ExpiresIn { get; set; }
#endregion
}
/// <summary>
/// Obtains an access token given an authorization code and callback URL.
/// </summary>
/// <param name="returnUrl">
/// The return url.
/// </param>
/// <param name="authorizationCode">
/// The authorization code.
/// </param>
/// <returns>
/// The access token.
/// </returns>
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode) {
// Note: Facebook doesn't like us to url-encode the redirect_uri value
var builder = new UriBuilder(TokenEndpoint);
builder.AppendQueryArgs(
new Dictionary<string, string> {
{ "client_id", this.appId },
{ "redirect_uri", NormalizeHexEncoding(returnUrl.AbsoluteUri) },
{ "client_secret", this.appSecret },
{ "code", authorizationCode },
{ "scope", "email" },
});
FacebookAccessTokenData graphData;
var request = WebRequest.Create(builder.Uri);
using (var response = request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
graphData = JsonHelper.Deserialize<FacebookAccessTokenData>(responseStream);
}
}
return graphData.AccessToken;
}
我解决了我的问题,我将做与@Adam 在他的回答中描述的相同的事情,根据@Adam、@SteveTerry 和@Adeem 的回答,我更改我的代码并创建自定义 FacebookClient class姓名。并替换为 nopCommerce 中 FacebookClient 的原始引用。
public class FacebookOAuth2Client : OAuth2Client
{
#region Constants and Fields
/// <summary>
/// The authorization endpoint.
/// </summary>
private const string AuthorizationEndpoint = "https://www.facebook.com/dialog/oauth";
/// <summary>
/// The token endpoint.
/// </summary>
private const string TokenEndpoint = "https://graph.facebook.com/oauth/access_token";
/// <summary>
/// The user info endpoint.
/// </summary>
private const string UserInfoEndpoint = "https://graph.facebook.com/me";
/// <summary>
/// The app id.
/// </summary>
private readonly string _appId;
/// <summary>
/// The app secret.
/// </summary>
private readonly string _appSecret;
/// <summary>
/// The requested scopes.
/// </summary>
private readonly string[] _requestedScopes;
#endregion
/// <summary>
/// Creates a new Facebook OAuth2 client, requesting the default "email" scope.
/// </summary>
/// <param name="appId">The Facebook App Id</param>
/// <param name="appSecret">The Facebook App Secret</param>
public FacebookClient(string appId, string appSecret)
: this(appId, appSecret, new[] { "email" }) { }
/// <summary>
/// Creates a new Facebook OAuth2 client.
/// </summary>
/// <param name="appId">The Facebook App Id</param>
/// <param name="appSecret">The Facebook App Secret</param>
/// <param name="requestedScopes">One or more requested scopes, passed without the base URI.</param>
public FacebookClient(string appId, string appSecret, params string[] requestedScopes)
: base("facebook")
{
if (string.IsNullOrWhiteSpace(appId))
throw new ArgumentNullException("appId");
if (string.IsNullOrWhiteSpace(appSecret))
throw new ArgumentNullException("appSecret");
if (requestedScopes == null)
throw new ArgumentNullException("requestedScopes");
if (requestedScopes.Length == 0)
throw new ArgumentException("One or more scopes must be requested.", "requestedScopes");
_appId = appId;
_appSecret = appSecret;
_requestedScopes = requestedScopes;
}
protected override Uri GetServiceLoginUrl(Uri returnUrl)
{
var state = string.IsNullOrEmpty(returnUrl.Query) ? string.Empty : returnUrl.Query.Substring(1);
return BuildUri(AuthorizationEndpoint, new NameValueCollection
{
{ "client_id", _appId },
{ "scope", string.Join(" ", _requestedScopes) },
{ "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
{ "state", state },
});
}
protected override IDictionary<string, string> GetUserData(string accessToken)
{
var uri = BuildUri(UserInfoEndpoint, new NameValueCollection { { "access_token", accessToken } });
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
using (var webResponse = webRequest.GetResponse())
using (var stream = webResponse.GetResponseStream())
{
if (stream == null)
return null;
using (var textReader = new StreamReader(stream))
{
var json = textReader.ReadToEnd();
var extraData = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
var data = extraData.ToDictionary(x => x.Key, x => x.Value.ToString());
data.Add("picture", string.Format("https://graph.facebook.com/{0}/picture", data["id"]));
return data;
}
}
}
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
var uri = BuildUri(TokenEndpoint, new NameValueCollection
{
{ "code", authorizationCode },
{ "client_id", _appId },
{ "client_secret", _appSecret },
{ "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
});
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
string accessToken = null;
HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
// handle response from FB
// this will not be a url with params like the first request to get the 'code'
Encoding rEncoding = Encoding.GetEncoding(response.CharacterSet);
using (StreamReader sr = new StreamReader(response.GetResponseStream(), rEncoding))
{
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var jsonObject = serializer.DeserializeObject(sr.ReadToEnd());
var jConvert = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(jsonObject));
Dictionary<string, object> desirializedJsonObject = JsonConvert.DeserializeObject<Dictionary<string, object>>(jConvert.ToString());
accessToken = desirializedJsonObject["access_token"].ToString();
}
return accessToken;
}
private static Uri BuildUri(string baseUri, NameValueCollection queryParameters)
{
var keyValuePairs = queryParameters.AllKeys.Select(k => HttpUtility.UrlEncode(k) + "=" + HttpUtility.UrlEncode(queryParameters[k]));
var qs = String.Join("&", keyValuePairs);
var builder = new UriBuilder(baseUri) { Query = qs };
return builder.Uri;
}
/// <summary>
/// Facebook works best when return data be packed into a "state" parameter.
/// This should be called before verifying the request, so that the url is rewritten to support this.
/// </summary>
public static void RewriteRequest()
{
var ctx = HttpContext.Current;
var stateString = HttpUtility.UrlDecode(ctx.Request.QueryString["state"]);
if (stateString == null || !stateString.Contains("__provider__=facebook"))
return;
var q = HttpUtility.ParseQueryString(stateString);
q.Add(ctx.Request.QueryString);
q.Remove("state");
ctx.RewritePath(ctx.Request.Path + "?" + q);
}
}
在这段代码中,我没有引用 JsonHelper,所以我使用简单的 jsonConvert。再次感谢@Adam、@SteveTerry 和@Adeem 的帮助。
我使用了@Vishal 分享的代码并获得了相同的效果。
我们必须关注的主要事情是覆盖 QueryAccessToken 方法以使用 json 响应。
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
var uri = BuildUri(TokenEndpoint, new NameValueCollection
{
{ "code", authorizationCode },
{ "client_id", _appId },
{ "client_secret", _appSecret },
{ "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
});
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
string accessToken = null;
HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
// handle response from FB
// this will not be a url with params like the first request to get the 'code'
Encoding rEncoding = Encoding.GetEncoding(response.CharacterSet);
using (StreamReader sr = new StreamReader(response.GetResponseStream(), rEncoding))
{
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var jsonObject = serializer.DeserializeObject(sr.ReadToEnd());
var jConvert = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(jsonObject));
Dictionary<string, object> desirializedJsonObject = JsonConvert.DeserializeObject<Dictionary<string, object>>(jConvert.ToString());
accessToken = desirializedJsonObject["access_token"].ToString();
}
return accessToken;
}
实现步骤:
第 1 步。您需要做的是添加一个名为 FacebookClientOverride.cs(而不是 FacebookClient.cs)
的文件
这是整个文件的代码片段。
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using DotNetOpenAuth.AspNet.Clients;
using Newtonsoft.Json;
public class FacebookClient : OAuth2Client
{
#region Constants and Fields
/// <summary>
/// The authorization endpoint.
/// </summary>
private const string AuthorizationEndpoint = "https://www.facebook.com/dialog/oauth";
/// <summary>
/// The token endpoint.
/// </summary>
private const string TokenEndpoint = "https://graph.facebook.com/oauth/access_token";
/// <summary>
/// The user info endpoint.
/// </summary>
private const string UserInfoEndpoint = "https://graph.facebook.com/me";
/// <summary>
/// The app id.
/// </summary>
private readonly string _appId;
/// <summary>
/// The app secret.
/// </summary>
private readonly string _appSecret;
/// <summary>
/// The requested scopes.
/// </summary>
private readonly string[] _requestedScopes;
#endregion
/// <summary>
/// Creates a new Facebook OAuth2 client, requesting the default "email" scope.
/// </summary>
/// <param name="appId">The Facebook App Id</param>
/// <param name="appSecret">The Facebook App Secret</param>
public FacebookClient(string appId, string appSecret)
: this(appId, appSecret, new[] { "email" }) { }
/// <summary>
/// Creates a new Facebook OAuth2 client.
/// </summary>
/// <param name="appId">The Facebook App Id</param>
/// <param name="appSecret">The Facebook App Secret</param>
/// <param name="requestedScopes">One or more requested scopes, passed without the base URI.</param>
public FacebookClient(string appId, string appSecret, params string[] requestedScopes)
: base("facebook")
{
if (string.IsNullOrWhiteSpace(appId))
throw new ArgumentNullException("appId");
if (string.IsNullOrWhiteSpace(appSecret))
throw new ArgumentNullException("appSecret");
if (requestedScopes == null)
throw new ArgumentNullException("requestedScopes");
if (requestedScopes.Length == 0)
throw new ArgumentException("One or more scopes must be requested.", "requestedScopes");
_appId = appId;
_appSecret = appSecret;
_requestedScopes = requestedScopes;
}
protected override Uri GetServiceLoginUrl(Uri returnUrl)
{
var state = string.IsNullOrEmpty(returnUrl.Query) ? string.Empty : returnUrl.Query.Substring(1);
return BuildUri(AuthorizationEndpoint, new NameValueCollection
{
{ "client_id", _appId },
{ "scope", string.Join(" ", _requestedScopes) },
{ "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
{ "state", state },
});
}
protected override IDictionary<string, string> GetUserData(string accessToken)
{
var uri = BuildUri(UserInfoEndpoint, new NameValueCollection { { "access_token", accessToken } });
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
using (var webResponse = webRequest.GetResponse())
using (var stream = webResponse.GetResponseStream())
{
if (stream == null)
return null;
using (var textReader = new StreamReader(stream))
{
var json = textReader.ReadToEnd();
var extraData = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
var data = extraData.ToDictionary(x => x.Key, x => x.Value.ToString());
data.Add("picture", string.Format("https://graph.facebook.com/{0}/picture", data["id"]));
return data;
}
}
}
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
var uri = BuildUri(TokenEndpoint, new NameValueCollection
{
{ "code", authorizationCode },
{ "client_id", _appId },
{ "client_secret", _appSecret },
{ "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
});
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
string accessToken = null;
HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
// handle response from FB
// this will not be a url with params like the first request to get the 'code'
Encoding rEncoding = Encoding.GetEncoding(response.CharacterSet);
using (StreamReader sr = new StreamReader(response.GetResponseStream(), rEncoding))
{
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var jsonObject = serializer.DeserializeObject(sr.ReadToEnd());
var jConvert = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(jsonObject));
Dictionary<string, object> desirializedJsonObject = JsonConvert.DeserializeObject<Dictionary<string, object>>(jConvert.ToString());
accessToken = desirializedJsonObject["access_token"].ToString();
}
return accessToken;
}
private static Uri BuildUri(string baseUri, NameValueCollection queryParameters)
{
var keyValuePairs = queryParameters.AllKeys.Select(k => HttpUtility.UrlEncode(k) + "=" + HttpUtility.UrlEncode(queryParameters[k]));
var qs = String.Join("&", keyValuePairs);
var builder = new UriBuilder(baseUri) { Query = qs };
return builder.Uri;
}
/// <summary>
/// Facebook works best when return data be packed into a "state" parameter.
/// This should be called before verifying the request, so that the url is rewritten to support this.
/// </summary>
public static void RewriteRequest()
{
var ctx = HttpContext.Current;
var stateString = HttpUtility.UrlDecode(ctx.Request.QueryString["state"]);
if (stateString == null || !stateString.Contains("__provider__=facebook"))
return;
var q = HttpUtility.ParseQueryString(stateString);
q.Add(ctx.Request.QueryString);
q.Remove("state");
ctx.RewritePath(ctx.Request.Path + "?" + q);
}
}
第 2 步。添加对 System.Web.Extensions
的引用
第 3 步。在 FacebookProviderAuthorizer.cs(Nopcommerce 项目)中
寻找 FacebookClient 属性
私人 FacebookClient _facebookApplication;
这应该是指您刚刚添加的新文件。
第 4 步。现在在文件 FacebookProviderAuthorizer.cs 中名为 VerifyAuthentication 的方法中放置一个断点。
authResult.IsSuccessful 现在必须为真,因为它已成功解析令牌。
谢谢大家。
如果解决方案对您有用,请点赞。
根据 我得到了我的解决方案。在 MVC4
每个人写下他们的 AppID
和 SecurityCode
。由于 facebook GRAPH API 的更改,以前的链接已损坏。因此每个人都需要更改 RegisterFacebookClient
class。但是这个class在.Net库中是一个密封的class,所以任何人都不能扩展或覆盖它。结果我们需要使用wrapper
class。我写这个答案是因为之前所有的答案供应商都遗漏了一件事,为此我吃了不少苦头。因为他们在AuthConfig
Class中没有提到class翘曲系统的系统。所以要解决这个问题需要太多的时间。因此,我将按以下步骤逐步进行。让我们考虑我的 Wrapper class 是 FacebookClientV2Dot3
因此我的 class 将是
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using DotNetOpenAuth.AspNet.Clients;
using Newtonsoft.Json;
public class FacebookClientV2Dot3 : OAuth2Client
{
#region Constants and Fields
/// <summary>
/// The authorization endpoint.
/// </summary>
private const string AuthorizationEndpoint = "https://www.facebook.com/dialog/oauth";
/// <summary>
/// The token endpoint.
/// </summary>
private const string TokenEndpoint = "https://graph.facebook.com/oauth/access_token";
/// <summary>
/// The user info endpoint.
/// </summary>
private const string UserInfoEndpoint = "https://graph.facebook.com/me";
/// <summary>
/// The app id.
/// </summary>
private readonly string _appId;
/// <summary>
/// The app secret.
/// </summary>
private readonly string _appSecret;
/// <summary>
/// The requested scopes.
/// </summary>
private readonly string[] _requestedScopes;
#endregion
/// <summary>
/// Creates a new Facebook OAuth2 client, requesting the default "email" scope.
/// </summary>
/// <param name="appId">The Facebook App Id</param>
/// <param name="appSecret">The Facebook App Secret</param>
public FacebookClient(string appId, string appSecret)
: this(appId, appSecret, new[] { "email" }) { }
/// <summary>
/// Creates a new Facebook OAuth2 client.
/// </summary>
/// <param name="appId">The Facebook App Id</param>
/// <param name="appSecret">The Facebook App Secret</param>
/// <param name="requestedScopes">One or more requested scopes, passed without the base URI.</param>
public FacebookClient(string appId, string appSecret, params string[] requestedScopes)
: base("facebook")
{
if (string.IsNullOrWhiteSpace(appId))
throw new ArgumentNullException("appId");
if (string.IsNullOrWhiteSpace(appSecret))
throw new ArgumentNullException("appSecret");
if (requestedScopes == null)
throw new ArgumentNullException("requestedScopes");
if (requestedScopes.Length == 0)
throw new ArgumentException("One or more scopes must be requested.", "requestedScopes");
_appId = appId;
_appSecret = appSecret;
_requestedScopes = requestedScopes;
}
protected override Uri GetServiceLoginUrl(Uri returnUrl)
{
var state = string.IsNullOrEmpty(returnUrl.Query) ? string.Empty : returnUrl.Query.Substring(1);
return BuildUri(AuthorizationEndpoint, new NameValueCollection
{
{ "client_id", _appId },
{ "scope", string.Join(" ", _requestedScopes) },
{ "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
{ "state", state },
});
}
protected override IDictionary<string, string> GetUserData(string accessToken)
{
var uri = BuildUri(UserInfoEndpoint, new NameValueCollection { { "access_token", accessToken } });
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
using (var webResponse = webRequest.GetResponse())
using (var stream = webResponse.GetResponseStream())
{
if (stream == null)
return null;
using (var textReader = new StreamReader(stream))
{
var json = textReader.ReadToEnd();
var extraData = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
var data = extraData.ToDictionary(x => x.Key, x => x.Value.ToString());
data.Add("picture", string.Format("https://graph.facebook.com/{0}/picture", data["id"]));
return data;
}
}
}
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
var uri = BuildUri(TokenEndpoint, new NameValueCollection
{
{ "code", authorizationCode },
{ "client_id", _appId },
{ "client_secret", _appSecret },
{ "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
});
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
string accessToken = null;
HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
// handle response from FB
// this will not be a url with params like the first request to get the 'code'
Encoding rEncoding = Encoding.GetEncoding(response.CharacterSet);
using (StreamReader sr = new StreamReader(response.GetResponseStream(), rEncoding))
{
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var jsonObject = serializer.DeserializeObject(sr.ReadToEnd());
var jConvert = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(jsonObject));
Dictionary<string, object> desirializedJsonObject = JsonConvert.DeserializeObject<Dictionary<string, object>>(jConvert.ToString());
accessToken = desirializedJsonObject["access_token"].ToString();
}
return accessToken;
}
private static Uri BuildUri(string baseUri, NameValueCollection queryParameters)
{
var keyValuePairs = queryParameters.AllKeys.Select(k => HttpUtility.UrlEncode(k) + "=" + HttpUtility.UrlEncode(queryParameters[k]));
var qs = String.Join("&", keyValuePairs);
var builder = new UriBuilder(baseUri) { Query = qs };
return builder.Uri;
}
/// <summary>
/// Facebook works best when return data be packed into a "state" parameter.
/// This should be called before verifying the request, so that the url is rewritten to support this.
/// </summary>
public static void RewriteRequest()
{
var ctx = HttpContext.Current;
var stateString = HttpUtility.UrlDecode(ctx.Request.QueryString["state"]);
if (stateString == null || !stateString.Contains("__provider__=facebook"))
return;
var q = HttpUtility.ParseQueryString(stateString);
q.Add(ctx.Request.QueryString);
q.Remove("state");
ctx.RewritePath(ctx.Request.Path + "?" + q);
}
}
看这里,我已经用较新版本的链接替换了所有 API 链接。
现在你需要修改你的
AuthConfig
只需使用包装器 class 而不是 RegisterFacebookClient
。完全阻止那部分代码。并添加这个...
OAuthWebSecurity.RegisterClient(new FacebookClientV2Dot3("AppID", "HassedPassword"));
然后一切成功。您的 Facebook 登录将恢复到以前的状态。
然而,您可能会遇到关于这个新 API 而不是以前的 API 的新问题,问题是 IP Whitelisting
。像这样image。希望你只需要这个。编码愉快。
我已经使用 nopcommerce 开发了一个 mvc 5 应用程序,我使用外部回调使用 facebook 登录,它工作正常,但现在它不工作,我找不到实际问题。并使用下面的代码
this.FacebookApplication.VerifyAuthentication(_httpContext, GenerateLocalCallbackUri());
它总是返回 null 并且身份验证状态失败我在网上搜索并执行了所有操作并遵循了这些步骤但我仍然无法使用 facebook 登录。
我的代码在FacebookProviderAuthorizer.cs
中是这样的private AuthorizeState VerifyAuthentication(string returnUrl)
{
var authResult = DotNetOpenAuth.AspNet.Clients.FacebookApplication.VerifyAuthentication(_httpContext, GenerateLocalCallbackUri());
if (authResult.IsSuccessful)
{
}
}
然后写回调方法
private Uri GenerateLocalCallbackUri()
{
string url = string.Format("{0}plugins/externalauthFacebook/logincallback/", _webHelper.GetStoreLocation());
return new Uri(url);
}
然后生成服务登录url
private Uri GenerateServiceLoginUrl()
{
//code copied from DotNetOpenAuth.AspNet.Clients.FacebookClient file
var builder = new UriBuilder("https://www.facebook.com/dialog/oauth");
var args = new Dictionary<string, string>();
args.Add("client_id", _facebookExternalAuthSettings.ClientKeyIdentifier);
args.Add("redirect_uri", GenerateLocalCallbackUri().AbsoluteUri);
args.Add("response_type", "token");
args.Add("scope", "email");
AppendQueryArgs(builder, args);
return builder.Uri;
}
我们 运行 在 2017 年 3 月 27 日星期一遇到了同样的问题,当时 Facebook 停止了对其 Graph API v2.2 的支持。
我们还使用了最初通过 Nuget 安装的 DotNetOpenAuth。源代码可在下面的 link 处找到:
https://github.com/DotNetOpenAuth/DotNetOpenAuth
具体来说,我们发现我们的代码使用了 4.3 b运行ch,其中包含 DotNetOpenAuth.AspNet.DLL 的源代码。检查源代码后,我们发现问题出在 DotNetOpenAuth.AspNet\Clients\OAuth2\FacebookClient.cs 中的这段代码,位于 QueryAccessToken 方法中:
using (WebClient client = new WebClient()) {
string data = client.DownloadString(builder.Uri);
if (string.IsNullOrEmpty(data)) {
return null;
}
var parsedQueryString = HttpUtility.ParseQueryString(data);
return parsedQueryString["access_token"];
}
具体而言,问题在于 ParseQueryString 调用。从 API 的 v2.3 开始,数据不再作为 HTML 查询字符串返回,而是以标准 JSON 格式返回。
为了解决这个问题,我们创建了自己的自定义 class 继承 OAuth2Client 并从 FacebookClient.cs 导入了大部分相同的代码。然后,我们将上面的代码片段替换为解析 JSON 响应以提取 access_token 和 returns 的代码。您可以在同一 FacebookClient class 的 GetUserData 方法中查看如何执行此操作的示例:
FacebookGraphData graphData;
var request =
WebRequest.Create(
"https://graph.facebook.com/me?access_token=" +
MessagingUtilities.EscapeUriDataStringRfc3986(accessToken));
using (var response = request.GetResponse()) {
using (var responseStream = response.GetResponseStream()) {
graphData = JsonHelper.Deserialize<FacebookGraphData>(responseStream);
}
}
唯一的其他更改是注册我们的自定义 class 代替 FacebookClient class,因此 OAuth 回调使用它来处理来自 Facebook API 的 post .一旦我们这样做了,一切又恢复顺利了。
基于 Steve 的 post,我创建了一个 "FriendlyFacebookClient" 来替代 FacebookClient,复制了一些内部方法,并将 QueryAccessToken 替换为以下内容:
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
UriBuilder builder = new UriBuilder("https://graph.facebook.com/oauth/access_token");
AppendQueryArgs(builder, (IEnumerable<KeyValuePair<string, string>>)new Dictionary<string, string>()
{
{ "client_id", this.appId},
{ "redirect_uri", FriendlyFacebookClient.NormalizeHexEncoding(returnUrl.AbsoluteUri)},
{ "client_secret", this.appSecret },
{ "code", authorizationCode },
{ "scope", "email" }
});
using (WebClient webClient = new WebClient())
{
var response = webClient.DownloadString(builder.Uri);
var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(response);
return data["access_token"];
}
}
根据@SteveTerry 的建议,我们需要更新 FacebookClient 中的 QueryAccessToken 函数 class。不幸的是 "FacebookClient" 是密封的 class 所以我们不能继承和覆盖。因此,您选择哪种方式取决于您。最终结果应该是这样的:
这个函数的旧代码是:
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode) {
// Note: Facebook doesn't like us to url-encode the redirect_uri value
var builder = new UriBuilder(TokenEndpoint);
builder.AppendQueryArgs(
new Dictionary<string, string> {
{ "client_id", this.appId },
{ "redirect_uri", NormalizeHexEncoding(returnUrl.AbsoluteUri) },
{ "client_secret", this.appSecret },
{ "code", authorizationCode },
{ "scope", "email" },
});
using (webclient client = new webclient()) {
string data = client.downloadstring(builder.uri);
if (string.isnullorempty(data)) {
return null;
}
var parsedquerystring = httputility.parsequerystring(data);
return parsedquerystring["access_token"];
}
}
为了支持新版fb api应该是这样的:
/// <summary>
/// Contains access_token of a Facebook user.
/// </summary>
[DataContract]
[EditorBrowsable(EditorBrowsableState.Never)]
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Facebook", Justification = "Brand name")]
public class FacebookAccessTokenData
{
#region Public Properties
/// <summary>
///
/// </summary>
[DataMember(Name = "access_token")]
public string AccessToken { get; set; }
/// <summary>
///
/// </summary>
[DataMember(Name = "token_type")]
public string TokenType { get; set; }
/// <summary>
///
/// </summary>
[DataMember(Name = "expires_in")]
public string ExpiresIn { get; set; }
#endregion
}
/// <summary>
/// Obtains an access token given an authorization code and callback URL.
/// </summary>
/// <param name="returnUrl">
/// The return url.
/// </param>
/// <param name="authorizationCode">
/// The authorization code.
/// </param>
/// <returns>
/// The access token.
/// </returns>
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode) {
// Note: Facebook doesn't like us to url-encode the redirect_uri value
var builder = new UriBuilder(TokenEndpoint);
builder.AppendQueryArgs(
new Dictionary<string, string> {
{ "client_id", this.appId },
{ "redirect_uri", NormalizeHexEncoding(returnUrl.AbsoluteUri) },
{ "client_secret", this.appSecret },
{ "code", authorizationCode },
{ "scope", "email" },
});
FacebookAccessTokenData graphData;
var request = WebRequest.Create(builder.Uri);
using (var response = request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
graphData = JsonHelper.Deserialize<FacebookAccessTokenData>(responseStream);
}
}
return graphData.AccessToken;
}
我解决了我的问题,我将做与@Adam 在他的回答中描述的相同的事情,根据@Adam、@SteveTerry 和@Adeem 的回答,我更改我的代码并创建自定义 FacebookClient class姓名。并替换为 nopCommerce 中 FacebookClient 的原始引用。
public class FacebookOAuth2Client : OAuth2Client
{
#region Constants and Fields
/// <summary>
/// The authorization endpoint.
/// </summary>
private const string AuthorizationEndpoint = "https://www.facebook.com/dialog/oauth";
/// <summary>
/// The token endpoint.
/// </summary>
private const string TokenEndpoint = "https://graph.facebook.com/oauth/access_token";
/// <summary>
/// The user info endpoint.
/// </summary>
private const string UserInfoEndpoint = "https://graph.facebook.com/me";
/// <summary>
/// The app id.
/// </summary>
private readonly string _appId;
/// <summary>
/// The app secret.
/// </summary>
private readonly string _appSecret;
/// <summary>
/// The requested scopes.
/// </summary>
private readonly string[] _requestedScopes;
#endregion
/// <summary>
/// Creates a new Facebook OAuth2 client, requesting the default "email" scope.
/// </summary>
/// <param name="appId">The Facebook App Id</param>
/// <param name="appSecret">The Facebook App Secret</param>
public FacebookClient(string appId, string appSecret)
: this(appId, appSecret, new[] { "email" }) { }
/// <summary>
/// Creates a new Facebook OAuth2 client.
/// </summary>
/// <param name="appId">The Facebook App Id</param>
/// <param name="appSecret">The Facebook App Secret</param>
/// <param name="requestedScopes">One or more requested scopes, passed without the base URI.</param>
public FacebookClient(string appId, string appSecret, params string[] requestedScopes)
: base("facebook")
{
if (string.IsNullOrWhiteSpace(appId))
throw new ArgumentNullException("appId");
if (string.IsNullOrWhiteSpace(appSecret))
throw new ArgumentNullException("appSecret");
if (requestedScopes == null)
throw new ArgumentNullException("requestedScopes");
if (requestedScopes.Length == 0)
throw new ArgumentException("One or more scopes must be requested.", "requestedScopes");
_appId = appId;
_appSecret = appSecret;
_requestedScopes = requestedScopes;
}
protected override Uri GetServiceLoginUrl(Uri returnUrl)
{
var state = string.IsNullOrEmpty(returnUrl.Query) ? string.Empty : returnUrl.Query.Substring(1);
return BuildUri(AuthorizationEndpoint, new NameValueCollection
{
{ "client_id", _appId },
{ "scope", string.Join(" ", _requestedScopes) },
{ "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
{ "state", state },
});
}
protected override IDictionary<string, string> GetUserData(string accessToken)
{
var uri = BuildUri(UserInfoEndpoint, new NameValueCollection { { "access_token", accessToken } });
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
using (var webResponse = webRequest.GetResponse())
using (var stream = webResponse.GetResponseStream())
{
if (stream == null)
return null;
using (var textReader = new StreamReader(stream))
{
var json = textReader.ReadToEnd();
var extraData = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
var data = extraData.ToDictionary(x => x.Key, x => x.Value.ToString());
data.Add("picture", string.Format("https://graph.facebook.com/{0}/picture", data["id"]));
return data;
}
}
}
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
var uri = BuildUri(TokenEndpoint, new NameValueCollection
{
{ "code", authorizationCode },
{ "client_id", _appId },
{ "client_secret", _appSecret },
{ "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
});
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
string accessToken = null;
HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
// handle response from FB
// this will not be a url with params like the first request to get the 'code'
Encoding rEncoding = Encoding.GetEncoding(response.CharacterSet);
using (StreamReader sr = new StreamReader(response.GetResponseStream(), rEncoding))
{
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var jsonObject = serializer.DeserializeObject(sr.ReadToEnd());
var jConvert = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(jsonObject));
Dictionary<string, object> desirializedJsonObject = JsonConvert.DeserializeObject<Dictionary<string, object>>(jConvert.ToString());
accessToken = desirializedJsonObject["access_token"].ToString();
}
return accessToken;
}
private static Uri BuildUri(string baseUri, NameValueCollection queryParameters)
{
var keyValuePairs = queryParameters.AllKeys.Select(k => HttpUtility.UrlEncode(k) + "=" + HttpUtility.UrlEncode(queryParameters[k]));
var qs = String.Join("&", keyValuePairs);
var builder = new UriBuilder(baseUri) { Query = qs };
return builder.Uri;
}
/// <summary>
/// Facebook works best when return data be packed into a "state" parameter.
/// This should be called before verifying the request, so that the url is rewritten to support this.
/// </summary>
public static void RewriteRequest()
{
var ctx = HttpContext.Current;
var stateString = HttpUtility.UrlDecode(ctx.Request.QueryString["state"]);
if (stateString == null || !stateString.Contains("__provider__=facebook"))
return;
var q = HttpUtility.ParseQueryString(stateString);
q.Add(ctx.Request.QueryString);
q.Remove("state");
ctx.RewritePath(ctx.Request.Path + "?" + q);
}
}
在这段代码中,我没有引用 JsonHelper,所以我使用简单的 jsonConvert。再次感谢@Adam、@SteveTerry 和@Adeem 的帮助。
我使用了@Vishal 分享的代码并获得了相同的效果。
我们必须关注的主要事情是覆盖 QueryAccessToken 方法以使用 json 响应。
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
var uri = BuildUri(TokenEndpoint, new NameValueCollection
{
{ "code", authorizationCode },
{ "client_id", _appId },
{ "client_secret", _appSecret },
{ "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
});
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
string accessToken = null;
HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
// handle response from FB
// this will not be a url with params like the first request to get the 'code'
Encoding rEncoding = Encoding.GetEncoding(response.CharacterSet);
using (StreamReader sr = new StreamReader(response.GetResponseStream(), rEncoding))
{
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var jsonObject = serializer.DeserializeObject(sr.ReadToEnd());
var jConvert = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(jsonObject));
Dictionary<string, object> desirializedJsonObject = JsonConvert.DeserializeObject<Dictionary<string, object>>(jConvert.ToString());
accessToken = desirializedJsonObject["access_token"].ToString();
}
return accessToken;
}
实现步骤: 第 1 步。您需要做的是添加一个名为 FacebookClientOverride.cs(而不是 FacebookClient.cs)
的文件这是整个文件的代码片段。
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using DotNetOpenAuth.AspNet.Clients;
using Newtonsoft.Json;
public class FacebookClient : OAuth2Client
{
#region Constants and Fields
/// <summary>
/// The authorization endpoint.
/// </summary>
private const string AuthorizationEndpoint = "https://www.facebook.com/dialog/oauth";
/// <summary>
/// The token endpoint.
/// </summary>
private const string TokenEndpoint = "https://graph.facebook.com/oauth/access_token";
/// <summary>
/// The user info endpoint.
/// </summary>
private const string UserInfoEndpoint = "https://graph.facebook.com/me";
/// <summary>
/// The app id.
/// </summary>
private readonly string _appId;
/// <summary>
/// The app secret.
/// </summary>
private readonly string _appSecret;
/// <summary>
/// The requested scopes.
/// </summary>
private readonly string[] _requestedScopes;
#endregion
/// <summary>
/// Creates a new Facebook OAuth2 client, requesting the default "email" scope.
/// </summary>
/// <param name="appId">The Facebook App Id</param>
/// <param name="appSecret">The Facebook App Secret</param>
public FacebookClient(string appId, string appSecret)
: this(appId, appSecret, new[] { "email" }) { }
/// <summary>
/// Creates a new Facebook OAuth2 client.
/// </summary>
/// <param name="appId">The Facebook App Id</param>
/// <param name="appSecret">The Facebook App Secret</param>
/// <param name="requestedScopes">One or more requested scopes, passed without the base URI.</param>
public FacebookClient(string appId, string appSecret, params string[] requestedScopes)
: base("facebook")
{
if (string.IsNullOrWhiteSpace(appId))
throw new ArgumentNullException("appId");
if (string.IsNullOrWhiteSpace(appSecret))
throw new ArgumentNullException("appSecret");
if (requestedScopes == null)
throw new ArgumentNullException("requestedScopes");
if (requestedScopes.Length == 0)
throw new ArgumentException("One or more scopes must be requested.", "requestedScopes");
_appId = appId;
_appSecret = appSecret;
_requestedScopes = requestedScopes;
}
protected override Uri GetServiceLoginUrl(Uri returnUrl)
{
var state = string.IsNullOrEmpty(returnUrl.Query) ? string.Empty : returnUrl.Query.Substring(1);
return BuildUri(AuthorizationEndpoint, new NameValueCollection
{
{ "client_id", _appId },
{ "scope", string.Join(" ", _requestedScopes) },
{ "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
{ "state", state },
});
}
protected override IDictionary<string, string> GetUserData(string accessToken)
{
var uri = BuildUri(UserInfoEndpoint, new NameValueCollection { { "access_token", accessToken } });
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
using (var webResponse = webRequest.GetResponse())
using (var stream = webResponse.GetResponseStream())
{
if (stream == null)
return null;
using (var textReader = new StreamReader(stream))
{
var json = textReader.ReadToEnd();
var extraData = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
var data = extraData.ToDictionary(x => x.Key, x => x.Value.ToString());
data.Add("picture", string.Format("https://graph.facebook.com/{0}/picture", data["id"]));
return data;
}
}
}
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
var uri = BuildUri(TokenEndpoint, new NameValueCollection
{
{ "code", authorizationCode },
{ "client_id", _appId },
{ "client_secret", _appSecret },
{ "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
});
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
string accessToken = null;
HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
// handle response from FB
// this will not be a url with params like the first request to get the 'code'
Encoding rEncoding = Encoding.GetEncoding(response.CharacterSet);
using (StreamReader sr = new StreamReader(response.GetResponseStream(), rEncoding))
{
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var jsonObject = serializer.DeserializeObject(sr.ReadToEnd());
var jConvert = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(jsonObject));
Dictionary<string, object> desirializedJsonObject = JsonConvert.DeserializeObject<Dictionary<string, object>>(jConvert.ToString());
accessToken = desirializedJsonObject["access_token"].ToString();
}
return accessToken;
}
private static Uri BuildUri(string baseUri, NameValueCollection queryParameters)
{
var keyValuePairs = queryParameters.AllKeys.Select(k => HttpUtility.UrlEncode(k) + "=" + HttpUtility.UrlEncode(queryParameters[k]));
var qs = String.Join("&", keyValuePairs);
var builder = new UriBuilder(baseUri) { Query = qs };
return builder.Uri;
}
/// <summary>
/// Facebook works best when return data be packed into a "state" parameter.
/// This should be called before verifying the request, so that the url is rewritten to support this.
/// </summary>
public static void RewriteRequest()
{
var ctx = HttpContext.Current;
var stateString = HttpUtility.UrlDecode(ctx.Request.QueryString["state"]);
if (stateString == null || !stateString.Contains("__provider__=facebook"))
return;
var q = HttpUtility.ParseQueryString(stateString);
q.Add(ctx.Request.QueryString);
q.Remove("state");
ctx.RewritePath(ctx.Request.Path + "?" + q);
}
}
第 2 步。添加对 System.Web.Extensions
的引用第 3 步。在 FacebookProviderAuthorizer.cs(Nopcommerce 项目)中 寻找 FacebookClient 属性 私人 FacebookClient _facebookApplication;
这应该是指您刚刚添加的新文件。
第 4 步。现在在文件 FacebookProviderAuthorizer.cs 中名为 VerifyAuthentication 的方法中放置一个断点。
authResult.IsSuccessful 现在必须为真,因为它已成功解析令牌。
谢谢大家。 如果解决方案对您有用,请点赞。
根据 MVC4
每个人写下他们的 AppID
和 SecurityCode
。由于 facebook GRAPH API 的更改,以前的链接已损坏。因此每个人都需要更改 RegisterFacebookClient
class。但是这个class在.Net库中是一个密封的class,所以任何人都不能扩展或覆盖它。结果我们需要使用wrapper
class。我写这个答案是因为之前所有的答案供应商都遗漏了一件事,为此我吃了不少苦头。因为他们在AuthConfig
Class中没有提到class翘曲系统的系统。所以要解决这个问题需要太多的时间。因此,我将按以下步骤逐步进行。让我们考虑我的 Wrapper class 是 FacebookClientV2Dot3
因此我的 class 将是
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using DotNetOpenAuth.AspNet.Clients;
using Newtonsoft.Json;
public class FacebookClientV2Dot3 : OAuth2Client
{
#region Constants and Fields
/// <summary>
/// The authorization endpoint.
/// </summary>
private const string AuthorizationEndpoint = "https://www.facebook.com/dialog/oauth";
/// <summary>
/// The token endpoint.
/// </summary>
private const string TokenEndpoint = "https://graph.facebook.com/oauth/access_token";
/// <summary>
/// The user info endpoint.
/// </summary>
private const string UserInfoEndpoint = "https://graph.facebook.com/me";
/// <summary>
/// The app id.
/// </summary>
private readonly string _appId;
/// <summary>
/// The app secret.
/// </summary>
private readonly string _appSecret;
/// <summary>
/// The requested scopes.
/// </summary>
private readonly string[] _requestedScopes;
#endregion
/// <summary>
/// Creates a new Facebook OAuth2 client, requesting the default "email" scope.
/// </summary>
/// <param name="appId">The Facebook App Id</param>
/// <param name="appSecret">The Facebook App Secret</param>
public FacebookClient(string appId, string appSecret)
: this(appId, appSecret, new[] { "email" }) { }
/// <summary>
/// Creates a new Facebook OAuth2 client.
/// </summary>
/// <param name="appId">The Facebook App Id</param>
/// <param name="appSecret">The Facebook App Secret</param>
/// <param name="requestedScopes">One or more requested scopes, passed without the base URI.</param>
public FacebookClient(string appId, string appSecret, params string[] requestedScopes)
: base("facebook")
{
if (string.IsNullOrWhiteSpace(appId))
throw new ArgumentNullException("appId");
if (string.IsNullOrWhiteSpace(appSecret))
throw new ArgumentNullException("appSecret");
if (requestedScopes == null)
throw new ArgumentNullException("requestedScopes");
if (requestedScopes.Length == 0)
throw new ArgumentException("One or more scopes must be requested.", "requestedScopes");
_appId = appId;
_appSecret = appSecret;
_requestedScopes = requestedScopes;
}
protected override Uri GetServiceLoginUrl(Uri returnUrl)
{
var state = string.IsNullOrEmpty(returnUrl.Query) ? string.Empty : returnUrl.Query.Substring(1);
return BuildUri(AuthorizationEndpoint, new NameValueCollection
{
{ "client_id", _appId },
{ "scope", string.Join(" ", _requestedScopes) },
{ "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
{ "state", state },
});
}
protected override IDictionary<string, string> GetUserData(string accessToken)
{
var uri = BuildUri(UserInfoEndpoint, new NameValueCollection { { "access_token", accessToken } });
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
using (var webResponse = webRequest.GetResponse())
using (var stream = webResponse.GetResponseStream())
{
if (stream == null)
return null;
using (var textReader = new StreamReader(stream))
{
var json = textReader.ReadToEnd();
var extraData = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
var data = extraData.ToDictionary(x => x.Key, x => x.Value.ToString());
data.Add("picture", string.Format("https://graph.facebook.com/{0}/picture", data["id"]));
return data;
}
}
}
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
var uri = BuildUri(TokenEndpoint, new NameValueCollection
{
{ "code", authorizationCode },
{ "client_id", _appId },
{ "client_secret", _appSecret },
{ "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
});
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
string accessToken = null;
HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
// handle response from FB
// this will not be a url with params like the first request to get the 'code'
Encoding rEncoding = Encoding.GetEncoding(response.CharacterSet);
using (StreamReader sr = new StreamReader(response.GetResponseStream(), rEncoding))
{
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var jsonObject = serializer.DeserializeObject(sr.ReadToEnd());
var jConvert = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(jsonObject));
Dictionary<string, object> desirializedJsonObject = JsonConvert.DeserializeObject<Dictionary<string, object>>(jConvert.ToString());
accessToken = desirializedJsonObject["access_token"].ToString();
}
return accessToken;
}
private static Uri BuildUri(string baseUri, NameValueCollection queryParameters)
{
var keyValuePairs = queryParameters.AllKeys.Select(k => HttpUtility.UrlEncode(k) + "=" + HttpUtility.UrlEncode(queryParameters[k]));
var qs = String.Join("&", keyValuePairs);
var builder = new UriBuilder(baseUri) { Query = qs };
return builder.Uri;
}
/// <summary>
/// Facebook works best when return data be packed into a "state" parameter.
/// This should be called before verifying the request, so that the url is rewritten to support this.
/// </summary>
public static void RewriteRequest()
{
var ctx = HttpContext.Current;
var stateString = HttpUtility.UrlDecode(ctx.Request.QueryString["state"]);
if (stateString == null || !stateString.Contains("__provider__=facebook"))
return;
var q = HttpUtility.ParseQueryString(stateString);
q.Add(ctx.Request.QueryString);
q.Remove("state");
ctx.RewritePath(ctx.Request.Path + "?" + q);
}
}
看这里,我已经用较新版本的链接替换了所有 API 链接。
现在你需要修改你的
AuthConfig
只需使用包装器 class 而不是 RegisterFacebookClient
。完全阻止那部分代码。并添加这个...
OAuthWebSecurity.RegisterClient(new FacebookClientV2Dot3("AppID", "HassedPassword"));
然后一切成功。您的 Facebook 登录将恢复到以前的状态。
然而,您可能会遇到关于这个新 API 而不是以前的 API 的新问题,问题是 IP Whitelisting
。像这样image。希望你只需要这个。编码愉快。