net mvc form example crear asp facebook-graph-api oauth-2.0 facebook-login identityserver3 katana

facebook graph api - mvc - Identity Server 3 Facebook Login Obtener correo electrónico



mvc authentication (1)

Finalmente me di cuenta de esto. La respuesta tiene algo que ver con los comentarios de Mitra, aunque ninguna de esas respuestas parecía encajar en la factura, así que pondré otra aquí. En primer lugar, debe solicitar access_token, no código (código de autorización) desde el punto final de autenticación de Facebook. Para hacer eso, configúralo así

var fb = new FacebookAuthenticationOptions { AuthenticationType = "Facebook", SignInAsAuthenticationType = signInAsType, AppId = ConfigurationManager.AppSettings["Facebook:AppId"], AppSecret = ConfigurationManager.AppSettings["Facebook:AppSecret"], Provider = new FacebookAuthenticationProvider() { OnAuthenticated = (context) => { context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, ClaimValueTypes.String, "Facebook")); return Task.FromResult(0); } } }; fb.Scope.Add("email"); app.UseFacebookAuthentication(fb);

Luego, debe detectar la respuesta una vez que haya iniciado sesión. Estoy usando el siguiente archivo del repositorio de ejemplos de IdentityServer3 , que sobrescribe (lee, proporciona funcionalidad) los métodos necesarios para iniciar sesión en un usuario desde sitios externos. A partir de esta respuesta, estoy usando el SDK de Facebook de C # con el reclamo de access_token recién devuelto en ExternalAuthenticationContext para solicitar los campos que necesito y agregarlos a la lista de reclamaciones. Entonces puedo usar esa información para crear / iniciar sesión en el usuario.

public override async Task AuthenticateExternalAsync(ExternalAuthenticationContext ctx) { var externalUser = ctx.ExternalIdentity; var claimsList = ctx.ExternalIdentity.Claims.ToList(); if (externalUser.Provider == "Facebook") { var extraClaims = GetAdditionalFacebookClaims(externalUser.Claims.First(claim => claim.Type == "urn:facebook:access_token")); claimsList.Add(new Claim("email", extraClaims.First(k => k.Key == "email").Value.ToString())); claimsList.Add(new Claim("given_name", extraClaims.First(k => k.Key == "first_name").Value.ToString())); claimsList.Add(new Claim("family_name", extraClaims.First(k => k.Key == "last_name").Value.ToString())); } if (externalUser == null) { throw new ArgumentNullException("externalUser"); } var user = await userManager.FindAsync(new Microsoft.AspNet.Identity.UserLoginInfo(externalUser.Provider, externalUser.ProviderId)); if (user == null) { ctx.AuthenticateResult = await ProcessNewExternalAccountAsync(externalUser.Provider, externalUser.ProviderId, claimsList); } else { ctx.AuthenticateResult = await ProcessExistingExternalAccountAsync(user.Id, externalUser.Provider, externalUser.ProviderId, claimsList); } }

¡Y eso es! Si tiene alguna sugerencia para simplificar este proceso, hágamelo saber. Iba a modificar este código para realizar la llamada a la API desde FacebookAuthenticationOptions, pero la propiedad Events ya no existe.

Editar: el método GetAdditionalFacebookClaims es simplemente un método que crea un nuevo FacebookClient dado el token de acceso que se retiró y consulta la API de Facebook para las reclamaciones de otros usuarios que necesita. Por ejemplo, mi método se ve así:

protected static JsonObject GetAdditionalFacebookClaims(Claim accessToken) { var fb = new FacebookClient(accessToken.Value); return fb.Get("me", new {fields = new[] {"email", "first_name", "last_name"}}) as JsonObject; }

El servidor de identidad está implementado y funciona bien. El inicio de sesión de Google funciona y devuelve varios reclamos, incluido el correo electrónico.

El inicio de sesión en Facebook está funcionando, y mi aplicación es en vivo y solicita permisos de correo electrónico cuando un nuevo usuario inicia sesión.

El problema es que no puedo recuperar el correo electrónico desde el punto final y parece que no puedo encontrar el access_token para solicitar manualmente la información del usuario. Todo lo que tengo es un "código" devuelto desde el punto de inicio de sesión de Facebook.

Aquí está la configuración de IdentityServer.

