6

使用新的 OAuthWebSecurity 进行 Facebook 身份验证,我在我的 Facebook 应用程序中添加了电子邮件权限。现在,正如我所读到的,我需要定义一个范围,以便能够实际获取结果中的电子邮件。到目前为止,如果没有范围,我没有收到用户的电子邮件,也不确定为什么,因为我看不到在哪里定义“范围”。

它只是ASP.NET MVC 4 默认 authenticationcontrollers 外部登录的一部分。

4

7 回答 7

21

首先,extraData 参数没有传递给 facebook。它仅供内部使用。请参阅以下链接,了解如何在您的网站上使用这些数据:

http://blogs.msdn.com/b/pranav_rastogi/archive/2012/08/24/customizing-the-login-ui-when-using-oauth-openid.aspx

现在,到肉:

除了方法RegisterFacebookClientRegisterYahooClient之外OAuthWebSecurity,还有一个泛型方法RegisterClient。这是我们将用于此解决方案的方法。

这个想法源于以下提供的代码:http: //mvc4beginner.com/Sample-Code/Facebook-Twitter/MVC-4-oAuth-Facebook-Login-EMail-Problem-Solved.html

但是,我们不会使用解决方案提供的 hacky 方法。相反,我们将创建一个名为的新类FacebookScopedClient,它将实现IAuthenticationClient. 然后我们将简单地使用以下方法注册该类:

OAuthWebSecurity.RegisterClient(new FacebookScopedClient("your_app_id", "your_app_secret"), "Facebook", null);

在 AuthConfig.cs

该类的代码是:

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;

    public class FacebookScopedClient : IAuthenticationClient
        {
            private string appId;
            private string appSecret;

            private const string baseUrl = "https://www.facebook.com/dialog/oauth?client_id=";
            public const string graphApiToken = "https://graph.facebook.com/oauth/access_token?";
            public const string graphApiMe = "https://graph.facebook.com/me?";


            private static string GetHTML(string URL)
            {
                string connectionString = URL;

                try
                {
                    System.Net.HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(connectionString);
                    myRequest.Credentials = CredentialCache.DefaultCredentials;
                    //// Get the response
                    WebResponse webResponse = myRequest.GetResponse();
                    Stream respStream = webResponse.GetResponseStream();
                    ////
                    StreamReader ioStream = new StreamReader(respStream);
                    string pageContent = ioStream.ReadToEnd();
                    //// Close streams
                    ioStream.Close();
                    respStream.Close();
                    return pageContent;
                }
                catch (Exception)
                {
                }
                return null;
            }

            private  IDictionary<string, string> GetUserData(string accessCode, string redirectURI)
            {

                string token = GetHTML(graphApiToken + "client_id=" + appId + "&redirect_uri=" + HttpUtility.UrlEncode(redirectURI) + "&client_secret=" + appSecret + "&code=" + accessCode);
                if (token == null || token == "")
                {
                    return null;
                }
                string data = GetHTML(graphApiMe + "fields=id,name,email,gender,link&access_token=" + token.Substring("access_token=", "&"));

                // this dictionary must contains
                Dictionary<string, string> userData = JsonConvert.DeserializeObject<Dictionary<string, string>>(data);
                return userData;
            }

            public FacebookScopedClient(string appId, string appSecret)
            {
                this.appId = appId;
                this.appSecret = appSecret;
            }

            public string ProviderName
            {
                get { return "Facebook"; }
            }

            public void RequestAuthentication(System.Web.HttpContextBase context, Uri returnUrl)
            {
                string url = baseUrl + appId + "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString()) + "&scope=email";
                context.Response.Redirect(url);
            }

            public AuthenticationResult VerifyAuthentication(System.Web.HttpContextBase context)
            {
                string code = context.Request.QueryString["code"];

                string rawUrl = context.Request.Url.OriginalString;
                //From this we need to remove code portion
                rawUrl = Regex.Replace(rawUrl, "&code=[^&]*", "");

                IDictionary<string, string> userData = GetUserData(code, rawUrl);

                if (userData == null)
                    return new AuthenticationResult(false, ProviderName, null, null, null);

                string id = userData["id"];
                string username = userData["email"];
                userData.Remove("id");
                userData.Remove("email");

                AuthenticationResult result = new AuthenticationResult(true, ProviderName, id, username, userData);
                return result;
            }
        }

现在在

public ActionResult ExternalLoginCallback(string returnUrl)

方法中AccountControllerresult.ExtraData应该有email。

编辑:我错过了这篇文章中的一些代码。我在下面添加它:

public static class String
    {
        public static string Substring(this string str, string StartString, string EndString)
        {
            if (str.Contains(StartString))
            {
                int iStart = str.IndexOf(StartString) + StartString.Length;
                int iEnd = str.IndexOf(EndString, iStart);
                return str.Substring(iStart, (iEnd - iStart));
            }
            return null;
        }
    }

干杯!

于 2012-10-29T16:31:11.487 回答
13

