2012-09-26 16 views
6

Używając nowego OAuthWebSecurity do uwierzytelnienia z Facebookiem, dodałem uprawnienia do wiadomości e-mail w mojej aplikacji na Facebooku. Teraz, jak mogę przeczytać, muszę zdefiniować zakres, aby móc faktycznie otrzymać e-mail w wyniku. Do tej pory bez zasięgu nie dostaję maila od użytkowników i nie jestem pewien dlaczego, ponieważ nie widzę gdzie zdefiniować "zasięg".OAuthWebSecurity z Facebookiem bez zgody e-mail zgodnie z oczekiwaniami

To tylko rip z domyślnego logowania użytkownika zewnętrznego.

+0

Może to pomaga: http://blogs.msdn.com/b/webdev/archive/2012/08/23/plugging-custom-oauth-openid-providers.aspx – CBroe

Odpowiedz

0

Zmierzyłem się z tym samym problemem. Jedynym sposobem, jaki udało mi się przekazać parametrowi "zasięg" do facebooka, było kodowanie mojego własnego klienta OAuth.

Aby to zrobić, należy rozszerzyć i wdrożyć abstrakcyjne metody DotNetOpenAuth.AspNet.Clients.OAuth2Client.

W metodzie GetServiceLoginUrl można dodać parametr scope do adresu URL. Po wywołaniu metody OAuthWebSecurity.VerifyAuthentication() właściwość AuthenticationResult.UserName zapewnia adres e-mail użytkownika.

Przykład można znaleźć here.

Powodzenia.

+0

Teraz jeszcze raz przyjrzałem się metodzie zarejestrować facebook i ma przeciążenie, które pobiera jakiś parametr extraData, ale czy jest to login? IDictionary fbParams = new Dictionary (); fbParams.Add ("zakres", "e-mail"); OAuthWebSecurity.RegisterFacebookClient ("onekey", "anotherkey", "facebook", fbParams); –

+0

Próbowałem. Ale OAuthWebSecurity został zignorowany. Pracowałeś dla ciebie? –

+0

Czy nie byłoby to przemyślane ... wydaje się być odpadem, aby nie zezwalać na takie deklaracje "extraData". Zastanawiam się, jakie było uzasadnienie, że nie robimy tego w ten sposób. –

21

Po pierwsze, parametr extraData nie jest przekazywany do Facebooka. Jest przeznaczony wyłącznie do użytku wewnętrznego. Zobacz poniższy link, w jaki sposób dane te mogą być wykorzystywane na swojej stronie:

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

Teraz do mięsa:

Oprócz metod RegisterFacebookClient, RegisterYahooClient itp w OAuthWebSecurity, istnieje również ogólna metoda RegisterClient. Jest to metoda, której będziemy używać w tym rozwiązaniu.

Ten pomysł kiełkuje z kodu umieszczonego na: http://mvc4beginner.com/Sample-Code/Facebook-Twitter/MVC-4-oAuth-Facebook-Login-EMail-Problem-Solved.html

Jednak nie będzie używany hacky podejście przewidziane przez rozwiązanie. Zamiast tego stworzymy nową klasę o nazwie FacebookScopedClient, która będzie implementować IAuthenticationClient. Wtedy wystarczy zarejestrować klasę używając:

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

w AuthConfig.cs

Kod dla klasy jest:

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

teraz w metodzie

public ActionResult ExternalLoginCallback(string returnUrl) 

w AccountController, result.ExtraData powinien mieć wiadomość e-mail.

Edytuj: Brakuje mi jakiegoś kodu w tym poście. Dodaję go poniżej:

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

Pozdrawiam!

+0

Miałem problem z linią "token.Substring (" access_token = "," & "))), ponieważ Substring akceptuje tylko liczby całkowite, ale to pomogło niezmiernie. Postanowiłem napisać post na blogu wyjaśniający, jak rozszerzyć to nieco dalej, ale ten post dostarczył mi 98%. http://savvydev.com/authenticating-facebook-users-with-mvc-4-oauth-and-obtaining-scope-permissions/ – MattSavage

