Приложение .NET ASP.NET MVC Facebook OAuth с определенной областью действия

Как проще всего создать .NET приложение ASP.NET MVC с Facebook OAuth с определенной областью действия?

Я пробовал много примеров. OAuthWebSecurity.RegisterClient не поддерживает добавление большего охвата Facebook. шаблон приложения Facebook создает холст и Мне нужно приложение без холста. FacebookScopedClient не заполнен и не может работать с этим исправлением.

Что ты посоветуешь?

Я также открыт для решений JavaScript/jQuery.


person Liad Livnat    schedule 20.02.2013    source источник


Ответы (2)


Возможно, я опоздал, но я столкнулся с этой проблемой несколько дней назад и нашел только несколько плохих решений в Интернете. Поэтому я написал свое собственное решение и могу опубликовать его только сейчас.

Я воспользовался классом DotNetOpenAuth.AspNet.Clients.OAuth2Client, который выполняет большую часть работы. Я только расширил это, включив область действия и дополнительные пользовательские данные.

public class FacebookExtendedClient : DotNetOpenAuth.AspNet.Clients.OAuth2Client
{   
    protected FacebookClient facebookClient;
    protected string fields;
    protected string scope;
    protected Func<string, object, string> fieldTransformer;
    protected bool emailAsUsername;
    protected IDictionary<string, string> userData;

    private string[] splittedFields;
    private string[] splittedScope;

    protected const string serviceLoginBaseUrl = "https://www.facebook.com/dialog/oauth";
    protected const string serviceMeBaseUrl = "https://graph.facebook.com/me";
    protected const string serviceAccessTokenBaseUrl = "https://graph.facebook.com/oauth/access_token";

    /// <summary>
    /// Create an instrance of the class.
    /// </summary>
    /// <param name="appId">The App ID of the application used to connect to Facebook service.</param>
    /// <param name="appSecret">The App Secret of the application used to connect to Facebook service.</param>
    /// <param name="fields">
    /// String containing comma separated fields to add to the request.
    /// If empty the request will retrieve the default fields based of the specified scope.
    /// </param>
    /// <param name="fieldTransformer">
    /// Function to be applied to the values retrived from facebook.
    /// If null provided the method will try to cast values from object to string explicitly,
    /// an InvalidCastException will be thrown if the cast will not be possible.
    /// </param>
    /// <param name="scope">
    /// String containing comma separated permissions to add to the request.
    /// If empty the request will have the basic scope.
    /// </param>
    /// <param name="emailAsUsername">Makes the email of the facebook user used as authentication username.</param>
    public FacebookExtendedClient(string appId, string appSecret, string fields = "", Func<string, object, string> fieldTransformer = null, string scope = "", bool emailAsUsername = false)
        : base("facebook")
    {
        if (string.IsNullOrEmpty(appId))
            throw new ArgumentException("The appId argument can not be null or empty.", "appId");
        if (string.IsNullOrEmpty(appSecret))
            throw new ArgumentException("The appSecret argument can not be null or empty.", "appSecret");

        fields = fields.Replace(" ", "");
        scope = scope.Replace(" ", "");
        this.splittedFields = fields.Split(',');
        this.splittedScope = scope.Split(',');

        if (emailAsUsername == true && !this.splittedFields.Contains("email") && !this.splittedScope.Contains("email"))
            throw new ArgumentException("The scope argument must contain the 'email' permission and the 'email' field to allow emailAsUsername to true.", "scope");

        this.facebookClient = new FacebookClient();
        this.facebookClient.AppId = appId;
        this.facebookClient.AppSecret = appSecret;
        this.fields = fields;
        this.fieldTransformer = fieldTransformer;
        this.scope = scope;
        this.emailAsUsername = emailAsUsername;
    }

    public FacebookClient FacebookClient
    {
        get
        {
            return this.facebookClient;
        }
    }

    public IDictionary<string, string> UserData
    {
        get
        {
            return this.userData;
        }
    }

    protected override Uri GetServiceLoginUrl(Uri returnUrl)
    {
        Dictionary<string, object> parameters = new Dictionary<string, object>();
        parameters.Add("redirect_uri", returnUrl.AbsoluteUri);

        if (!string.IsNullOrEmpty(this.scope))
            parameters.Add("scope", this.scope);

        return this.facebookClient.GetLoginUrl(parameters);
    }

    protected override IDictionary<string, string> GetUserData(string accessToken)
    {
        // This method makes the AuthenticationResult's UserName property be the facebook username of the logged user,
        // but if the facebook username is missing the facebook id will be used.
        // If emailAsUsername is true then AuthenticationResult's UserName property is the email retrieved from facebook
        // and the facebook username can be retrieved by the key "fb_username" in this.userData

        FacebookClient facebookClient = new FacebookClient(accessToken);

        var getResult = facebookClient.Get<IDictionary<string, object>>("me", new { fields = this.fields });
        Dictionary<string, string> result = new Dictionary<string, string>();

        if (this.fieldTransformer != null)
        {
            foreach (var pair in getResult)
                result.Add(pair.Key, this.fieldTransformer(pair.Key, pair.Value));
        }
        else
        {
            foreach (var pair in getResult)
            {
                string value = pair.Value.ToString();

                if (value == null)
                    throw new InvalidCastException("Cast not possible for the object associate to the key '" + pair.Key + "'.");

                result.Add(pair.Key, value);
            }
        }

        if (this.splittedFields.Contains("username"))
            result["fb_username"] = result["username"];

        if (this.emailAsUsername)
            result["username"] = result["email"];

        this.userData = result;

        return result;
    }

    protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
    {
        UriBuilder builder = new UriBuilder(serviceAccessTokenBaseUrl);
        builder.Query = string.Format("client_id={0}&client_secret={1}&redirect_uri={2}&code={3}",
            this.facebookClient.AppId, this.facebookClient.AppSecret, HttpUtility.UrlEncode(Encoding.ASCII.GetBytes(returnUrl.AbsoluteUri)), authorizationCode);

        using (WebClient client = new WebClient())
        {
            string str = client.DownloadString(builder.Uri);

            if (string.IsNullOrEmpty(str))
                return null;

            return HttpUtility.ParseQueryString(str)["access_token"];
        }
    }
}

Вы также можете использовать его, зарегистрировав его в OAuthWebSecurity следующим образом (поместите метод RegisterAuth в Application_Start, как в шаблоне InternetApplication):

public static class AuthConfig
{
    public static void RegisterAuth()
    {
        configuration.LoadFromAppSettings();

        OAuthWebSecurity.RegisterClient(new FacebookExtendedClient(
            "##YOUR_APP_ID##",
            "##YOUR_APP_SECRET##",
            "id,first_name,last_name,link,username,gender,email,age_range,picture.height(200)",
            new Func<string, object, string>(fieldsTransformer),
            "email"));
    }

    private static string fieldsTransformer(string key, object value)
    {
        switch (key)
        {
            case "picture":
                var data = (value as IDictionary<string, object>)["data"] as IDictionary<string, object>;
                return data["url"].ToString();
            case "age_range":
                var min = (value as IDictionary<string, object>)["min"];
                return min.ToString();
            default:
                return value.ToString();
        }
    }
}

Как вы можете видеть в приведенном выше примере, метод fieldsTransformer получит ключ и значение для каждого выбранного поля, в этом случае он преобразует объект, полученный facebook для изображения, в URL-адрес изображения. Это удобный метод, если для параметра Func указано значение null, будет сохранено JSON-представление объекта значений.

Получить информацию о клиенте позже, после входа в систему, можно следующим образом:

[Authorize]
public class HomeController : Controller
{
    public ActionResult Index()
    {
        IDictionary<string, string> userData = (OAuthWebSecurity.GetOAuthClientData("facebook").AuthenticationClient as FacebookExtendedClient).UserData;
        string email = userData["email"];

        // If leave null the fieldTransform of the client you can access to complex properties like this:
        JObject picture = JObject.Parse(userData["picture"]);
        string url = (picture["data"] as JObject)["url"].ToString();

        ViewBag.Email = userData["email"];
        ViewBag.PictureUrl = url;
        return View();
    }
}

Надеюсь, вам понравится этот код, даже если он немного запоздал! :)

person Paolo Dragone    schedule 26.04.2013
comment
Спасибо, что поделились этим Дунаданом. Какое пространство имен для FacebookClient в классе FacebookExtendedClient? Если я использую предложенное пространство имен DotNetOpenAuth.AspNet.Clients, я получаю множество синтаксических ошибок при использовании FacebookClient. - person Amethi; 07.05.2013
comment
Ах, это клиент Facebook, доступный через NuGet. - person Amethi; 07.05.2013
comment
Да, помните об использовании правильной сборки: FacebookClient входит в пакет Facebook, а не DotNetOpenAuth.AspNet.Clients, к сожалению, они имеют одинаковое имя. - person Paolo Dragone; 07.05.2013
comment
Мне нравится этот подход, и я использовал что-то очень похожее, прежде чем увидел этот пост. Меня беспокоит то, что если вашему приложению требуются разные разрешения в разное время в разных пользовательских сценариях, как вы параметризуете это, не получая распространения подклассов OAuth2Client, каждый из которых инициализируется с разной областью? - person John Grant; 17.07.2013
comment
@Dunadan, в проекте MVC 4 я реализовал это так, во-первых, чтобы показать Facebook в имени кнопки (@p.Displayname), в _ExternalLoginsListPartial.cshtml в файле AuthConfig.cs требуется следующая настройка: OAuthWebSecurity.RegisterClient (новый FacebookExtendedClient(... электронная почта), Facebook, ноль); 2-й: код, который вы добавили в HomeController/Index (я думаю, как тестирование), должен быть помещен/настроен в Accountcontroller/ExternalLoginCallback . Тем не менее большое спасибо за ваш код, мне очень понравилась эта реализация! - person firepol; 02.07.2014

Я пишу свое первое приложение для Facebook с веб-приложением С# MVC 5 в VS 2013.

В файл AppStart\StartupAuth.cs я внес следующие изменения.