更新 MVC4 Internet 项目中的 NuGet 包。

DotNetOpenAuthCore。它将自动更新所有依赖项。

现在 result.UserName 将包含电子邮件地址而不是您的姓名。

    [AllowAnonymous]
    public ActionResult ExternalLoginCallback(string returnUrl)
    {
        AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
        if (!result.IsSuccessful)
        {
            return RedirectToAction("ExternalLoginFailure");
        }

        if (OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false))
        {
            return RedirectToLocal(returnUrl);
        }

        if (User.Identity.IsAuthenticated)
        {
            // If the current user is logged in add the new account
            OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, User.Identity.Name);
            return RedirectToLocal(returnUrl);
        }
        else
        {
            // User is new, ask for their desired membership name
            string loginData = OAuthWebSecurity.SerializeProviderUserId(result.Provider, result.ProviderUserId);
            ViewBag.ProviderDisplayName = OAuthWebSecurity.GetOAuthClientData(result.Provider).DisplayName;
            ViewBag.ReturnUrl = returnUrl;
            return View("ExternalLoginConfirmation", new RegisterExternalLoginModel { UserName = result.UserName, ExternalLoginData = loginData });
        }
    }

这是什么原因?

https://github.com/AArnott/dotnetopenid/blob/a9d2443ee1a35f13c528cce35b5096abae7128f4/src/DotNetOpenAuth.AspNet/Clients/OAuth2/FacebookClient.cs已在最新的 NuGet 包中更新。

带有修复的提交: https ://github.com/AArnott/dotnetopenid/commit/a9d2443ee1a35f13c528cce35b5096abae7128f4

于 2012-12-09T18:21:18.520 回答
2

我使用了Varun 的答案,但我必须进行一些小修改才能使其适用于托管在AppHarbor上的应用程序。

AppHarbor 必须使用 url 中的端口号做一些时髦的事情来处理负载平衡。你可以在这里阅读更多关于它的信息。简而言之,在 AppHarbor 上托管时获取当前请求的 AbsoluteUri 可能会返回端口号不是 80 的 uri。这会导致 Facebook 身份验证出现问题,因为他们希望您的返回 url 是您在创建应用程序时指定的。

问题出现在string rawUrl = context.Request.Url.OriginalString;in VerifyAuthentication()。如果您使用此代码,rawUrl可能包含 80 以外的某些端口号,导致 Facebook 身份验证失败。相反,将该行替换为

string rawUrl = GetRawUrl(context.Request.Url);

并将GetRawUrl()函数添加到类中:

public static string GetRawUrl(Uri url)
{
    var port = url.Port;
    if (SettingsHelper.GetHostingService() == HostingServices.AppHarbor)
        port = 80;

    return new UriBuilder(url)
    {
        Port = port
    }.Uri.AbsoluteUri;
}

您需要替换if (SettingsHelper.GetHostingService() == HostingServices.AppHarbor)为您自己的逻辑来确定您的应用程序是否在 AppHarbor 上运行。

于 2013-01-28T16:53:18.660 回答
0

我为这个问题写了自己的解决方案。我扩展了 OAuth2Client 以利用它的工作,并使用 facebook 范围和其他功能来检索额外的用户数据。我在这里发布了自己的解决方案,希望对某人有所帮助!

于 2013-04-26T11:20:17.567 回答
0

由于 FB 强制要求“对重定向 URI 使用严格模式”,因此需要 RewriteRequest(对于 Google Oauth)。在 OAuthWebSecurity.VerifyAuthentication 之前的回调处理中添加下面的调用。

FacebookScopedClient.RewriteRequest();

FacebookScopedClient 类

using System;
using System.Collections.Generic;
using System.Text;
using DotNetOpenAuth.AspNet;
using System.Web;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using Newtonsoft.Json;

namespace UserAccounts.WebApi.ExternalLogin
{
    // Thnks to Har Kaur https://www.c-sharpcorner.com/blogs/facebook-integration-by-using-oauth and https://github.com/mj1856/DotNetOpenAuth.GoogleOAuth2/blob/master/DotNetOpenAuth.GoogleOAuth2/GoogleOAuth2Client.cs
    public class FacebookScopedClient : IAuthenticationClient
    {
        private string appId;
        private string appSecret;
        private static string providerName = "Facebook";

        private const string baseUrl = "https://www.facebook.com/dialog/oauth?client_id=";
        public const string graphApiToken = "https://graph.facebook.com/oauth/access_token?";
        public const string graphApiMe = "https://graph.facebook.com/me?";


