2013-05-23 13 views
11

Update2 Questo post sta diventando vecchio ma ancora rilevante .. Di seguito è il modo in cui l'ho risolto. Ho segnato la risposta degli altri ragazzi perché penso che risponda meglio alla domanda. Sto chiamando un metodo simile (sto per refactoring :)) in accountcontroller. La stringa dovrebbe essere una lista ... Penso che tu la capisca.Facebook web application autorizzazioni estese secondo passo non mostrare

/// <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 
    } 

Poi ogni metodo di effettuare una chiamata a Facebook Like/Mi/casa con facebok C# SDK cattura FacebookOAuthException e reindirizza al metodo folling. Questo è il modo in cui applichiamo la best practice per non chiedere autorizzazioni agli utenti in anticipo ma quando necessario. Questo metodo dovrebbe avere l'URL aredirect che corrisponde allo stesso modo, ma abbiamo appena iniziato :)

Spero che sia d'aiuto!

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

Aggiornamento: Questo comportamento è causato da attuazione oauth Net che ha lo scopo hard coded in classi chiuse. Aggiunta la figura 4 per mostrare il parametro di richiesta in cui la mancanza di ambiti aggiuntivi oltre a "email" (che viene inviato con tutte le richieste dal provider .net oauth). L'aggiunta di ", publish_stream" alla stringa di query mi dà il comportamento desiderato. Qualcuno sa come raggiungerlo?

Si prega di non inviare risposte o commenti sulle migliori pratiche di Facebook o soluzioni alternative. Ho una soluzione alternativa ma vorrei che funzionasse con i parametri registerfacebookclient predefiniti. Ho aggiornato l'applicazione per utilizzare publish_stream in base alle due risposte specificando quali autorizzazioni sto richiedendo.

figura 4 Image showing scope parameter in query string

domanda originale: Sono la creazione di un'applicazione (C# .Net4.5 MVC4, vista rasoio) che hanno bisogno di più o meno tutte le autorizzazioni degli utenti disponibili da facebook. Puoi vedere esempi di codice sotto come ho impostato tutto.

Il problema è che quando si fa clic su "OK" nella figura 1, Facebook mi rimanda alla mia applicazione. Come ho capito, dovrebbe esserci uno schermo aggiuntivo (figura 2) che richiede i permessi "più pesanti". A partire da ora ho solo le autorizzazioni indicate nella figura uno. Quella parte funziona ...

Figura 1 Facebook permissions dialog C#

Figura 2 enter image description here

Quindi, utilizzando di base 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); 

Questo è come gestire la risposta, ma qui Facebook non ha richiesto all'utente le autorizzazioni estese ma solo per la posta elettronica,

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

Il più vicino ad una risposta ho trovato è stato un plugin wp che aveva lo stesso problema. Il loro problema è stato risolto impostando il dominio su localhost. Ecco come è impostata la mia applicazione. ! [Inserisci la descrizione dell'immagine qui] [4]

risposta

7

Ho avuto lo stesso problema. Come ho fatto, ho configurato RegisterFacebookClient con il dizionario per definire l'ambito della mia app e, sfortunatamente, la richiesta non includeva l'ambito come configurato. Quindi ho trovato that. Sembra che avrebbe funzionato, ma non era abbastanza. Quindi ho trovato this.

ecco cosa risolvere i miei problemi:

Prima di tutto ho aggiunto questo nuovo client per il mio codice:

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

ho messo su una cartella "infrastruttura" nella mia soluzione asp.net con roba oder, prossimo a cambiare la mia vecchia configurazione, al fine di utilizzare il nuovo client di Facebook, come segue:

vecchio codice:

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

Nuovo Codice:

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

Questo è tutto. Potrebbe aiutarti, come mi ha aiutato.

+2

Questo è strano. Le tue soluzioni funzionano per me, ma solo locali.Una volta che pubblico il mio sito web nel cloud, l'autenticazione di Facebook non funziona più. I domini delle mie app sono configurati correttamente su Facebook, perché funziona con Facebookclient predefinito. – Bastaspast

+1

@Bastaspast, il problema in azzurro è che il ruolo di instace URL è chiamato all'interno della sua specifica porta di rete, in quanto la proprietà 'context.Request.Url.OriginalString' lo restituisce. Quindi manca la corrispondenza con l'URL passato a Facebook. Non ho trovato un modo elegante per risolverlo, ma l'ho fatto: '#if! DEBUG rawUrl = rawUrl.Replace (": "+ context.Request.Url.Port," "); # endif' Quindi tutto funzionerà perfettamente localmente e nel cloud. Ma questa soluzione ti consente di eseguire solo in DEBUG in locale, in una mano in cui puoi commentare quella riga in questo caso. –

+0

grazie per la tua spiegazione! – Bastaspast

1

La tua app è registrata per questi ambiti?Conosco Google OAuth, hanno un ambito separato che esegue il mapping su una sola autorizzazione. La tua app deve essere registrata per gli ambiti, al fine di ottenere la seconda finestra. Altrimenti, avrai accesso solo alle informazioni pubbliche richieste dal tuo primo popup ..

+1

Non avevo registrato l'applicazione con gli ambiti. Tuttavia, questo non ha risolto il mio problema. Analizzando ulteriormente il mio problema, ho visto questo comportamento perché l'extradata fornito come ambito imposta la posta solo quando viene effettuata la richiesta. Il problema è da qualche parte nell'uso di .Net OAuth. –

1

Prima di tutto, offline_access non esiste più, quindi dalle autorizzazioni che stai richiedendo.

"[App] che hanno bisogno di più o meno tutte le autorizzazioni degli utenti disponibili da Facebook"

Facebook scoraggia attivamente chiedendo cumuli di permessi direttamente dal principio "just in case" perché potrebbero essere necessario in seguito. Si dovrebbe chiedere un permesso esteso solo quando è effettivamente necessario per un'azione che l'utente ha appena attivato per la prima volta.

Inoltre, è necessario chiedere le autorizzazioni di "lettura" e "scrittura" separatamente.

Non so se questi aspetti stiano effettivamente innescando il tuo errore - ma so che Facebook ha già inviato avvisi sugli sviluppatori per la cosa in lettura/scrittura; anche se un dipendente di FB ha confermato che tali avvisi possono essere ignorati per ora, potrebbero iniziare applicando lo a un certo punto nel futuro.

+1

Grazie per il tuo consiglio CBroe. Sto costruendo un'applicazione con un'interfaccia per la comunità cieca e non udente. Ciò mi richiede di usare il minor numero possibile di interazioni e popup. –

+0

Penso che OP sia corretto e che i suoi scopi vengano persi da qualche parte nell'SDK, non nell'effettiva chiamata a Facebook – Igy

+0

Magnus Karlsson Anch'io sto affrontando lo stesso problema, hai avuto qualche soluzione? –

Problemi correlati