I am attempting to upgrade to newest APNS connectivity functionality but when I submit a request I get a http 400 bad request result with no reason. What am I missing / doing wrong?
Method for sending below:
public async void SendAsync(string deviceToken, string p8privateKey, string p8privateKeyId, string teamId)
{
var path = $"/3/device/{deviceToken}";
var obj = new
{
aps = new
{
alert = "test00001"
}
};
var json = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
var request = new HttpRequestMessage(HttpMethod.Post, new Uri("https://api.development.push.apple.com:443" + path))
{
Version = new Version(2, 0)
};
string jwToken = CreateJwtToken(p8privateKey, p8privateKeyId, teamId);
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", jwToken);
request.Headers.TryAddWithoutValidation(":method", "POST");
request.Headers.TryAddWithoutValidation(":path", path);
request.Headers.Add("apns-topic", "com.the-app");
request.Content = new StringContent(json);
string sReq = JsonConvert.SerializeObject(request);
string jsonContent = request.Content.ReadAsStringAsync().Result;
WinHttpHandler handler = new WinHttpHandler();
HttpClient http = new HttpClient(handler);
var t = await http.SendAsync(request);
}
Here are the other used methods:
private static string CreateJwtToken(string p8privateKeyId, string p8privateKey, string teamId)
{
var header = JsonHelper.Serialize(new { alg = "ES256", kid = p8privateKeyId });
var payload = JsonHelper.Serialize(new { iss = teamId, iat = ToEpoch(DateTime.UtcNow) });
var key = CngKey.Import(Convert.FromBase64String(p8privateKey), CngKeyBlobFormat.Pkcs8PrivateBlob);
using (var dsa = new ECDsaCng(key))
{
dsa.HashAlgorithm = CngAlgorithm.Sha256;
var headerBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(header));
var payloadBasae64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(payload));
var unsignedJwtData = $"{headerBase64}.{payloadBasae64}";
var signature = dsa.SignData(Encoding.UTF8.GetBytes(unsignedJwtData));
return $"{unsignedJwtData}.{Convert.ToBase64String(signature)}";
}
}
private static int ToEpoch(DateTime time)
{
var span = DateTime.UtcNow - new DateTime(1970, 1, 1);
return Convert.ToInt32(span.TotalSeconds);
}
I have limited to the payload to what I think is the bear minimum requirements. I am new to jwt/http2 and APNS.
I could very well be missing something simple.
Related
Setting up DeviceCheck on iOS is super easy, but implementing the server-side using C# is difficult as there are hardly any examples and some tricky JWT code is needed, which has to be absolutely perfect for it to work. Does anyone have a solution?
Here is a complete solution. I stripped out the first and last line from the p8 file e.g. "key begins here" (or whatever) and also took out all the new lines so that the key was just one long line
static void Main(string[] args)
{
string deviceToken = ""; //you get this from the device
string transcationId = Guid.NewGuid().ToString();
var payload = new Dictionary<string, object>() {
{ "device_token", deviceToken },
{ "timestamp", DateTimeOffset.UtcNow.ToUnixTimeSeconds() * 1000 },
{ "transaction_id", transcationId }
};
var token = GetProviderToken();
var payloadJson = JsonConvert.SerializeObject(payload);
using (var httpClient = new HttpClient())
{
using (var request = new HttpRequestMessage(new HttpMethod("POST"), "https://api.development.devicecheck.apple.com/v1/query_two_bits"))
{
request.Headers.TryAddWithoutValidation("Authorization", $"Bearer {token}");
request.Content = new StringContent(payloadJson);
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/x-www-form-urlencoded");
var response = httpClient.SendAsync(request).Result;
Console.WriteLine(response);
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
}
}
}
private static CngKey GetPrivateKey()
{
using (var reader = File.OpenText(#".\applekey.p8.txt"))
{
var ecPrivateKeyParameters = (ECPrivateKeyParameters)new PemReader(reader).ReadObject();
var x = ecPrivateKeyParameters.Parameters.G.AffineXCoord.GetEncoded();
var y = ecPrivateKeyParameters.Parameters.G.AffineYCoord.GetEncoded();
var d = ecPrivateKeyParameters.D.ToByteArrayUnsigned();
return EccKey.New(x, y, d);
}
}
private static string GetProviderToken()
{
var epochNow = (int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
var payload = new Dictionary<string, object>()
{
{"iss", "<10 DIGIT TEAM CODE FROM APPLE DEV CENTER>"},
{"iat", epochNow}
};
var extraHeaders = new Dictionary<string, object>()
{
{"kid", "<THE NAME OF THE P8 FILE, 10 DIGITS>"}
};
var privateKey = GetPrivateKey();
return JWT.Encode(payload, privateKey, JwsAlgorithm.ES256, extraHeaders);
}
I am trying to call this code
string accessToken = #".."; //valid token with right scopes
public string EventsUrl = #"https://outlook.office.com/api/v2.0/me/events";
// generate body
var postBody = JsonBody(invite);
using (var client = new HttpClient())
{
using (var request = new HttpRequestMessage(HttpMethod.Post, EventsUrl))
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var content = new StringContent(postBody, Encoding.UTF8, "application/json");
request.Content = content;
var response = await client.SendAsync(request);
return (response.IsSuccessStatusCode);
}
}
The method that create JsonBody is,
public string JsonBody(User user, Session session){
var invite = new EventInvite
{
Attendees = new Attendee[1]
};
invite.Attendees[0] = new Attendee
{
Type = "Required",
EmailAddress = new Emailaddress { Name = user.GetName(), Address = user.GetEmail() }
};
invite.Start = new Start { DateTime = session.DateTime_Start };
invite.End = new End { DateTime = session.DateTime_Start.AddMinutes(15) };
invite.Subject = session.Name;
invite.Body = new Body { ContentType = "HTML", Content = $"Some Content" };
return JsonConvert.SerializeObject(eventInvite);
}
I am getting a Bad Request as response. Is there any alternative to build an Event? I want this code to be very thin as this is accessed in non UI based application
What is the best way to create Calendar Event?
The fix involves setting up the Timezone in the Start and End
string timeZone="Singapore Standard Time";
invite.Start = new Start { DateTime = session.DateTime_Start, TimeZone = timeZone };
invite.End = new End { DateTime = session.DateTime_Start.AddMinutes(60), TimeZone = timeZone };
var postBody = JsonConvert.SerializeObject(invite, Formatting.Indented);
I have this code which I am attempting to use to communicate an API via RestSharp.
const string task = "pay";
const string command_api_token = "9ufks6FjffGplu9HbaN7uq6XXPPVQXBP";
const string merchant_email_on_voguepay = "mymail#mail.com";
Random rnd = new Random();
string refl = DateTime.Now + rnd.Next(0,9999999).ToString();
byte[] hash_target = Encoding.Default.GetBytes(command_api_token + task + merchant_email_on_voguepay + refl);
string hashD = BitConverter.ToString(new SHA512CryptoServiceProvider().ComputeHash(hash_target)).Replace("-", string.Empty).ToUpper();
var keyValues = new Dictionary<string, string>
{
{ "task", "pay"},
{ "merchant", "3333-4444"},
{ "ref",refl},
{ "hash",hashD},
{ "amount", "20"},
{ "seller", "seller#mail.com"},
{ "remarks", "payment"},
};
//serialization using Newtonsoft JSON
string json = JsonConvert.SerializeObject(keyValues);
//url encode the json
var postString = Server.UrlEncode(json);
//calling API with Restsharp
var client = new RestClient("https://voguepay.com/api/");
var request = new RestRequest(Method.POST);
request.AddParameter("json",json);
IRestResponse response = client.Execute(request);
Textbox1.Text = response.Content;
I think the arrangement of my code is not really ok, because I keep getting error message on each move I make.
If I try to post it as it is above, I get
"response":"X006","description":"Invalid hash"...
If try to get "url encode the json" involved in the "calling API with Restsharp", I get error message as
"response":"X001","description":"Invalid Merchant Id"...
I think I am not placing things right, can someone look at my work and point out what could be the issue with this code?
I am using below code for calling API may this one help u.Here i am passing one class object u replace this by Dictionary and try..
public void insertData(OCMDataClass kycinfo, string clientId, string type)
{
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(System.Configuration.ConfigurationManager.AppSettings["CBService"]);
string postBody = Newtonsoft.Json.JsonConvert.SerializeObject(kycinfo);
var jsonString = JsonConvert.SerializeObject(kycinfo);
var content = new StringContent(jsonString, System.Text.Encoding.UTF8, "application/json");
var myContent = JsonConvert.SerializeObject(kycinfo);
var buffer = System.Text.Encoding.UTF8.GetBytes(myContent);
var byteContent = new ByteArrayContent(buffer);
var result = client.PostAsync("Bfilvid/api/SvcVId/CreateKYCRepository", content).Result;
if (result.IsSuccessStatusCode)
{
string resultContent = result.Content.ReadAsStringAsync().Result;
}
else
{
string resultContent = result.Content.ReadAsStringAsync().Result;
}
}
}
catch (Exception ex)
{
}
I'm trying to use the below code but for some reason I'm getting an invalid or expired token it seemed to work once but never again.
Any ideas? (consumerKey and consumerSecret are constants generated in the class.)
public ActionResult Index()
{
string twitterAccount = System.Configuration.ConfigurationManager.AppSettings["twitterAccount"];
JsonDeserializer jsonDeserializer = new JsonDeserializer();
var model = new TwitterVM.LandingModel();
var qs = GetToken();
string oauthToken = qs["oauth_token"];
string oauthTokenSecret = qs["oauth_token_secret"];
RestClient client = new RestClient("https://api.twitter.com/1.1")
{
Authenticator = OAuth1Authenticator.ForProtectedResource(consumerKey, consumerSecret, oauthToken, oauthTokenSecret)
};
RestRequest request = new RestRequest("statuses/user_timeline", Method.GET);
request.Parameters.Add(new Parameter()
{
Name = "screen_name",
Value = twitterAccount,
Type = ParameterType.GetOrPost
});
request.Parameters.Add(new Parameter()
{
Name = "count",
Value = 10,
Type = ParameterType.GetOrPost
});
request.Parameters.Add(new Parameter()
{
Name = "include_rts",
Value = true,
Type = ParameterType.GetOrPost
});
request.Parameters.Add(new Parameter()
{
Name = "include_entities",
Value = true,
Type = ParameterType.GetOrPost
});
IRestResponse response = client.Execute(request);
model.Tweets =
jsonDeserializer.Deserialize<List<TwitterVM.Tweet>>(response);
return View(model);
}
private NameValueCollection GetToken()
{
RestClient client = new RestClient("https://api.twitter.com") { Authenticator = OAuth1Authenticator.ForRequestToken(consumerKey, consumerSecret) };
//Do the auth shit...
RestRequest request = new RestRequest("oauth/request_token", Method.POST);
IRestResponse response = client.Execute(request);
return HttpUtility.ParseQueryString(response.Content);
}
Using Twitter's OAuth2 API (https://api.twitter.com/oauth2/token)
See https://dev.twitter.com/oauth/application-only for details....
var client = await CreateHttpClient("....", "....");
//don't dispose this client and use for subsequent API calls
var screenName = "....";
var count = 10;
var include_rts = true;
var url = $"https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name={screenName}&include_rts={include_rts}&count={count}";
var json = await client.GetStringAsync(url);
public static async Task<HttpClient> CreateHttpClient(string consumerKey, string consumerSecret)
{
var bearerToken = Convert.ToBase64String(Encoding.UTF8.GetBytes(consumerKey + ":" + consumerSecret));
string url = "https://api.twitter.com/oauth2/token";
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Basic " + bearerToken);
var resp = await client.PostAsync(url, new StringContent("grant_type=client_credentials", Encoding.UTF8, "application/x-www-form-urlencoded")).ConfigureAwait(false);
resp.EnsureSuccessStatusCode();
var result = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
var jObj = new JavaScriptSerializer().Deserialize<Dictionary<string,string>>(result);
if (jObj["token_type"] != "bearer") throw new Exception("Invalid Response From Twitter/OAuth");
client.DefaultRequestHeaders.Remove("Authorization");
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Bearer " + jObj["access_token"]);
return client;
}
I try to add new product to Woocommerce using c# RestSharp, but answer from server is:
{"errors":[{"code":"woocommerce_api_authentication_error","message":"oauth_consumer_key parameter mangler"}]}
For adding product i use next code:
public string AddProduct()
{
Method method = Method.POST;
string result = "";
string endpoint = "products";
var client = new RestClient(ApiUrl);
var parameters = new Dictionary<string, string>();
var request = createRequestWithParams(parameters, endpoint, method);
request.RequestFormat = DataFormat.Json;
request.AddJsonBody(new DTO.WCProduct { title = "eeee2", type = "simple", regular_price = "777", description = "Descr" });
AddOAuthparams(ref parameters, method.ToString(), endpoint);
result = client.Execute(request).Content;
return result;
}
Where Method createRequestWithParams is :
private RestRequest createRequestWithParams(Dictionary<string, string> parameters, string res, Method methos)
{
var req = new RestRequest(res, methos);
foreach (var item in parameters)
{
req.AddParameter(item.Key, item.Value);
}
return req;
}`
Where Method AddOAuthparams is :
void AddOAuthparams(ref Dictionary<string, string> parameters, string method, string endpoint)
{
parameters["oauth_consumer_key"] = this.ConsumerKey;
parameters["oauth_timestamp"] =
DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds.ToString();
parameters["oauth_timestamp"] = parameters["oauth_timestamp"].Substring(0, parameters["oauth_timestamp"].IndexOf(",")); //todo fix for . or ,
parameters["oauth_nonce"] = Hash(parameters["oauth_timestamp"]);
parameters["oauth_signature_method"] = "HMAC-SHA256";
parameters["oauth_signature"] = GenerateSignature(parameters, method, endpoint);
}
public string GenerateSignature(Dictionary<string, string> parameters, string method, string endpoint)
{
var baserequesturi = Regex.Replace(HttpUtility.UrlEncode(this.ApiUrl + endpoint), "(%[0-9a-f][0-9a-f])", c => c.Value.ToUpper());
var normalized = NormalizeParameters(parameters);
var signingstring = string.Format("{0}&{1}&{2}", method, baserequesturi,
string.Join("%26", normalized.OrderBy(x => x.Key).ToList().ConvertAll(x => x.Key + "%3D" + x.Value)));
var signature =
Convert.ToBase64String(HashHMAC(Encoding.UTF8.GetBytes(this.ConsumerSecret),
Encoding.UTF8.GetBytes(signingstring)));
Console.WriteLine(signature);
return signature;
}
private Dictionary<string, string> NormalizeParameters(Dictionary<string, string> parameters)
{
var result = new Dictionary<string, string>();
foreach (var pair in parameters)
{
var key = HttpUtility.UrlEncode(HttpUtility.UrlDecode(pair.Key));
key = Regex.Replace(key, "(%[0-9a-f][0-9a-f])", c => c.Value.ToUpper()).Replace("%", "%25");
var value = HttpUtility.UrlEncode(HttpUtility.UrlDecode(pair.Value));
value = Regex.Replace(value, "(%[0-9a-f][0-9a-f])", c => c.Value.ToUpper()).Replace("%", "%25");
result.Add(key, value);
}
return result;
}
** But if i try to Get info about products or Delete some product this functions worked fine, **
This code i find on Github https://github.com/kloon/WooCommerce-REST-API-Client-Library
I think, that my function for signature is worked bad, but i don't understand what should be fixed.
Maybe this is an old question. But it will be worth for someone came from google searching any hint.
edit: From the error message, i think you forgot to include the oauth_consumer_key as the message. Make sure your request include the oauth_consumer_key, by check the RestRequest query parameter.
Btw instead using the implementation from kloon, you can use the Woocommerce C# Library from my repository
Try the below solution its very easy to integrate and require very less line of codes.It Works for me
static void Main(string[] args)
{
string requestURL = #"http://www.example.co.uk/test/wp-json/wc/v1/products";
UriBuilder tokenRequestBuilder = new UriBuilder(requestURL);
var query = HttpUtility.ParseQueryString(tokenRequestBuilder.Query);
query["oauth_consumer_key"] = "consumer_key";
query["oauth_nonce"] = Guid.NewGuid().ToString("N");
query["oauth_signature_method"] = "HMAC-SHA1";
query["oauth_timestamp"] = (Math.Truncate((DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds)).ToString();
string signature = string.Format("{0}&{1}&{2}", "POST", Uri.EscapeDataString(requestURL), Uri.EscapeDataString(query.ToString()));
string oauth_Signature = "";
using (HMACSHA1 hmac = new HMACSHA1(Encoding.ASCII.GetBytes("consumer_Secret&")))
{
byte[] hashPayLoad = hmac.ComputeHash(Encoding.ASCII.GetBytes(signature));
oauth_Signature = Convert.ToBase64String(hashPayLoad);
}
query["oauth_signature"] = oauth_Signature;
tokenRequestBuilder.Query = query.ToString();
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(tokenRequestBuilder.ToString());
request.ContentType = "application/json; charset=utf-8";
// request.Method = "GET";
request.Method = "POST";
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
string json = File.ReadAllText(#"D:\JsonFile.txt");//File Path for Json String
streamWriter.Write(json);
streamWriter.Flush();
}
var httpResponse = (HttpWebResponse)request.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
}
}