【问题标题】:Facebook web application extended permissions second step dont showFacebook网络应用扩展权限第二步不显示
【发布时间】:2013-05-23 14:44:00
【问题描述】:

更新2 这篇文章已经过时但仍然相关..以下是我解决它的方法。我标记了其他人的答案,因为我认为它更好地回答了这个问题。我在 accountcontroller 中调用了一个类似的方法(我即将重构:))。字符串应该是一个列表......我想你明白了。

/// <summary>
    /// Use this method when an action fails due to lack of priviligies. It will redirect user to facebook with provided permission request.
    /// Refactor to handle list of request.
    /// </summary>
    /// <param name="permission"></param>
    private static void AddAdditionalPermissions(string permission)
    {
        System.Diagnostics.Trace.TraceInformation(permission + " not authorized for user.");
        string facebook_urlAuthorize_base = "https://graph.facebook.com/oauth/authorize";
        string scope = permission; //see: https://developers.facebook.com/docs/authentication/permissions/ for extended permissions
        string urlAuthorize = facebook_urlAuthorize_base;
        urlAuthorize += "?client_id=" + AppId;
        urlAuthorize += "&redirect_uri=" + "https://fbd.anteckna.nu/";
        urlAuthorize += "&scope=" + scope;

        //redirect the users browser to Facebook to ask the user to authorize our Facebook application
        HttpContext.Current.Response.Redirect(urlAuthorize, true); //this cannot be done using WebRequest since facebook may need to show dialogs in the users browser
    }

然后,每个使用 facebook C# SDK 调用 facebook(如 /me/home)的方法都会捕获 FacebookOAuthException 并重定向到以下方法。这就是我们应用最佳实践的方式,即在需要时不事先询问用户权限。这个方法应该也有匹配的 aredirect url,但我们刚刚开始:)

希望对你有帮助!

/// <summary>
    /// Check for what permissions to request or different ways to handle FacebookOAuthExceptions.
    /// </summary>
    /// <param name="foae">The exception object</param>
    public static void HandleAuthorizationsExceptions(FacebookOAuthException foae)
    {
        if (foae.Message.Contains("publish_permissions"))
        {
            AddAdditionalPermissions("publish_permissions");
        }
        else if (foae.Message.Contains("read_stream"))
        {
            AddAdditionalPermissions("read_stream");
        }
        else
        {
            System.Diagnostics.Trace.TraceError("Unhandled error at:" + foae.StackTrace);
        }
    }

更新:此行为是由 .Net oauth 实现引起的,该实现具有在密封类中硬编码的范围。添加了图 4 以显示请求参数,其中除了“电子邮件”(由 .net oauth 提供程序与所有请求一起发送)之外缺少其他范围。将“,publish_stream”添加到查询字符串给了我想要的行为。有谁知道如何做到这一点?

请不要提交有关 facebook 最佳实践或替代解决方案的答案或 cmets。我有一个替代解决方案,但希望它与默认的 registerfacebookclient 参数一起使用。我已根据指定我要求的权限的两个答案更新了应用程序以仅使用 publish_stream。

图4

原问题: 我正在设置一个应用程序(C#.Net4.5 MVC4,剃刀视图),它几乎需要来自 facebook 的所有可用用户权限。 您可以在下面查看我如何设置的代码示例。

问题是当点击图 1 中的“okay”时,Facebook 将我发送回我的应用程序。据我了解,应该有一个额外的屏幕(图 2)要求“更重”权限。截至目前,我只获得图一中所述的权限。那部分有效...

图 1

图 2

所以,使用基本 AuthConfig.cs

var facebooksocialData = new Dictionary<string, object>();
            facebooksocialData.Add("scope", "email,publish_stream,read_stream,publish_actions,manage_pages,create_event,offline_access");
            OAuthWebSecurity.RegisterFacebookClient(
                appId: "165359673639901",
                appSecret: "15091cb2094a1996ae6c7b324f0300e6",
                displayName: "Facebook",
                extraData: facebooksocialData);

这就是我处理响应的方式,但这里 facebook 没有提示用户扩展权限,而只是提示电子邮件,

AccountController.cs

 //
        // GET: /Account/ExternalLoginCallback

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

            // Save the accesstoken into session
            Session["accesstoken"] = result.ExtraData["accesstoken"];
            Session["id"] = result.ExtraData["id"];

            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 });
            }
        }

我能找到的最接近答案的是一个 wp 插件,它有同样的问题。通过将域设置为 localhost 解决了他们的问题。这就是我的应用程序的设置方式。