Убедитесь, что в классе есть следующие операторы использования:

using Microsoft.AspNet.Identity;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Facebook;
using Owin;`

Затем в разделе аутентификации Facebook в нижней части метода ConfigureAuth:

FacebookAuthenticationOptions fbOptions = new FacebookAuthenticationOptions();
fbOptions.AppId="YOUR_APP_ID";
fbOptions.AppSecret="YOUR_APP_SECRET";
fbOptions.Scope.Add("user_about_me");
fbOptions.Scope.Add("user_actions.books");
fbOptions.Scope.Add("user_actions.music");
//...more at the bottom
fbOptions.Scope.Add("video_upload");
fbOptions.Scope.Add("xmpp_login");
app.UseFacebookAuthentication(fbOptions);

Вот и все! Ниже приведен полный список на 01.02.2014:

fbOptions.Scope.Add("user_about_me");
fbOptions.Scope.Add("user_actions.books");
fbOptions.Scope.Add("user_actions.music");
fbOptions.Scope.Add("user_actions.news");
fbOptions.Scope.Add("user_actions.video");
fbOptions.Scope.Add("user_activities");
fbOptions.Scope.Add("user_birthday");
fbOptions.Scope.Add("user_checkins");
fbOptions.Scope.Add("user_education_history");
fbOptions.Scope.Add("user_events");
fbOptions.Scope.Add("user_friends");
fbOptions.Scope.Add("user_games_activity");
fbOptions.Scope.Add("user_groups");
fbOptions.Scope.Add("user_hometown");
fbOptions.Scope.Add("user_interests");
fbOptions.Scope.Add("user_likes");
fbOptions.Scope.Add("user_location");
fbOptions.Scope.Add("user_notes");
fbOptions.Scope.Add("user_online_presence");
fbOptions.Scope.Add("user_photo_video_tags");
fbOptions.Scope.Add("user_photos");
fbOptions.Scope.Add("user_questions");
fbOptions.Scope.Add("user_relationship_details");
fbOptions.Scope.Add("user_relationships");
fbOptions.Scope.Add("user_religion_politics");
fbOptions.Scope.Add("user_status");
fbOptions.Scope.Add("user_subscriptions");
fbOptions.Scope.Add("user_videos");
fbOptions.Scope.Add("user_website");
fbOptions.Scope.Add("user_work_history");

fbOptions.Scope.Add("friends_about_me");
fbOptions.Scope.Add("friends_actions.books");
fbOptions.Scope.Add("friends_actions.music");
fbOptions.Scope.Add("friends_actions.news");
fbOptions.Scope.Add("friends_actions.video");
fbOptions.Scope.Add("friends_activities");
fbOptions.Scope.Add("friends_birthday");
fbOptions.Scope.Add("friends_checkins");
fbOptions.Scope.Add("friends_education_history");
fbOptions.Scope.Add("friends_events");
fbOptions.Scope.Add("friends_games_activity");
fbOptions.Scope.Add("friends_groups");
fbOptions.Scope.Add("friends_hometown");
fbOptions.Scope.Add("friends_interests");
fbOptions.Scope.Add("friends_likes");
fbOptions.Scope.Add("friends_location");
fbOptions.Scope.Add("friends_notes");
fbOptions.Scope.Add("friends_online_presence");
fbOptions.Scope.Add("friends_photo_video_tags");
fbOptions.Scope.Add("friends_photos");
fbOptions.Scope.Add("friends_questions");
fbOptions.Scope.Add("friends_relationship_details");
fbOptions.Scope.Add("friends_relationships");
fbOptions.Scope.Add("friends_religion_politics");
fbOptions.Scope.Add("friends_status");
fbOptions.Scope.Add("friends_subscriptions");
fbOptions.Scope.Add("friends_videos");
fbOptions.Scope.Add("friends_website");
fbOptions.Scope.Add("friends_work_history");

fbOptions.Scope.Add("ads_management");
fbOptions.Scope.Add("ads_read");
fbOptions.Scope.Add("create_event");
fbOptions.Scope.Add("create_note");
fbOptions.Scope.Add("email");
fbOptions.Scope.Add("export_stream");
fbOptions.Scope.Add("manage_friendlists");
fbOptions.Scope.Add("manage_notifications");
fbOptions.Scope.Add("manage_pages");
fbOptions.Scope.Add("photo_upload");
fbOptions.Scope.Add("publish_actions");
fbOptions.Scope.Add("publish_checkins");
fbOptions.Scope.Add("publish_stream");
fbOptions.Scope.Add("read_friendlists");
fbOptions.Scope.Add("read_insights");
fbOptions.Scope.Add("read_mailbox");
fbOptions.Scope.Add("read_page_mailboxes");
fbOptions.Scope.Add("read_requests");
fbOptions.Scope.Add("read_stream");
fbOptions.Scope.Add("rsvp_event");
fbOptions.Scope.Add("share_item");
fbOptions.Scope.Add("sms");
fbOptions.Scope.Add("status_update");
fbOptions.Scope.Add("video_upload");
fbOptions.Scope.Add("xmpp_login");
person Rich    schedule 02.02.2014