2012-09-26 22 views
6

Utilizando el nuevo OAuthWebSecurity para autenticar con Facebook, agregué el permiso de correo electrónico en mi aplicación de Facebook. Ahora, como puedo leer, necesito definir un alcance para poder obtener realmente el correo electrónico en el resultado. Hasta ahora, sin el alcance, no recibo el correo electrónico de los usuarios y no estoy seguro de por qué, ya que no puedo ver dónde definir el "alcance".OAuthWebSecurity con Facebook sin permiso de correo electrónico como se esperaba

Es solo una copia del ASP.NET MVC 4 iniciadores de autenticación de autenticación externos.

+0

Quizás esto ayude: http://blogs.msdn.com/b/webdev/archive/2012/08/23/plugging-custom-oauth-openid-providers.aspx – CBroe

Respuesta

0

Me enfrenté al mismo problema aquí. La única forma que encontré para pasar el parámetro "alcance" a Facebook fue codificar mi propio cliente OAuth.

Para hacerlo, debe extender e implementar los métodos abstractos de DotNetOpenAuth.AspNet.Clients.OAuth2Client.

En el método GetServiceLoginUrl puede agregar el parámetro de ámbito a la url. Por lo tanto, cuando llama al método OAuthWebSecurity.VerifyAuthentication(), AuthenticationResult.UserName proporciona el correo electrónico del usuario.

Se puede encontrar un ejemplo here.

Buena suerte.

+0

Bien, ahora miré una vez más el método para registrar facebook y tiene una sobrecarga que toma algún parámetro extraData, pero ¿es esto para el inicio de sesión? IDictionary fbParams = new Dictionary (); fbParams.Add ("alcance", "correo electrónico"); OAuthWebSecurity.RegisterFacebookClient ("onekey", "anotherkey", "facebook", fbParams); –

+0

Lo probé. Pero OAuthWebSecurity fue ignorado. ¿Te ha funcionado? –

+0

No habría sido considerado ... parece un desperdicio no habilitar las declaraciones "extraData" de esta manera. Me pregunto cuál fue el razonamiento para no hacerlo de esa manera. –

21

En primer lugar, el parámetro extraData no se pasa a Facebook. Es solo para uso interno. Véase el siguiente enlace de cómo estos datos se puede utilizar en su sitio:

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

Ahora, a la carne:

Además de los métodos RegisterFacebookClient, etc. RegisterYahooClient en OAuthWebSecurity, también hay un método genérico RegisterClient. Este es el método que usaremos para esta solución.

Esta idea germina a partir del código proporcionado en: http://mvc4beginner.com/Sample-Code/Facebook-Twitter/MVC-4-oAuth-Facebook-Login-EMail-Problem-Solved.html

Sin embargo, no vamos a utilizar el enfoque hacky proporcionada por la solución. En cambio, crearemos una nueva clase llamada FacebookScopedClient que implementará IAuthenticationClient. A continuación, vamos a simplemente registrar la clase usando:

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

en AuthConfig.cs

El código para la clase es:

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

ahora en el método

public ActionResult ExternalLoginCallback(string returnUrl) 

en AccountController, result.ExtraData debe tener el correo electrónico.

Editar: He perdido un código en esta publicación. Estoy añadiendo a continuación:

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

Salud!

+0

Tuve un problema con su línea "token.Substring (" access_token = "," & "))", ya que la subcadena solo acepta números enteros, pero esto ayudó inmensamente. Decidí escribir una publicación en el blog explicando cómo ampliar esto un poco más, pero esta publicación me consiguió el 98%. http://savvydev.com/authenticating-facebook-users-with-mvc-4-oauth-and-obtaining-scope-permissions/ – MattSavage

+0

Disculpa.Se olvidó de incluir la clase de extensión para la cadena. Agregándolo al código ahora ... –

+0

Gracias, Varun. Tuve que hacer un pequeño ajuste para las aplicaciones alojadas en AppHarbor (que explico en [otra respuesta] (http://stackoverflow.com/a/14567037/389899)) pero usted hizo todo el trabajo duro. – blachniet

13

Actualice su paquete NuGet en su proyecto de Internet MVC4.

DotNetOpenAuthCore. Se actualizará automáticamente todas las dependencias.

Ahora result.UserName contendrá la dirección de correo electrónico en lugar de su nombre.

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

¿La razón de esto?

https://github.com/AArnott/dotnetopenid/blob/a9d2443ee1a35f13c528cce35b5096abae7128f4/src/DotNetOpenAuth.AspNet/Clients/OAuth2/FacebookClient.cs ha sido actualizado en el último paquete NuGet.

El comprometerse con la solución: https://github.com/AArnott/dotnetopenid/commit/a9d2443ee1a35f13c528cce35b5096abae7128f4

+1

Esto es genial :) muchas gracias, eso me ahorró tiempo y esfuerzo para personalizarlo, ahora está construido como debería ser;) – Shadi

+0

@PUssInBoots. Grandes instrucciones. Los seguí y recibí un correo electrónico de vuelta, excepto que ahora es '[email protected].facebook.com' que no es lo que quiero. ¿Alguna idea de por qué recibo esta larga dirección de proxymail.facebook.com? – user576838

+0

@ user576838 Nunca antes había visto ese tipo de correo electrónico en mi aplicación. Tal vez intente iniciar sesión con otro usuario con otro correo electrónico de otro proveedor de correo electrónico, vea si obtiene los mismos resultados. – PussInBoots

2

que utilizan Varun's answer, pero tenía que hacer una pequeña modificación para conseguir que funcione para mi aplicación está alojada en AppHarbor.

AppHarbor tiene que hacer algunas cosas funky con el número de puerto en direcciones URL para manejar el equilibrio de carga. Puede leer un poco más al respecto here. En resumen, obtener el AbsoluteUri de la solicitud actual mientras se aloja en AppHarbor puede devolver un uri con un número de puerto distinto a 80. Esto causa problemas con la autenticación de Facebook, ya que esperan que la url de devolución sea la que especificó al crear su aplicación.

El problema viene en string rawUrl = context.Request.Url.OriginalString; en VerifyAuthentication(). Si usa este código, rawUrl puede contener un número de puerto distinto a 80, lo que hace que falle la autenticación de Facebook. En su lugar, vuelva a colocar esa línea con

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

y añadir la función GetRawUrl() a la clase:

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

Usted tendrá que reemplazar if (SettingsHelper.GetHostingService() == HostingServices.AppHarbor) con su propia lógica para determinar si su aplicación se ejecuta en AppHarbor .

-2

Se puede hacer ... como esto:

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); 
+1

Esto parece muy plausible. Pero no funcionó para mí. – elif

+1

Esto no funciona. El parámetro "extraData" no se envía ni usa Facebook. –

1

escribí mi propia solución a este problema. Amplié el OAuth2Client para aprovechar su trabajo y usé el alcance de Facebook y otras características para recuperar datos adicionales del usuario. Publiqué mi propia solución here, ¡espero que ayude a alguien!

Cuestiones relacionadas