var fb = new FacebookAuthenticationOptions { AuthenticationType = "Facebook", SignInAsAuthenticationType = signInAsType, AppId = ConfigurationManager.AppSettings["Facebook:AppId"], AppSecret = ConfigurationManager.AppSettings["Facebook:AppSecret"] }; fb.Scope.Add("email"); app.UseFacebookAuthentication(fb);

Entonces, por supuesto, he personalizado el método AuthenticateLocalAsync, pero los reclamos que recibo solo incluyen el nombre. Sin reclamo por correo electrónico.

Explorando el código fuente para el servidor de identidad, me di cuenta de que hay algunas afirmaciones que están sucediendo para transformar los reclamos de Facebook, por lo que amplié esa clase para depurarla y ver si estaba eliminando cualquier reclamo, lo que no es cierto.

También vi las llamadas http con fiddler, y solo veo lo siguiente (disculpas porque el formato de código no funciona muy bien en las URL. Intenté formatear los params de la cadena de consulta uno con sus propias líneas, pero no fue así)

  1. (facebook.com)

    / dialog / oauth? response_type = code & client_id = xxx & redirect_uri = https% 3A% 2F% 2Fidentity. [site] .com% 2Fid% 2Fsignin-facebook & scope = email & state = xxx

  2. (facebook.com)

    /login.php? skip_api_login = 1 & api_key = xxx & signed_next = 1 & next = https% 3A% 2F% 2Fwww.facebook.com% 2Fv2.7% 2Fdialog% 2Foauth% 3Fredirect_uri% 3Dhttps% 253A% 252F% 252Fidentity. [sitio]. com% 252Fid% 252Fsignin-facebook% 26state% 3Dxxx% 26scope% 3Demail% 26response_type% 3Dcode% 26client_id% 3Dxxx% 26ret% 3Dlogin% 26logger_id% 3Dxxx & cancel_url = https% 3A% 2F% 2Fidentity. [site] .com% 2Fid% 2Fsignin- facebook% 3Ferror% 3Daccess_denied% 26error_code% 3D200% 26error_description% 3DPermissions% 2Berror% 26error_reason% 3Duser_denied% 26state% 3Dxxx% 23_% 3D_ & display = page & locale = en_US & logger_id = xxx

  3. (facebook.com)

    POST / cookie / consent /? Pv = 1 & dpr = 1 HTTP / 1.1

  4. (facebook.com)

    /login.php? login_attempt = 1 & next = https% 3A% 2F% 2Fwww.facebook.com% 2Fv2.7% 2Fdialog% 2Foauth% 3Fredirect_uri% 3Dhttps% 253A% 252F% 252Fidentity. [site] .com% 252Fid% 252Fsignin- Facebook% 26Estado% 3Dxxx% 26scope% 3Demail% 26response_type% 3Dcode% 26client_id% 3Dxxx% 26ret% 3Dlogin% 26logger_id% 3Dxxx & lwv = 100

  5. (facebook.com)

    /v2.7/dialog/oauth?redirect_uri = https% 3A% 2F% 2Fidentity. [site] .com% 2Fid% 2Fsignin-facebook & state = xxx & scope = email & response_type = code & client_id = xxx & ret = login & logger_id = xxx & hash = xxx

  6. (servidor de identidad)

    / id / signin-facebook? code = xxx y estado = xxx

Vi el parámetro de código en esa última llamada y pensé que tal vez podría usar el código allí para obtener el access_token de la API de Facebook https://developers.facebook.com/docs/facebook-login/manually-build-a-login -fluir

Sin embargo, cuando lo intenté, recibí un mensaje de la API que indicaba que el código ya se había utilizado.

También traté de cambiar el UserInformationEndpoint a FacebookAuthenticationOptions para forzarlo a solicitar el correo electrónico al agregar? Fields = email al final de la ubicación predeterminada del punto final, pero eso hace que el servidor de identidad escuche el error "Hubo un error al iniciar sesión el proveedor externo. El mensaje de error es: access_denied ".

Podría ser capaz de arreglar todo esto si puedo cambiar el middleware para enviar la solicitud con response_type = id_token pero no puedo encontrar la manera de hacerlo o cómo extraer ese token de acceso cuando se devuelve, en primer lugar, para ser capaz de usar el Cd de Facebook #.

Así que supongo que cualquier ayuda o dirección sería increíble. He pasado innumerables horas investigando e intentando resolver el problema. Todo lo que necesito hacer es obtener la dirección de correo electrónico del usuario conectado a través de IdentityServer3. No suena tan duro y, sin embargo, estoy atascado.