【问题讨论】:

    标签: c# asp.net facebook asp.net-mvc-4 facebook-c#-sdk


    【解决方案1】:

    我遇到了同样的问题。正如您所做的那样,我使用字典配置了 RegisterFacebookClient 来定义我的应用程序的范围,不幸的是,请求没有包括我配置的范围。所以我找到了that。这似乎可行,但这还不够。于是我找到了this

    所以这就是解决我的问题的方法:

    首先我将这个新客户端添加到我的代码中:

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Web;
    using DotNetOpenAuth.AspNet;
    using Newtonsoft.Json;
    
    namespace MyApp.UI.Infrastructure
    {
        public class FacebookScopedClient : IAuthenticationClient
        {
            private string appId;
            private string appSecret;
            private string scope;
    
            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 access_token = token.Substring(token.IndexOf("access_token="), token.IndexOf("&"));
                string data = GetHTML(graphApiMe + "fields=id,name,email,username,gender,link&" + access_token);
    
                // this dictionary must contains
                Dictionary<string, string> userData = JsonConvert.DeserializeObject<Dictionary<string, string>>(data);
                return userData;
            }
    
            public FacebookScopedClient(string appId, string appSecret, string scope)
            {
                this.appId = appId;
                this.appSecret = appSecret;
                this.scope = scope;
            }
    
            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=" + scope;
                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["username"];
                userData.Remove("id");
                userData.Remove("username");
    
                AuthenticationResult result = new AuthenticationResult(true, ProviderName, id, username, userData);
                return result;
            }
        }
    }
    

    我将它放在我的 asp.net 解决方案中的“基础设施”文件夹中,然后我更改了旧配置,以便使用新的 facebook 客户端,如下所示:

    旧代码:

    OAuthWebSecurity.RegisterFacebookClient(
    appId: "<app-id>",
    appSecret: "<app-secret>",
    displayName: "Facebook",
    extraData: facebookExtraData);
    

    新代码:

    OAuthWebSecurity.RegisterClient(
                        new FacebookScopedClient(
                            "<app-id>",
                            "<app-secret>",
                            "scope"), 
                        "Facebook", 
                        null);
    

    就是这样。它可能会帮助你,就像帮助我一样。

    【讨论】:

    • 这很奇怪。您的解决方案对我有用,但仅限于本地。一旦我将我的网站发布到云端,facebook 身份验证将不再有效。我的应用程序域在 facebook 上正确配置,因为默认的 facebookclient 有效。
    • @Bastaspast,天蓝色的问题是角色实例 URL 在其特定的网络端口内被调用,因为属性“context.Request.Url.OriginalString”返回它。所以它错过了匹配传递给 facebook 的 URL。我还没有找到一种优雅的方法来解决它,但我做到了:#if !DEBUG rawUrl = rawUrl.Replace(":" + context.Request.Url.Port, ""); #endif 因此,一切都将在本地和云端顺利运行。但是这个解决方案只允许你在本地的 DEBUG 中运行,在这种情况下你可以评论那行。
    【解决方案2】:

    您的应用是否注册了这些范围?我熟悉 Google OAuth,他们有一个单独的范围映射到一个权限。您的应用应注册范围,以便获得第二个窗口。否则,您将只能访问您的第一个弹出窗口要求的公共信息..

    【讨论】:

    • 我没有在范围内注册应用程序。但是,这并没有解决我的问题。进一步调查我的问题我看到了这种行为,因为作为范围提供的额外数据仅在发出请求时设置电子邮件。问题出在使用 .Net OAuth 的某个地方。
    【解决方案3】:

    首先,offline_access 已经不存在了,所以它来自你要求的权限。

    “[app] 几乎需要来自 facebook 的所有可用用户权限”

    Facebook 不鼓励从一开始就“以防万一”直接请求大量权限,因为以后可能会需要这些权限。仅当用户第一次触发的操作实际需要扩展权限时,才应请求扩展权限。

    另外,您应该分别请求“读取”和“写入”权限。

    我不知道这些方面是否真的触发了您的错误 - 但我知道 Facebook 已经针对读/写的事情发送了开发人员警报;尽管 FB 员工确认这些警报现在可以忽略,但他们可能会在未来某个时候开始强制执行

    【讨论】:

    • 感谢您的建议 CBroe。我正在为盲人和聋人社区构建一个带有界面的应用程序。这要求我使用尽可能少的交互和弹出窗口。
    • 我认为 OP 是正确的,他的作用域在 SDK 的某个地方丢失了,而不是在对 Facebook 的实际调用中
    • Magnus Karlsson 我也遇到了同样的问题,请问您的解决方法了吗?
    • @IsaiyavanBabuKaran 是的,我创建了一个向 facebook 发送请求的方法,我可以在每个 FacebookOAuthException 中使用它。奇迹般有效。明天我会用 som 代码更新。顺便说一句,我认为当您需要预先获得权限时,Matheus 代码看起来很棒!我现在正在投票:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-03
    • 2011-03-07
    • 2011-05-15
    • 1970-01-01
    • 1970-01-01
    • 2013-02-28
    相关资源
    最近更新 更多