+0

Przeprosiny.Zapomniałem dodać klasy rozszerzenia dla ciągu znaków. Dodanie go do kodu teraz ..... –

+0

Dzięki, Varun. Musiałem wprowadzić drobne poprawki dla aplikacji hostowanych w AppHarbor (co wyjaśnię w [innej odpowiedzi] (http://stackoverflow.com/a/14567037/389899)), ale wykonałeś całą ciężką pracę. – blachniet

13

Zaktualizuj swój pakiet NuGet w swoim projekcie internetowym MVC4.

DotNetOpenAuthCore. Automatycznie zaktualizuje wszystkie zależności.

Teraz result.UserName będzie zawierać adres e-mail zamiast Twojego imienia i nazwiska.

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

Powód tego?

https://github.com/AArnott/dotnetopenid/blob/a9d2443ee1a35f13c528cce35b5096abae7128f4/src/DotNetOpenAuth.AspNet/Clients/OAuth2/FacebookClient.cs został zaktualizowany w najnowszym pakiecie NuGet.

popełnić z fix: https://github.com/AArnott/dotnetopenid/commit/a9d2443ee1a35f13c528cce35b5096abae7128f4

+1

To jest świetne :) bardzo dziękuję, że zaoszczędziło mi czasu i wysiłku, aby go dostosować, teraz jest zbudowany tak jak powinien;) – Shadi

+0

@PUssInBoots. Świetne instrukcje. Śledziłem ich i otrzymuję e-mail z powrotem, z wyjątkiem tego, że jest teraz "[email protected].facebook.com", co nie jest tym, czego chcę. Jakiś pomysł, dlaczego otrzymuję ten długi adres proxymail.facebook.com? – user576838

+0

@ user576838 Nigdy wcześniej nie widziałem tego rodzaju wiadomości e-mail w mojej aplikacji. Może spróbuj zalogować się u innego użytkownika za pomocą innej wiadomości e-mail od innego dostawcy poczty e-mail, sprawdź, czy uzyskasz takie same wyniki. – PussInBoots

2

użyłem Varun's answer, ale musiałem zrobić małą modyfikację aby zmusić go do pracy dla mojej aplikacji jest utrzymywana na AppHarbor.

AppHarbor musi zrobić kilka funky z numerem portu w adresach URL, aby poradzić sobie z równoważeniem obciążenia. Możesz przeczytać trochę więcej na ten temat here. W skrócie, pobranie wartości AbsoluteUri z bieżącego żądania podczas hostowania w AppHarbor może zwrócić uri z numerem portu innym niż 80. Powoduje to problemy z uwierzytelnianiem Facebooka, ponieważ oczekują one, że Twój URL powrotu będzie taki, jaki podałeś przy tworzeniu aplikacji.

Problem występuje pod adresem string rawUrl = context.Request.Url.OriginalString; w VerifyAuthentication(). Jeśli użyjesz tego kodu, rawUrl może zawierać numer portu inny niż 80, powodując niepowodzenie uwierzytelniania Facebook. Zamiast zastąpić tę linię z

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

i dodać funkcję GetRawUrl() do klasy:

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

trzeba będzie zastąpić if (SettingsHelper.GetHostingService() == HostingServices.AppHarbor) z własnej logiki do ustalenia, czy aplikacja jest uruchomiona na AppHarbor .

-2

To można zrobić ... tak:

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

To wygląda bardzo wiarygodnie. Ale nie zadziałało to dla mnie. – elif

+1

To nie działa. Parametr "extraData" nie jest wysyłany do lub używany przez Facebook. –

1

pisałam własne rozwiązanie tego problemu. Rozszerzyłem OAuth2Client, aby wykorzystać to działanie i wykorzystałem zasięg Facebooka i inne funkcje do pobrania dodatkowych danych użytkownika. Napisałem własne rozwiązanie here, mam nadzieję, że pomoże komuś!

Powiązane problemy