//----------------------------------------------------------------------- // // Copyright (c) Microsoft. All rights reserved. // //----------------------------------------------------------------------- namespace DotNetOpenAuth.AspNet.Clients { using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Net; using System.Web; using DotNetOpenAuth.Messaging; /// /// The facebook client. /// [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Facebook", Justification = "Brand name")] public sealed class FacebookClient : OAuth2Client { #region Constants and Fields /// /// The authorization endpoint. /// private const string AuthorizationEndpoint = "https://www.facebook.com/dialog/oauth"; /// /// The token endpoint. /// private const string TokenEndpoint = "https://graph.facebook.com/oauth/access_token"; /// /// The _app id. /// private readonly string appId; /// /// The _app secret. /// private readonly string appSecret; #endregion #region Constructors and Destructors /// /// Initializes a new instance of the class. /// /// /// The app id. /// /// /// The app secret. /// public FacebookClient(string appId, string appSecret) : base("facebook") { Requires.NotNullOrEmpty(appId, "appId"); Requires.NotNullOrEmpty(appSecret, "appSecret"); this.appId = appId; this.appSecret = appSecret; } #endregion #region Methods /// /// The get service login url. /// /// /// The return url. /// /// An absolute URI. protected override Uri GetServiceLoginUrl(Uri returnUrl) { // Note: Facebook doesn't like us to url-encode the redirect_uri value var builder = new UriBuilder(AuthorizationEndpoint); builder.AppendQueryArgs( new Dictionary { { "client_id", this.appId }, { "redirect_uri", returnUrl.AbsoluteUri } }); return builder.Uri; } /// /// The get user data. /// /// /// The access token. /// /// A dictionary of profile data. protected override IDictionary GetUserData(string accessToken) { FacebookGraphData graphData; var request = WebRequest.Create( "https://graph.facebook.com/me?access_token=" + MessagingUtilities.EscapeUriDataStringRfc3986(accessToken)); using (var response = request.GetResponse()) { using (var responseStream = response.GetResponseStream()) { graphData = JsonHelper.Deserialize(responseStream); } } // this dictionary must contains var userData = new Dictionary(); userData.AddItemIfNotEmpty("id", graphData.Id); userData.AddItemIfNotEmpty("username", graphData.Email); userData.AddItemIfNotEmpty("name", graphData.Name); userData.AddItemIfNotEmpty("link", graphData.Link == null ? null : graphData.Link.AbsoluteUri); userData.AddItemIfNotEmpty("gender", graphData.Gender); userData.AddItemIfNotEmpty("birthday", graphData.Birthday); return userData; } /// /// Obtains an access token given an authorization code and callback URL. /// /// /// The return url. /// /// /// The authorization code. /// /// /// The access token. /// protected override string QueryAccessToken(Uri returnUrl, string authorizationCode) { // Note: Facebook doesn't like us to url-encode the redirect_uri value var builder = new UriBuilder(TokenEndpoint); builder.AppendQueryArgs( new Dictionary { { "client_id", this.appId }, { "redirect_uri", NormalizeHexEncoding(returnUrl.AbsoluteUri) }, { "client_secret", this.appSecret }, { "code", authorizationCode }, }); using (WebClient client = new WebClient()) { string data = client.DownloadString(builder.Uri); if (string.IsNullOrEmpty(data)) { return null; } var parsedQueryString = HttpUtility.ParseQueryString(data); return parsedQueryString["access_token"]; } } /// /// Converts any % encoded values in the URL to uppercase. /// /// The URL string to normalize /// The normalized url /// NormalizeHexEncoding("Login.aspx?ReturnUrl=%2fAccount%2fManage.aspx") returns "Login.aspx?ReturnUrl=%2FAccount%2FManage.aspx" /// /// There is an issue in Facebook whereby it will rejects the redirect_uri value if /// the url contains lowercase % encoded values. /// private static string NormalizeHexEncoding(string url) { var chars = url.ToCharArray(); for (int i = 0; i < chars.Length - 2; i++) { if (chars[i] == '%') { chars[i + 1] = char.ToUpperInvariant(chars[i + 1]); chars[i + 2] = char.ToUpperInvariant(chars[i + 2]); i += 2; } } return new string(chars); } #endregion } }