        private static string GetHTML(string URL)
        {
            string connectionString = URL;

            try
            {
                System.Net.HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(connectionString);
                myRequest.Credentials = CredentialCache.DefaultCredentials;
                //// Get the response  
                WebResponse webResponse = myRequest.GetResponse();
                Stream respStream = webResponse.GetResponseStream();
                ////  
                StreamReader ioStream = new StreamReader(respStream);
                string pageContent = ioStream.ReadToEnd();
                //// Close streams  
                ioStream.Close();
                respStream.Close();
                return pageContent;
            }
            catch (WebException ex)
            {
                StreamReader reader = new StreamReader(ex.Response.GetResponseStream());
                string line;
                StringBuilder result = new StringBuilder();
                while ((line = reader.ReadLine()) != null)
                {
                    result.Append(line);
                }

            }
            catch (Exception)
            {
            }
            return null;
        }


        private IDictionary<string, string> GetUserData(string accessCode, string redirectURI)
        {
            string value = "";
            string token = GetHTML(graphApiToken + "client_id=" + appId + "&redirect_uri=" +
                                    HttpUtility.UrlEncode(redirectURI) + "&client_secret=" +
                                       appSecret + "&code=" + accessCode);
            if (token == null || token == "")
            {
                return null;
            }
            if (token != null || token != "")
            {
                if (token.IndexOf("access_token") > -1)
                {
                    string[] arrtoken = token.Replace("\''", "").Split(':');
                    string[] arrval = arrtoken[1].ToString().Split(',');
                    value = arrval[0].ToString().Replace("\"", "");
                }
            }
            string data = GetHTML(graphApiMe + "fields=id,name,email,gender,link&access_token=" + value);


            // this dictionary must contains  
            Dictionary<string, string> userData = JsonConvert.DeserializeObject<Dictionary<string, string>>(data);
            return userData;
        }


        public FacebookScopedClient(string appId, string appSecret)
        {
            this.appId = appId;
            this.appSecret = appSecret;
        }

        public string ProviderName
        {
            get { return providerName; }
        }

        public void RequestAuthentication(System.Web.HttpContextBase context, Uri returnUrl)
        {
            var uriBuilder = new UriBuilder(returnUrl);
            uriBuilder.Query = "";
            var newUri = uriBuilder.Uri;

            string returnUrlQuery = HttpUtility.UrlEncode(returnUrl.Query);
            string url = baseUrl + appId + "&scope=email" + "&state=" + returnUrlQuery + "&redirect_uri=" + HttpUtility.UrlEncode(newUri.ToString());
            context.Response.Redirect(url);
        }

        public AuthenticationResult VerifyAuthentication(System.Web.HttpContextBase context)
        {
            string code = context.Request.QueryString["code"];

            string rawUrl = context.Request.Url.OriginalString;
            //From this we need to remove code portion  
            rawUrl = Regex.Replace(rawUrl, "&code=[^&]*", "");
            var uriBuilder = new UriBuilder(rawUrl);
            uriBuilder.Query = "";
            var newUri = uriBuilder.Uri;

            IDictionary<string, string> userData = GetUserData(code, newUri.ToString());

            if (userData == null)
                return new AuthenticationResult(false, ProviderName, null, null, null);

            string id = userData["id"];
            string username = userData["email"];
            userData.Remove("id");
            userData.Remove("email");

            AuthenticationResult result = new AuthenticationResult(true, ProviderName, id, username, userData);
            return result;
        }

        /// <summary>
        /// Facebook requires that all 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.
        /// Thnks to Matt Johnson mj1856 https://github.com/mj1856/DotNetOpenAuth.GoogleOAuth2/blob/master/DotNetOpenAuth.GoogleOAuth2/GoogleOAuth2Client.cs
        /// </summary>
        /// 
        public static void RewriteRequest()
        {
            var ctx = HttpContext.Current;

            var stateString = HttpUtility.UrlDecode(ctx.Request.QueryString["state"]);
            if (stateString == null || !stateString.Contains("__provider__=" + providerName)) return;

            var q = HttpUtility.ParseQueryString(stateString);
            q.Add(ctx.Request.QueryString);
            q.Remove("state");

            ctx.RewritePath(ctx.Request.Path + "?" + q);
        }
    }
}
于 2018-10-06T19:22:47.427 回答
-1

我在这里遇到了同样的问题。我发现将“范围”参数传递给 facebook 的唯一方法是编写我自己的 OAuth 客户端。

为此,您必须扩展和实现 DotNetOpenAuth.AspNet.Clients.OAuth2Client 的抽象方法。

在 GetServiceLoginUrl 方法中,您可以将范围参数添加到 url。因此,当您调用 OAuthWebSecurity.VerifyAuthentication() 方法时,AuthenticationResult.UserName 会提供用户的电子邮件。

一个例子可以在这里找到。

祝你好运。

于 2012-09-27T16:20:29.153 回答
-2

可以这样做......像这样:

var fb = new Dictionary<string, object>();
fb.Add("scope", "email,publish_actions");
OAuthWebSecurity.RegisterFacebookClient(
appId: ConfigurationManager.AppSettings["FacebookAppId"],
appSecret: ConfigurationManager.AppSettings["FacebookAppSecret"],
displayName: "FaceBook",
extraData: fb);
于 2013-04-18T20:37:32.157 回答