Facebook需要一个公共网址才能发布POST,以便为用户取消对您应用的授权。
我在防火墙后面并且不希望在我的生产环境中测试这个,因此有没有人设法在c#(或其他)集成测试中模仿这个POST?
这将涉及构建一个带有signed_request字段的表单并加密到base64并将其发布到我的本地Web应用程序来处理......
答案 0 :(得分:0)
以下是我在PHP中的表现:
<?php
function generateSignedRequest($data, $secret) {
$payload = base64UrlEncode(json_encode($data));
$signature = base64UrlEncode(hash_hmac('sha256', $payload, $secret, $raw = true));
return "$signature.$payload";
}
function base64UrlEncode($input) {
$input = rtrim(base64_encode($input), '=');
return strtr($input, '+/', '-_');
}
$app_secret = "YOUR_APP_SECRET_HERE";
$data = array(
'algorithm' => 'HMAC-SHA256',
'issued_at' => 1332162396,
'user' => array(
'country' => 'bg',
'locale' => 'en_US'
),
'user_id' => '100003387159594'
);
$signed_request = generateSignedRequest($data, $app_secret);
?>
<form action="deauthorize_callback.php" method="post">
<input type="text" name="signed_request" value="<?php echo $signed_request?>" size="255" />
<input type="submit" value="Send Request" />
</form>
如果您在本地环境中使用PHP,则可以尝试将表单提交到后端,看看它是否有效。您只需要在数据数组中替换您的应用程序密钥和用户ID。
希望这有帮助,到目前为止它对我有用。
答案 1 :(得分:0)
我设法找到了类似的东西来构建我已经转换的Ruby中的有效负载,这里是集成测试:
此类构建将作为request.form [“signed_request”]
POST的字符串public class SignedRequest
{
public static string SignedRequestFor(string applicationSecret, string json)
{
// encode payload
var payload = ToUrlBase64String(json);
// create sig
var Hmac = SignWithHmac(UTF8Encoding.UTF8.GetBytes(payload), UTF8Encoding.UTF8.GetBytes(applicationSecret));
var HmacBase64 = ToUrlBase64String(Hmac);
// concat and return
return HmacBase64.Concatenate(".", payload);
}
private static byte[] SignWithHmac(byte[] dataToSign, byte[] keyBody)
{
using (var hmacAlgorithm = new HMACSHA256(keyBody))
{
hmacAlgorithm.ComputeHash(dataToSign);
return hmacAlgorithm.Hash;
}
}
private static string ToUrlBase64String(string input)
{
byte[] encodedBytes = Encoding.UTF8.GetBytes(input);
return ToUrlBase64String(encodedBytes);
}
private static string ToUrlBase64String(byte[] input)
{
return Convert.ToBase64String(input).Replace("=", String.Empty)
.Replace('+', '-')
.Replace('/', '_');
}
}
测试使用的小值对象:
public class RequestEnvelope
{
public long user_id { get; set; }
public string algorithm = "HMAC-SHA256";
}
测试:
[Test]
public void CanDeauthorizeUser()
{
var appSecret = ConfigurationManager.AppSettings["NewsletterSubscriptionFacebookAppSecret"];
var envelope = new RequestEnvelope { user_id = 123456 };
var signedRequest = SignedRequest.SignedRequestFor(appSecret, JsonConvert.SerializeObject(envelope));
var postData = "signed_request=" + signedRequest;
byte[] postBytes = Encoding.UTF8.GetBytes(postData);
var request = WebRequest.Create(ConfigurationManager.AppSettings["AppWebUrl"] + "/SubscriptionHandler.ashx?a=deactivate");
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = postBytes.Length;
using (var newStream = request.GetRequestStream())
{
// Send the data.
newStream.Write(postBytes, 0, postBytes.Length);
newStream.Close();
}
// Get the response.
using (var response = request.GetResponse())
{
var status = ((HttpWebResponse) response).StatusDescription;
// Get the stream containing all content returned by the requested server.
using (var dataStream = response.GetResponseStream())
{
// Open the stream using a StreamReader for easy access.
using (var reader = new StreamReader(dataStream))
{
// Read the content fully up to the end.
string responseFromServer = reader.ReadToEnd();
Console.WriteLine(responseFromServer);
Console.WriteLine("status: " + status);
}
}
}
}
我发布到处理程序的代码段:
string signedRequestValue = HttpContext.Current.Request.Form["signed_request"];
var items = SignedRequestParser.Parse(ConfigurationManager.AppSettings["NewsletterSubscriptionFacebookAppSecret"], signedRequestValue);
用于解析该代码的代码(从codeplex facebook API中获取/修改):
public class SignedRequestParser
{
/// <summary>
/// Parses the signed request string.
/// </summary>
/// <param name="appSecret">The unique app hash known the provider and the developer</param>
/// <param name="signedRequestValue">The encoded signed request value.</param>
/// <returns>The valid signed request.</returns>
public static Dictionary<string, string> Parse(string appSecret, string signedRequestValue)
{
Check.Require(!String.IsNullOrEmpty(signedRequestValue));
Check.Require(signedRequestValue.Contains("."), "Invalid Signed Request");
string[] parts = signedRequestValue.Split('.');
var encodedValue = parts[0];
if (String.IsNullOrEmpty(encodedValue))
{
throw new InvalidOperationException("Invalid Signed Request");
}
var sig = Base64UrlDecode(encodedValue);
var payload = parts[1];
using (var cryto = new System.Security.Cryptography.HMACSHA256(Encoding.UTF8.GetBytes(appSecret)))
{
var hash = Convert.ToBase64String(cryto.ComputeHash(Encoding.UTF8.GetBytes(payload)));
var hashDecoded = Base64UrlDecode(hash);
if (hashDecoded != sig)
{
throw new InvalidOperationException("Invalid Signed Request.");
}
}
var payloadJson = Encoding.UTF8.GetString(Convert.FromBase64String(Base64UrlDecode(payload)));
var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(payloadJson);
return data;
}
/// <summary>
/// Converts the base 64 url encoded string to standard base 64 encoding.
/// </summary>
/// <param name="encodedValue">The encoded value.</param>
/// <returns>The base 64 string.</returns>
private static string Base64UrlDecode(string encodedValue)
{
Check.Require(!String.IsNullOrEmpty(encodedValue));
encodedValue = encodedValue.Replace('+', '-').Replace('/', '_').Trim();
int pad = encodedValue.Length % 4;
if (pad > 0)
{
pad = 4 - pad;
}
encodedValue = encodedValue.PadRight(encodedValue.Length + pad, '=');
return encodedValue;
}
}