internal protected virtual void ApiAsync(string path, IDictionary <string, object> parameters, HttpMethod httpMethod, object userToken) { var mergedParameters = FacebookUtils.Merge(null, parameters); if (!mergedParameters.ContainsKey("access_token") && !String.IsNullOrEmpty(AccessToken)) { mergedParameters["access_token"] = AccessToken; } Uri requestUrl; string contentType; byte[] postData = BuildRequestData(path, mergedParameters, httpMethod, out requestUrl, out contentType); var tempState = new WebClientStateContainer { UserState = userToken, Method = httpMethod, RequestUri = requestUrl }; Action <string, Exception> callback = (json, ex) => { byte[] result = null; if (ex == null) { result = Encoding.UTF8.GetBytes(json); } if (httpMethod == HttpMethod.Get) { DownloadDataCompleted(this, new DownloadDataCompletedEventArgsWrapper(ex, false, tempState, result)); } else { UploadDataCompleted(this, new UploadDataCompletedEventArgsWrapper(ex, false, tempState, result)); } }; var request = (HttpWebRequest)HttpWebRequest.Create(requestUrl); request.Method = FacebookUtils.ConvertToString(httpMethod); // Set the http method GET, POST, etc. if (httpMethod == HttpMethod.Post) { if (path == null && mergedParameters.ContainsKey("batch")) { tempState.IsBatchRequest = true; } request.ContentType = contentType; request.BeginGetRequestStream((ar) => { RequestCallback(ar, postData, callback, tempState); }, request); } else { request.BeginGetResponse((ar) => { ResponseCallback(ar, callback, tempState); }, request); } }
private byte[] BuildRequestData(string path, IDictionary <string, object> parameters, HttpMethod method, out Uri requestUrl, out string contentType) { parameters = parameters ?? new Dictionary <string, object>(); path = FacebookUtils.ParseQueryParametersToDictionary(path, parameters); Uri baseUrl; if (parameters.ContainsKey("method")) { if (String.IsNullOrEmpty((string)parameters["method"])) { throw new ArgumentException("You must specify a value for the method parameter."); } // Set the format to json parameters["format"] = "json-strings"; baseUrl = GetApiUrl(parameters["method"].ToString()); } else { if (!String.IsNullOrEmpty(path) && path.StartsWith("/", StringComparison.Ordinal)) { path = path.Substring(1, path.Length - 1); } baseUrl = GetUrl("graph", path); } return(BuildRequestData(baseUrl, parameters, method, out requestUrl, out contentType)); }
/// <summary> /// Parses the session value from a cookie. /// </summary> /// <param name="appSecret"> /// The app Secret. /// </param> /// <param name="cookieValue"> /// The session value. /// </param> /// <returns> /// The Facebook session object. /// </returns> internal static FacebookSession ParseCookieValue(string appSecret, string cookieValue) { Contract.Requires(!String.IsNullOrEmpty(appSecret)); Contract.Requires(!String.IsNullOrEmpty(cookieValue)); Contract.Requires(!cookieValue.Contains(","), "Session value must not contain a comma."); // var cookieValue = "\"access_token=124973200873702%7C2.OAaqICOCk_B4sZNv59q8Yg__.3600.1295118000-100001327642026%7Cvz4H9xjlRZPfg2quCv0XOM5g9_o&expires=1295118000&secret=lddpssZCuPoEtjcDFcWtoA__&session_key=2.OAaqICOCk_B4sZNv59q8Yg__.3600.1295118000-100001327642026&sig=1d95fa4b3dfa5b26c01c8ac8676d80b8&uid=100001327642026\""; // var result = FacebookSession.Parse("3b4a872617be2ae1932baa1d4d240272", cookieValue); // Parse the cookie var dictionary = new JsonObject(); var parts = cookieValue.Replace("\"", string.Empty).Split('&'); foreach (var part in parts) { if (!string.IsNullOrEmpty(part) && part.Contains("=")) { var nameValue = part.Split('='); if (nameValue.Length == 2) { var s = FacebookUtils.UrlDecode(nameValue[1]); dictionary.Add(nameValue[0], s); } } } var signature = GenerateSessionSignature(appSecret, dictionary); if (dictionary.ContainsKey("sig") && dictionary["sig"].ToString() == signature) { return(new FacebookSession(dictionary)); } return(null); }
/// <summary> /// Build the URL for given domain alias, path and parameters. /// </summary> /// <param name="name"> /// The name of the domain (from the domain maps). /// </param> /// <param name="path"> /// Optional path (without a leading slash) /// </param> /// <param name="parameters"> /// Optional query parameters /// </param> /// <returns> /// The string of the url for the given parameters. /// </returns> internal virtual Uri GetUrl(string name, string path, IDictionary <string, object> parameters) { Contract.Requires(!String.IsNullOrEmpty(name)); Contract.Ensures(Contract.Result <Uri>() != default(Uri)); return(FacebookUtils.GetUrl(DomainMaps, name, path, parameters)); }
internal protected virtual object Api(string path, IDictionary <string, object> parameters, HttpMethod httpMethod, Type resultType) { var mergedParameters = FacebookUtils.Merge(null, parameters); if (!mergedParameters.ContainsKey("access_token") && !string.IsNullOrEmpty(AccessToken)) { mergedParameters["access_token"] = AccessToken; } Uri requestUrl; string contentType; byte[] postData = BuildRequestData(path, mergedParameters, httpMethod, out requestUrl, out contentType); var jsonString = MakeRequest(httpMethod, requestUrl, postData, contentType); var json = JsonSerializer.Current.DeserializeObject(jsonString); FacebookApiException facebookApiException = ExceptionFactory.GetGraphException(json) ?? ExceptionFactory.CheckForRestException(DomainMaps, requestUrl, json); if (facebookApiException != null) { throw facebookApiException; } return(resultType == null ? json : JsonSerializer.Current.DeserializeObject(jsonString, resultType)); }
/// <summary> /// Gets the login uri. /// </summary> /// <param name="parameters"> /// The parameters. /// </param> /// <returns> /// Returns the facebook login uri. /// </returns> /// <remarks> /// http://developers.facebook.com/docs/reference/dialogs/oauth /// Parameters that can be used: /// client_id : Your application's identifier. This is called client_id instead of app_id for this particular method to be compliant with the OAuth 2.0 specification. Required, but automatically specified by most SDKs. /// redirect_uri : The URL to redirect to after the user clicks a button on the dialog. Required, but automatically specified by most SDKs. /// scope : Optional. A comma-delimited list of permissions. /// state : Optional. An opaque string used to maintain application state between the request and callback. When Facebook redirects the user back to your redirect_uri, this value will be included unchanged in the response. /// response_type : Optional, default is token. The requested response: an access token (token), an authorization code (code), or both (code_and_token). /// display : The display mode in which to render the dialog. The default is page on the www subdomain and wap on the m subdomain. This is automatically specified by most SDKs. (For WP7 builds it is set to touch.) /// </remarks> public Uri GetLoginUrl(IDictionary <string, object> parameters) { Contract.Ensures(Contract.Result <Uri>() != null); var defaultParameters = new Dictionary <string, object>(); defaultParameters["client_id"] = AppId; defaultParameters["redirect_uri"] = RedirectUri ?? new Uri("http://www.facebook.com/connect/login_success.html"); #if WINDOWS_PHONE defaultParameters["display"] = "touch"; #endif var mergedParameters = FacebookUtils.Merge(defaultParameters, parameters); // check if client_id and redirect_uri is not null or empty. if (mergedParameters["client_id"] == null || string.IsNullOrEmpty(mergedParameters["client_id"].ToString())) { throw new ArgumentException("client_id required."); } if (mergedParameters["redirect_uri"] == null || string.IsNullOrEmpty(mergedParameters["redirect_uri"].ToString())) { throw new ArgumentException("redirect_uri required."); } // seems like if we don't do this and rather pass the original uri object, // it seems to have http://localhost:80/csharpsamples instead of // http://localhost/csharpsamples // notice the port number, that shouldn't be there. // this seems to happen for iis hosted apps. mergedParameters["redirect_uri"] = mergedParameters["redirect_uri"].ToString(); var url = "http://www.facebook.com/dialog/oauth/?" + FacebookUtils.ToJsonQueryString(mergedParameters); return(new Uri(url)); }
/// <summary> /// Generates a MD5 signature for the facebook session. /// </summary> /// <param name="secret"> /// The app secret. /// </param> /// <param name="dictionary"> /// The dictionary. /// </param> /// <returns> /// Returns the generated signature. /// </returns> /// <remarks> /// http://developers.facebook.com/docs/authentication/ /// </remarks> internal static string GenerateSessionSignature(string secret, IDictionary <string, object> dictionary) { Contract.Requires(!string.IsNullOrEmpty(secret)); Contract.Requires(dictionary != null); Contract.Ensures(!string.IsNullOrEmpty(Contract.Result <string>())); var payload = new StringBuilder(); // sort by the key and remove "sig" if present var parts = (from a in dictionary orderby a.Key where a.Key != "sig" select string.Format(CultureInfo.InvariantCulture, "{0}={1}", a.Key, a.Value)).ToList(); parts.ForEach(s => payload.Append(s)); payload.Append(secret); var hash = FacebookUtils.ComputerMd5Hash(Encoding.UTF8.GetBytes(payload.ToString())); var signature = new StringBuilder(); foreach (var h in hash) { signature.Append(h.ToString("x2", CultureInfo.InvariantCulture)); } return(signature.ToString()); }
/// <summary> /// Gets the logout url. /// </summary> /// <param name="parameters"> /// The parameters. /// </param> /// <returns> /// Returns the logout url. /// </returns> public Uri GetLogoutUrl(IDictionary <string, object> parameters) { // more information on this at http://stackoverflow.com/questions/2764436/facebook-oauth-logout var uriBuilder = new UriBuilder("http://m.facebook.com/logout.php"); var defaultParams = new Dictionary <string, object>(); defaultParams["confirm"] = 1; defaultParams["next"] = RedirectUri ?? new Uri("http://www.facebook.com"); var mergedParameters = FacebookUtils.Merge(defaultParams, parameters); if (mergedParameters["next"] == null) { mergedParameters.Remove("next"); } else { mergedParameters["next"] = mergedParameters["next"].ToString(); } uriBuilder.Query = FacebookUtils.ToJsonQueryString(mergedParameters); return(uriBuilder.Uri); }
/// <summary> /// Makes an asynchronous POST request to the Facebook server. /// </summary> /// <param name="path"> /// The resource path. /// </param> /// <param name="parameters"> /// The parameters. /// </param> /// <param name="callback"> /// The callback. /// </param> public void PostAsync(string path, object parameters, FacebookAsyncCallback callback) { if (string.IsNullOrEmpty(path) && parameters == null) { throw new ArgumentException("At least path or parameters must be defined."); } PostAsync(path, FacebookUtils.ToDictionary(parameters), callback, null); }
/// <summary> /// Makes an asynchronous POST request to the Facebook server. /// </summary> /// <param name="parameters"> /// The parameters. /// </param> /// <param name="callback"> /// The callback. /// </param> public void PostAsync(object parameters, FacebookAsyncCallback callback) { if (parameters == null) { throw new ArgumentNullException("parameters"); } PostAsync(FacebookUtils.ToDictionary(parameters), callback, null); }
/// <summary> /// Makes a POST request to the Facebook server. /// </summary> /// <param name="parameters"> /// The parameters. /// </param> /// <exception cref="Facebook.FacebookApiException" /> /// <returns> /// The json result. /// </returns> public object Post(object parameters) { Contract.Requires(parameters != null); if (parameters is IDictionary <string, object> ) { return(Post((IDictionary <string, object>)parameters)); } return(Post(FacebookUtils.ToDictionary(parameters))); }
/// <summary> /// Makes a POST request to the Facebook server. /// </summary> /// <param name="path"> /// The resource path. /// </param> /// <param name="parameters"> /// The parameters. /// </param> /// <exception cref="Facebook.FacebookApiException" /> /// <returns> /// The json result. /// </returns> public object Post(string path, object parameters) { Contract.Requires(!(String.IsNullOrEmpty(path) && parameters == null)); if (parameters is IDictionary <string, object> ) { return(Post(path, (IDictionary <string, object>)parameters)); } return(Post(path, FacebookUtils.ToDictionary(parameters))); }
public static FacebookApiException TryGetRestException(IDictionary <string, Uri> domainMaps, Uri requestUri, object json) { FacebookApiException error = null; // HACK: We have to do this because the REST Api doesn't return // the correct status codes when an error has occurred. if (FacebookUtils.IsUsingRestApi(domainMaps, requestUri)) { // If we are using the REST API we need to check for an exception error = GetRestException(json); } return(error); }
/// <summary> /// Internal method for parsing the Facebook oauth ur.. /// </summary> /// <param name="uri"> /// The url to parse. /// </param> /// <param name="throws"> /// Whether to throw the exception or not incase an error occurs. /// </param> /// <returns> /// The <see cref="FacebookOAuthResult"/>. /// </returns> /// <exception cref="InvalidOperationException"> /// Throws if cannot parse the specified url. /// </exception> private static FacebookOAuthResult Parse(Uri uri, bool throws) { IDictionary <string, object> parameters = null; try { bool found = false; if (!string.IsNullOrEmpty(uri.Fragment)) { // #access_token and expires_in are in fragment var fragment = uri.Fragment.Substring(1); parameters = FacebookUtils.ParseUrlQueryString(fragment); if (parameters.ContainsKey("access_token")) { found = true; } } // code, state, error_reason, error and error_description are in query // ?error_reason=user_denied&error=access_denied&error_description=The+user+denied+your+request. var queryPart = FacebookUtils.ParseUrlQueryString(uri.Query); if (queryPart.ContainsKey("code") || (queryPart.ContainsKey("error") && queryPart.ContainsKey("error_description"))) { found = true; } if (found) { parameters = FacebookUtils.Merge(parameters, queryPart); return(new FacebookOAuthResult(parameters)); } } catch { if (throws) { throw; } return(null); } if (throws) { throw new InvalidOperationException("Could not parse authentication url."); } return(null); }
/// <summary> /// Builds the request post data and request uri based on the given parameters. /// </summary> /// <param name="uri"> /// The request uri. /// </param> /// <param name="parameters"> /// The request parameters. /// </param> /// <param name="httpMethod"> /// The http method. /// </param> /// <param name="requestUrl"> /// The outputted request uri. /// </param> /// <param name="contentType"> /// The request content type. /// </param> /// <returns> /// The request post data. /// </returns> internal static byte[] BuildRequestData(Uri uri, IDictionary <string, object> parameters, HttpMethod httpMethod, out Uri requestUrl, out string contentType) { Contract.Requires(uri != null); Contract.Requires(parameters != null); var requestUrlBuilder = new UriBuilder(uri); // Set the default content type contentType = "application/x-www-form-urlencoded"; byte[] postData = null; string queryString = string.Empty; if (httpMethod == HttpMethod.Get) { queryString = FacebookUtils.ToJsonQueryString(parameters); } else { if (parameters.ContainsKey("access_token")) { queryString = string.Concat("access_token=", parameters["access_token"]); parameters.Remove("access_token"); } else if (parameters.ContainsKey("oauth_token")) { queryString = string.Concat("oauth_token=", parameters["oauth_token"]); parameters.Remove("oauth_token"); } var containsMediaObject = parameters.Where(p => p.Value is FacebookMediaObject).Count() > 0; if (containsMediaObject) { string boundary = DateTime.Now.Ticks.ToString("x", CultureInfo.InvariantCulture); postData = BuildMediaObjectPostData(parameters, boundary); contentType = String.Concat("multipart/form-data; boundary=", boundary); } else { postData = Encoding.UTF8.GetBytes(FacebookUtils.ToJsonQueryString(parameters)); } } requestUrlBuilder.Query = queryString; requestUrl = requestUrlBuilder.Uri; return(postData); }
/// <summary> /// Checks for rest exception. /// </summary> /// <param name="domainMaps"> /// The domain maps. /// </param> /// <param name="requestUri"> /// The request uri. /// </param> /// <param name="json"> /// The json string. /// </param> /// <returns> /// Returns <see cref="FacebookApiException"/> if it is a rest exception otherwise null. /// </returns> internal static FacebookApiException CheckForRestException(IDictionary <string, Uri> domainMaps, Uri requestUri, string json) { Contract.Requires(requestUri != null); FacebookApiException error = null; // HACK: We have to do this because the REST Api doesn't return // the correct status codes when an error has occurred. if (FacebookUtils.IsUsingRestApi(domainMaps, requestUri)) { // If we are using the REST API we need to check for an exception var resultObject = JsonSerializer.Current.DeserializeObject(json); error = GetRestException(resultObject); } return(error); }
public FacebookSession(IDictionary <string, object> dictionary, IFacebookApplication settings) { if (dictionary == null) { throw new ArgumentNullException("dictionary"); } if (settings == null) { throw new ArgumentNullException("settings"); } _settings = settings; var data = dictionary is JsonObject ? dictionary : FacebookUtils.ToDictionary(dictionary); AccessToken = data.ContainsKey("access_token") ? (string)data["access_token"] : null; if (!data.ContainsKey("uid") && !string.IsNullOrEmpty(AccessToken)) { data.Add("uid", ParseUserIdFromAccessToken(AccessToken)); } string sUserId = data.ContainsKey("uid") && data["uid"] != null ? data["uid"].ToString() : null; long userId = 0; long.TryParse(sUserId, out userId); UserId = userId; Secret = data.ContainsKey("secret") ? (string)data["secret"] : null; SessionKey = data.ContainsKey("session_key") ? (string)data["session_key"] : null; if (data.ContainsKey("expires")) { Expires = data["expires"].ToString() == "0" ? DateTime.MaxValue : DateTimeConvertor.FromUnixTime(Convert.ToDouble(data["expires"])); } else { Expires = DateTime.MinValue; } Signature = data.ContainsKey("sig") ? (string)data["sig"] : null; BaseDomain = data.ContainsKey("base_domain") ? (string)data["base_domain"] : null; _data = data; }
private static string MakeRequest(HttpMethod httpMethod, Uri requestUrl, byte[] postData, string contentType) { var request = (HttpWebRequest)WebRequest.Create(requestUrl); request.Method = FacebookUtils.ConvertToString(httpMethod); // Set the http method GET, POST, etc. if (postData != null) { request.ContentLength = postData.Length; request.ContentType = contentType; using (Stream dataStream = request.GetRequestStream()) { dataStream.Write(postData, 0, postData.Length); } } var responseData = string.Empty; Exception exception = null; try { var response = (HttpWebResponse)request.GetResponse(); using (var streamReader = new StreamReader(response.GetResponseStream())) { responseData = streamReader.ReadToEnd(); } response.Close(); } catch (WebException ex) { exception = (Exception)ExceptionFactory.GetGraphException(ex) ?? ex; } finally { if (exception != null) { throw exception; } } return(responseData); }
/// <summary> /// Gets the login url. /// </summary> /// <param name="parameters"> /// The parameters. /// </param> /// <returns> /// Returns the Facebook login url. /// </returns> /// <remarks> /// http://developers.facebook.com/docs/reference/dialogs/oauth /// Parameters that can be used: /// client_id : Your application's identifier. This is called client_id instead of app_id for this particular method to be compliant with the OAuth 2.0 specification. Required, but automatically specified by most SDKs. /// redirect_uri : The URL to redirect to after the user clicks a button on the dialog. Required, but automatically specified by most SDKs. /// scope : Optional. A comma-delimited list of permissions. /// state : Optional. An opaque string used to maintain application state between the request and callback. When Facebook redirects the user back to your redirect_uri, this value will be included unchanged in the response. /// response_type : Optional, default is token. The requested response: an access token (token), an authorization code (code), or both (code token). /// display : The display mode in which to render the dialog. The default is page on the www subdomain and wap on the m subdomain. This is automatically specified by most SDKs. (For WP7 builds it is set to touch.) /// </remarks> public virtual Uri GetLoginUrl(IDictionary <string, object> parameters) { var defaultParameters = new Dictionary <string, object>(); defaultParameters["client_id"] = AppId; defaultParameters["redirect_uri"] = RedirectUri ?? new Uri("http://www.facebook.com/connect/login_success.html"); #if WINDOWS_PHONE defaultParameters["display"] = "touch"; #endif var mergedParameters = FacebookUtils.Merge(defaultParameters, parameters); // check if client_id and redirect_uri is not null or empty. if (mergedParameters["client_id"] == null || string.IsNullOrEmpty(mergedParameters["client_id"].ToString())) { throw new ArgumentException("client_id required."); } if (mergedParameters["redirect_uri"] == null || string.IsNullOrEmpty(mergedParameters["redirect_uri"].ToString())) { throw new ArgumentException("redirect_uri required."); } // seems like if we don't do this and rather pass the original uri object, // it seems to have http://localhost:80/csharpsamples instead of // http://localhost/csharpsamples // notice the port number, that shouldn't be there. // this seems to happen for iis hosted apps. mergedParameters["redirect_uri"] = mergedParameters["redirect_uri"].ToString(); var url = "http://www.facebook.com/dialog/oauth/?" + FacebookUtils.ToJsonQueryString(mergedParameters); // In order to be compliant with the OAuth spec Facebook have made changes to their auth APIs. // As part of this update, they will be deprecating 'code_and_token' and need developers // to use 'code%20token'. Everything is identical, just replace '_and_' with encoded // <space> '%20'. // url = url.Replace("response_type=code+token", "response_type=code%20token"); return(new Uri(url)); }
/// <summary> /// Makes a POST request to the Facebook server. /// </summary> /// <param name="parameters"> /// The parameters. /// </param> /// <exception cref="Facebook.FacebookApiException" /> /// <returns> /// The json result. /// </returns> public object Post(object parameters) { Contract.Requires(parameters != null); return(Post(FacebookUtils.ToDictionary(parameters))); }
/// <summary> /// Makes an asynchronous POST request to the Facebook server. /// </summary> /// <param name="path"> /// The resource path. /// </param> /// <param name="parameters"> /// The parameters. /// </param> /// <param name="userToken"> /// The user token. /// </param> public void PostAsync(string path, object parameters, object userToken) { Contract.Requires(!(String.IsNullOrEmpty(path) && parameters == null)); PostAsync(path, FacebookUtils.ToDictionary(parameters), userToken); }
/// <summary> /// Makes an asynchronous POST request to the Facebook server. /// </summary> /// <param name="parameters"> /// The parameters. /// </param> /// <param name="callback"> /// The callback. /// </param> public void PostAsync(object parameters, FacebookAsyncCallback callback) { Contract.Requires(parameters != null); PostAsync(FacebookUtils.ToDictionary(parameters), callback, null); }
/// <summary> /// Makes an asynchronous POST request to the Facebook server. /// </summary> /// <param name="path"> /// The resource path. /// </param> /// <param name="parameters"> /// The parameters. /// </param> /// <param name="callback"> /// The callback. /// </param> public void PostAsync(string path, object parameters, FacebookAsyncCallback callback) { Contract.Requires(!(String.IsNullOrEmpty(path) && parameters == null)); PostAsync(path, FacebookUtils.ToDictionary(parameters), callback, null); }
/// <summary> /// Makes an asynchronous POST request to the Facebook server. /// </summary> /// <param name="parameters"> /// The parameters. /// </param> public void PostAsync(object parameters) { Contract.Requires(parameters != null); PostAsync(FacebookUtils.ToDictionary(parameters)); }
/// <summary> /// Converts the facebook batch to POST parameters. /// </summary> /// <param name="batchParameter"> /// The batch parameter. /// </param> /// <returns> /// The post parameters. /// </returns> protected IDictionary <string, object> ToParameters(FacebookBatchParameter batchParameter) { Contract.Requires(batchParameter != null); Contract.Ensures(Contract.Result <IDictionary <string, object> >() != null); IDictionary <string, object> returnResult = null; var defaultParameters = new Dictionary <string, object>(); defaultParameters["method"] = FacebookUtils.ConvertToString(batchParameter.HttpMethod); IDictionary <string, object> parameters = null; if (batchParameter.Parameters == null) { parameters = new Dictionary <string, object>(); } else { if (batchParameter.Parameters is IDictionary <string, object> ) { parameters = (IDictionary <string, object>)batchParameter.Parameters; } else { parameters = FacebookUtils.ToDictionary(batchParameter.Parameters); } } var path = FacebookUtils.ParseQueryParametersToDictionary(batchParameter.Path, parameters); string queryString = string.Empty; if (batchParameter.HttpMethod == HttpMethod.Get) { queryString = FacebookUtils.ToJsonQueryString(parameters); } else { defaultParameters["body"] = FacebookUtils.ToJsonQueryString(parameters); } var relativeUrl = new StringBuilder(path); if (!string.IsNullOrEmpty(queryString)) { relativeUrl.AppendFormat("?{0}", queryString); } defaultParameters["relative_url"] = relativeUrl.ToString(); var data = batchParameter.Data; if (data == null) { returnResult = defaultParameters; } else { if (!(data is IDictionary <string, object>)) { data = FacebookUtils.ToDictionary(batchParameter.Data); } returnResult = FacebookUtils.Merge(defaultParameters, (IDictionary <string, object>)data); } return(returnResult); }
/// <summary> /// Parse the signed request string. /// </summary> /// <param name="secret"> /// The secret. /// </param> /// <param name="signedRequestValue"> /// The signed request value. /// </param> /// <param name="maxAge"> /// The max age. /// </param> /// <param name="currentTime"> /// The current time. /// </param> /// <param name="throws"> /// The throws. /// </param> /// <returns> /// The FacebookSignedRequest. /// </returns> internal static IDictionary <string, object> TryParse(string secret, string signedRequestValue, int maxAge, double currentTime, bool throws) { Contract.Requires(!String.IsNullOrEmpty(signedRequestValue)); Contract.Requires(!String.IsNullOrEmpty(secret)); Contract.Requires(maxAge >= 0); Contract.Requires(currentTime >= 0); Contract.Requires(signedRequestValue.Contains("."), Properties.Resources.InvalidSignedRequest); try { // NOTE: currentTime added to parameters to make it unit testable. string[] split = signedRequestValue.Split('.'); if (split.Length != 2) { // need to have exactly 2 parts throw new InvalidOperationException(Properties.Resources.InvalidSignedRequest); } string encodedSignature = split[0]; string encodedEnvelope = split[1]; if (string.IsNullOrEmpty(encodedSignature)) { throw new InvalidOperationException(Properties.Resources.InvalidSignedRequest); } if (string.IsNullOrEmpty(encodedEnvelope)) { throw new InvalidOperationException(Properties.Resources.InvalidSignedRequest); } var envelope = (IDictionary <string, object>)JsonSerializer.Current.DeserializeObject(Encoding.UTF8.GetString(FacebookUtils.Base64UrlDecode(encodedEnvelope))); string algorithm = (string)envelope["algorithm"]; if (!algorithm.Equals("AES-256-CBC HMAC-SHA256") && !algorithm.Equals("HMAC-SHA256")) { // TODO: test throw new InvalidOperationException("Invalid signed request. (Unsupported algorithm)"); } byte[] key = Encoding.UTF8.GetBytes(secret); byte[] digest = FacebookUtils.ComputeHmacSha256Hash(Encoding.UTF8.GetBytes(encodedEnvelope), key); if (!digest.SequenceEqual(FacebookUtils.Base64UrlDecode(encodedSignature))) { throw new InvalidOperationException(Facebook.Web.Properties.Resources.InvalidSignedRequestSignature); } IDictionary <string, object> result; if (algorithm.Equals("HMAC-SHA256")) { // for requests that are signed, but not encrypted, we're done result = envelope; } else { result = new JsonObject(); result["algorithm"] = algorithm; long issuedAt = (long)envelope["issued_at"]; if (issuedAt < currentTime) { throw new InvalidOperationException(Web.Properties.Resources.OldSignedRequest); } result["issued_at"] = issuedAt; // otherwise, decrypt the payload byte[] iv = FacebookUtils.Base64UrlDecode((string)envelope["iv"]); byte[] rawCipherText = FacebookUtils.Base64UrlDecode((string)envelope["payload"]); var plainText = FacebookUtils.DecryptAes256CBCNoPadding(rawCipherText, key, iv); var payload = (IDictionary <string, object>)JsonSerializer.Current.DeserializeObject(plainText); result["payload"] = payload; } return(result); } catch { if (throws) { throw; } return(null); } }