Verify app invite server-side - c#

When our mobile app user sends app-invite to fb user and he accepts it, the server should give a reward to the first one. So I need a way to verify whether the invite was sent.
var fb = new FacebookClient(APP_ID + "|" + SECRET_ID);
fb.AppId = APP_ID;
fb.AppSecret = SECRET_ID;
dynamic result = fb.Get(???);
I searched on GraphAPI docs and it seems that I need to retrieve users apprequests. How to do that from the server side and where to look at to perform such verification?
UPDATE
Ok, now I know that it's allowed to reward only for accepted invites. I can record who invites who in the db and give a reward only when a new invited user joins. But I still need a way to verify that these invites were actually sent.
UPDATE2
As the documentation states apprequests call from application returns all the requests sent from this application. So I think it would be enough for me to just check that there are any requests from this app:
dynamic result = fb.Get("/" + facebookId + "/apprequests");
IEnumerable data = result.data;
return data.Cast<object>().Count() != 0;
But I can't check it now. Can anyone confirm that if a user sends invite to app to another user this invite will be seen through apprequests from the application access token?

my code for this:
public static FacebookRequestData GetInviteHash()
{
string requestId = Request["request_ids"];
var accessToken = GetAccessToken(ConfigurationManager.AppSettings["FacebookAppId"], ConfigurationManager.AppSettings["FacebookSecret"]);
string response;
using (var webClient = new WebClient())
{
response = webClient.DownloadString(string.Format("https://graph.facebook.com/{0}?{1}", requestId, accessToken));
}
var javaScriptSerializer = new JavaScriptSerializer();
return javaScriptSerializer.Deserialize<FacebookRequestData>(javaScriptSerializer.Deserialize<FacebookRequestInfo>(response).data);
}
private static string GetAccessToken(string appId, string password)
{
using (var webClient = new WebClient())
{
return webClient.DownloadString(string.Format("https://graph.facebook.com/oauth/access_token?client_id={0}&client_secret={1}&grant_type=client_credentials", appId, password));
}
}
private class FacebookRequestInfo
{
public string data { get; set; }
}
FacebookRequestData - my custom class with structure of fields that I posted to fb earlier

Done it:
public static bool CheckInvite(string fromId, string toId)
{
var fb = new FacebookClient(APP_ID + "|" + SECRET_ID);
fb.AppId = APP_ID;
fb.AppSecret = SECRET_ID;
dynamic result = fb.Get(string.Format("/{0}/apprequests", toId));
foreach (var el in result.data)
if ((string)el.from.id == fromId)
{
DateTime dateTime = DateTime.Parse((string)el.created_time, CultureInfo.InvariantCulture);
if ((DateTime.Now - dateTime).TotalMinutes < 15)
{
return true;
}
}
return false;
}

Related

Need help to find where does API takes data from in ASP.NET MVC

I am a begginer and i work in a MVC project which I cant understand it well yet.
I can't understand where does the API takes data from when I try to connect in Login Screen.
It doesn't use Entity Framework and there isn't a json with the data.
When I enter Id and Pass it calls an API (GetAPIResponse) which somehow finds that is correct.
Need help to understand the code and the logic behind it.
LoginBL class contains:
public bool IsAuthenticated(LoginEntity user)
{
string url = string.Empty;
string callType = string.Empty;
string server = string.Empty;
try
{
// get URL, Call type, Server from config file
url = ConfigurationManager.AppSettings["login_url"].ToString();
callType = ConfigurationManager.AppSettings["calltype"].ToString();
server = ConfigurationManager.AppSettings["server"].ToString();
// Encrypt password
string password = Scrambler.GenerateMD5Hash(user.Password);
// Prepare content for the POST request
string content = #"calltype=" + callType + "&server=" + server + "&user=" + user.UserName + "&pass=" + password + "";
Debug.WriteLine("Callcenter login url: " + content);
HttpResponseMessage json_list = ApiCallBL.GetAPIResponse(url, content);
LoginResponseEntity obj = new LoginResponseEntity();
obj = JsonConvert.DeserializeObject<LoginResponseEntity>(json_list.Content.ReadAsStringAsync().Result);
Debug.WriteLine(callType + " Response: " + json_list.Content.ReadAsStringAsync().Result);
//if API resultCode return 0 then user details and token save in session for further use
if (obj.ResultCode == 0)
{
int restrict = obj.UserInfo.RestrictCallType.HasValue ?
obj.UserInfo.RestrictCallType.Value : 0;
HttpContext.Current.Session["user_id"] = obj.UserInfo.usr_id;
HttpContext.Current.Session["user_name"] = obj.UserInfo.usr_username;
HttpContext.Current.Session["user_group_id"] = obj.UserInfo.UserGroupID;
HttpContext.Current.Session["groupid"] = obj.UserInfo.groupid;
HttpContext.Current.Session["token"] = obj.Token;
HttpContext.Current.Session["web_server_url"] = obj.ServerInfo.web_server_url;
HttpContext.Current.Session["centerX"] = obj.ServerInfo.DefaultGeoX;
HttpContext.Current.Session["centerY"] = obj.ServerInfo.DefaultGeoY;
HttpContext.Current.Session["dateFormat"] = obj.ServerInfo.dateFormat;
HttpContext.Current.Session["currency"] = obj.ServerInfo.currency;
HttpContext.Current.Session["customer_img"] = obj.ServerInfo.customer_img;
HttpContext.Current.Session["groups"] = obj.groups;
HttpContext.Current.Session["restrict_call_type"] = restrict ;
Debug.WriteLine("obj.UserInfo.UserGroupID " + obj.UserInfo.UserGroupID);
Debug.WriteLine("obj.UserInfo.groups " + obj.groups);
//HttpContext.Current.Session["defaultLanguage"] = obj.ServerInfo.defaultLanguage;
HttpCookie cookie = new HttpCookie("Login");
// if remember me checked then user name and password stored in cookie else cookes is expired
if (user.RememberMe)
{
cookie.Values.Add("user_name", obj.UserInfo.usr_username);
cookie.Values.Add("pwd", user.Password);
cookie.Expires = DateTime.Now.AddDays(15);
HttpContext.Current.Response.Cookies.Add(cookie);
}
else
{
cookie.Expires = DateTime.Now.AddDays(-1);
HttpContext.Current.Response.Cookies.Add(cookie);
}
return true;
}
else
{
//ResultCode -5 :Invalid Login ,-1:Database Error ,-2:Server Error ,-3:Invalid Parameter specified ,-4:Invalid Token
return false;
}
}
catch
{
throw;
}
finally
{
url = string.Empty;
callType = string.Empty;
server = string.Empty;
}
}
Okay here after converts pass to MD5 creates a "string content" with the information given.
Then in next line (HttpResponseMessage json_list = ApiCallBL.GetAPIResponse(url, content);) calls the API with the url and content as parameters where it finds if the data exists.
API code:
public static HttpResponseMessage GetAPIResponse(string url, string content)
{
StringBuilder traceLog = null;
HttpContent httpContent = null;
try
{
traceLog = new StringBuilder();
traceLog.AppendLine("Start: BusinessLayer getAPIResponse() Request Data:- " + DateTime.Now + "URL = " + url + "&content = " + httpContent);
using (HttpClient client = new HttpClient())
{
httpContent = new StringContent(content);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
var resp = client.PostAsync(url, httpContent).Result;
Debug.WriteLine("resp: " + resp.Content.ReadAsStringAsync().Result);
traceLog.AppendLine("End: BusinessLayer getAPIResponse() call completed HttpResponseMessage received");
return resp;
}
}
catch
{
throw;
}
finally
{
traceLog = null;
httpContent.Dispose();
url = string.Empty;
content = string.Empty;
}
}
In the following line, console prints the result that I cant understand where it cames from (Debug.WriteLine("resp: " + resp.Content.ReadAsStringAsync().Result);)
Sorry for the confusion , I am in my first job with zero work experience and I am called to learn how this works alone without proper education on ASP.NET from them.
You will not go very far without debbugger. Learn how to debug in Visual Studio (YouTube tutorials might be fastest way). Place debug points along critical points in code (for example moment when client sends and receives response is line var resp = client.PostAsync...) and check variables.
Url for API server is actually defined in the line
url = ConfigurationManager.AppSettings["login_url"].ToString();
ConfigurationManager means Web.config file, check it's appSettings section for login_url entry, there is your url.
Btw, using (HttpClient client = new HttpClient()) is not a good way to use a HttpClient and will lead to port exhaustion. It's ok for small number of requests, but for larger ones you must reuse it, or use HttpClientFactory (for .NET Core).

Lead Ads Testing Tool - Server failure (102)

I'm trying to get Facebook leadgen ad data.
1-)As seen below, facebook sends the data to me successfully and I receive it successfully.
Successful Process img
2-)But the submissions I made on just this page do not come. What could be the reason for this?
Failed Process img
*But only the opinions I made on this page are not coming. What could be the reason for this?
Facebook doesn't even post. As seen in the picture, Server failure (102) information is displayed. What is the reason of this?
3-)The code I received the incoming data
Asp.Net Api Method
public async Task<HttpResponseMessage> Post([FromBody] JsonData data)
{
try
{
dbmanager db = new dbmanager();
db.Jsonkaydetv2(data);
var entry = data.Entry.FirstOrDefault();
var change = entry?.Changes.FirstOrDefault();
if (change == null) return new HttpResponseMessage(HttpStatusCode.BadRequest);
//Generate user access token here https://developers.facebook.com/tools/accesstoken/
const string token = "XXXX";
var leadUrl = $"https://graph.facebook.com/v2.10/{change.Value.LeadGenId}?access_token={token}";
var formUrl = $"https://graph.facebook.com/v2.10/{change.Value.FormId}?access_token={token}";
using (var httpClientLead = new HttpClient())
{
var response = await httpClientLead.GetStringAsync(formUrl);
if (!string.IsNullOrEmpty(response))
{
var jsonObjLead = JsonConvert.DeserializeObject<LeadFormData>(response);
db.JsonkaydetLeadFormData(jsonObjLead);
//jsonObjLead.Name contains the lead ad name
//Jsonkaydet(jsonObjLead.Name+"x");
//If response is valid get the field data
using (var httpClientFields = new HttpClient())
{
var responseFields = await httpClientFields.GetStringAsync(leadUrl);
if (!string.IsNullOrEmpty(responseFields))
{
var jsonObjFields =JsonConvert.DeserializeObject<LeadData(responseFields);
db.JsonkaydetLeadData(jsonObjFields);
//jsonObjFields.FieldData contains the field value
}
}
}
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
catch (Exception ex)
{
Jsonkaydet(ex.ToString());
Trace.WriteLine($"Error-->{ex.Message}");
Trace.WriteLine($"StackTrace-->{ex.StackTrace}");
return new HttpResponseMessage(HttpStatusCode.BadGateway);
}
}

Cosmos DB Azure Table API oData Authentication REST / C#?

I'm trying to access Azure Cosmos DB using Table API.
The challenge is, despite creating SharedKeyLite, server is still returning Unauthorized - seems like SharedKeyLite is not supported or I'm generating the signature or headers wrong.
Here is the code
static readonly string storageAccountName = "accountName";
static readonly string storageAccountKey = "xxxx";
static readonly string uri = "https://accountName.table.cosmosdb.azure.com/Contacts()";
static readonly string utc_date = DateTime.UtcNow.ToString("r");
static void Main(string[] args)
{
Console.WriteLine(GetResult().Result);
}
static async Task<string> GetResult()
{
// Set this to whatever payload you desire. Ours is null because
// we're not passing anything in.
Byte[] requestPayload = null;
var requestDateString = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
var requestUri = new Uri(uri);
DateTime now = DateTime.UtcNow;
//Instantiate the request message with a null payload.
using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, uri)
{ Content = (requestPayload == null) ? null : new ByteArrayContent(requestPayload) })
{
ConstructHeaders(httpRequestMessage.Headers, requestDateString);
string authorizationHeader = GenerateSharedKeyLite(storageAccountKey, storageAccountName, uri,requestDateString);
httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("SharedKeyLite", authorizationHeader);
// Send the request.
using (HttpResponseMessage httpResponseMessage = await new HttpClient().SendAsync(httpRequestMessage))
{
string json = await httpResponseMessage.Content.ReadAsStringAsync();
return json;
}
}
}
These are the headers I"m adding, expansion of ConstructHeaders method.
Refer this link for request parameters
//Construct the headers
static void ConstructHeaders(HttpRequestHeaders headers, string now)
{
headers.Add("x-ms-date", now);
headers.Add("x-ms-version", "2017-04-17");
// If you need any additional headers, add them here before creating
// the authorization header.
headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if (headers.Contains("DataServiceVersion"))
headers.Remove("DataServiceVersion");
headers.Add("DataServiceVersion", "3.0;NetFx");
if (headers.Contains("MaxDataServiceVersion"))
headers.Remove("MaxDataServiceVersion");
headers.Add("MaxDataServiceVersion", "3.0;NetFx");
}
And this is the method that creates the SharedKeyLite
//Created Shared Key Lite
static string GenerateSharedKeyLite(string accessKey, string account, string url, string date)
{
var uri = new Uri(url);
var canonicalizedResourceString = uri.PathAndQuery;
var queryStart = canonicalizedResourceString.IndexOf('?');
if (queryStart > -1)
{
if (queryStart < canonicalizedResourceString.Length - 1)
{
var path = canonicalizedResourceString.Substring(0, queryStart);
var parameters = HttpUtility.ParseQueryString(canonicalizedResourceString.Substring(queryStart + 1));
var sb = new StringBuilder();
foreach (var keyOri in parameters.Keys)
{
var value = parameters[keyOri];
var key = keyOri.ToLowerInvariant();
sb.Append("\n");
sb.Append(key);
sb.Append(":");
sb.Append(value);
}
canonicalizedResourceString = canonicalizedResourceString + sb.ToString();
}
else
{
canonicalizedResourceString = canonicalizedResourceString.Substring(0, canonicalizedResourceString.Length - 1);
}
}
canonicalizedResourceString = $"/{account}{canonicalizedResourceString}";
var stringToSign = $"{date}\n{canonicalizedResourceString}";
var signedSignature = string.Empty;
using (var hmac = new HMACSHA256(Convert.FromBase64String(accessKey)))
{
var outputBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
var signature = Convert.ToBase64String(outputBytes);
return $"{account}:{signature}";
}
}
Any Help? Ideally I want to perform the odata query using simple.odata, but first trying to make this work using HttpClient
Just copy your code and it works on my side. If you haven't modified your code, please make sure your storageAccountName and storageAccountKey are correct.
BTW, in method GenerateSharedKeyLite there's no need to add query parameters to canonicalizedResourceString for entity operation. You only need to add comp if you want to operate component info for table or service. See constructing-the-canonicalized-resource-string.
The query string should include the question mark and the comp parameter (for example, ?comp=metadata). No other parameters should be included on the query string.

C# Sending Cookies with Post Request

I want to sign in to youtube with post request. I used xNet for HttpRequest.
I wrote following codes:
static void Main(string[] args)
{
string url = "https://accounts.google.com/_/signin/sl/lookup?hl=en&_reqid=55174&rt=j";
HttpClass httpClass = new HttpClass();;
httpClass.PostRequestAsync(url, "username", "password");
}
class HttpClass {
public async Task PostRequestAsync(string url, string account, string pass)
{
xNet.HttpRequest http = new xNet.HttpRequest();
http.Cookies = new CookieDictionary();
string type = "application/x-www-form-urlencoded;charset=utf-8";
string query =
"continue=https%3A%2F%2Fwww.youtube.com%2Fsignin%3Ffeature%3Dsign_in_button%26action_handle_signin%3Dtrue%26app%3Ddesktop%26next%3D%252F%26hl%3Den&service=youtube&hl=en&f.req=%5B%22" + account + "%22%2C%22AEThLlxFJqXTI-dLw8jxU_Lw8c4Qtpc4DAAeEE1rpkbEUFqwwK1U86bZEzsWmZKM5IjRccPvbYTLgb0yonB3vputyMTNm-8YcGqbe_GeaB6RHFJImp_gZ-y0jFv4nduPGxM-zpJX8BahbDlIyeY2sP8-puVe3W1iwKX3rGcSFGMevHHK-ByNEUY%22%2C%5B%5D%2Cnull%2C%22TR%22%2Cnull%2Cnull%2C2%2Cfalse%2Ctrue%2C%5Bnull%2Cnull%2C%5B2%2C1%2Cnull%2C1%2C%22https%3A%2F%2Faccounts.google.com%2FServiceLogin%3Fuilel%3D3%26passive%3Dtrue%26service%3Dyoutube%26continue%3Dhttps%253A%252F%252Fwww.youtube.com%252Fsignin%253Ffeature%253Dsign_in_button%2526action_handle_signin%253Dtrue%2526app%253Ddesktop%2526next%253D%25252F%2526hl%253Den%26hl%3Den%22%2Cnull%2C%5B%5D%2C4%2C%5B%5D%2C%22GlifWebSignIn%22%5D%2C1%2C%5Bnull%2Cnull%2C%5B%5D%5D%2Cnull%2Cnull%2Cnull%2Ctrue%5D%2C%22" + account + "%22%5D&bgRequest=%5B%22identifier%22%2C%22!Pj2lPRxCiup4YICaVSxEHyFXdsNE5lECAAAAQ1IAAAAbCgAW9I8p8C1f10xg_NjCyA99rybP30APm5kBCr5B19mb-UkpwTj1ZsyybospA0TSjuUTuJeCHmkiRqKfhHxRE1CV0Yd7nifpK8VCTMNnmUMrl4-anneYlV-Bs3NQESEmJTEcxBOjvbo_tXSasO8KbZopdTxzUHm-qBGOQRTUZM4Hw6x-1HJdLoCQ2bi4FoAhbsWEt6paR0K4neYHS1kdxewjDKefWWCQ__O3C71yOjm6p0S1rjNUEM0ak9V8N2CcnIFYQ77b1B98nHCZmgMr81YtgAOF8ClSb4ZV8AiUc96rC1rvMV2RIvW54RUgsJwWHXBx0nid8tRMdUmzCTymoa-_at7qE1nJL8SMAU9WEnGOs0u2xKlBKGsjNgnqhligTDBDPnp7%22%5D&azt=AFoagUUuZ6teJ3APaa8f6ly_olQZHdGWBg%3A1525177142108&cookiesDisabled=false&deviceinfo=%5Bnull%2Cnull%2Cnull%2C%5B%5D%2Cnull%2C%22TR%22%2Cnull%2Cnull%2C%5B%5D%2C%22GlifWebSignIn%22%2Cnull%2C%5Bnull%2Cnull%2C%5B%5D%5D%5D&gmscoreversion=undefined&checkConnection=youtube%3A288%3A1&checkedDomains=youtube&pstMsg=1&";
string html;
html = http.Post(url, query, type).ToString();
htmlTest(html);
type = "application/x-www-form-urlencoded";
query =
account +
"&continue=https%3A%2F%2Fwww.youtube.com%2Fsignin%3Ffeature%3Dsign_in_button%26action_handle_signin%3Dtrue%26app%3Ddesktop%26next%3D%252F%26hl%3Den&password=" +
pass + "&ca=&ct=";
http.Cookies = new CookieDictionary();
html = http.Post(url, query, type).ToString();
htmlTest(html);
}
public void htmlTest(string html)
{
File.WriteAllText("a.html", html);
Process.Start("a.html");
}
}
Response is:
)]}' [[["er",null,null,null,["gf.rrerr",1,"https://support.google.com/accounts/answer/61416?hl\u003den"] ,null,"gf.rrerr"] ,["e",2,null,null,149] ]]
The given link "https://support.google.com/accounts/answer/61416?hl\u003den" is says you must to open your cache data. I think I need to pass cookies with post but How can I pass cookies with post request?
Try to use cookíe with code
request.Cookies = new CookieDictionary()
{
{"ggg", "hhh"}
};

Send Push Notification from a device via Azure NotificationHub REST Api

I am trying to send a push notification from an iOS device (iPhone) via Azure NotificationHub REST Api. I am attempting this from a Xamarin.iOS solution following the Azure documentation I found online.
Response returns following info:
Error: '50002: Provider Internal Error'
Status code: 500
Code used to invoke NotificationHub REST Api (from iOS client app):
var hubUtil = new NotificationHubUtility("Endpoint=sb://company-name.servicebus.windows.net/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=00000000000000011111111111111122222222222222");
hubUtil.SendNotificationMessage("This is a TEST Notification!").ConfigureAwait(false);
public class NotificationHubUtility
{
public string Endpoint { get; private set; }
public string SasKeyName { get; private set; }
public string SasKeyValue { get; private set; }
public string HubName { get; private set; }
public string ApiVersion { get; private set; }
public NotificationHubUtility(string connectionString)
{
//Parse Connectionstring
string[] parts = connectionString.Split(new char[] {';'});
for (int i = 0; i < parts.Length; i++)
{
if (parts[i].StartsWith("Endpoint", StringComparison.CurrentCulture))
Endpoint = "https" + parts[i].Substring(11);
if (parts[i].StartsWith("SharedAccessKeyName", StringComparison.CurrentCulture))
SasKeyName = parts[i].Substring(20);
if (parts[i].StartsWith("SharedAccessKey", StringComparison.CurrentCulture))
SasKeyValue = parts[i].Substring(16);
}
HubName = "my-hub";
ApiVersion = "?api-version=2014-09-01";
}
public string GetSaSToken(string uri, int minUntilExpire)
{
string targetUri = Uri.EscapeDataString(uri.ToLower()).ToLower();
// Add an expiration in seconds to it.
long expiresOnDate = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
expiresOnDate += minUntilExpire * 60 * 1000;
long expires_seconds = expiresOnDate / 1000;
var toSign = targetUri + "\n" + expires_seconds;
// Generate a HMAC-SHA256 hash or the uri and expiration using your secret key.
IMacAlgorithmProvider hasher = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha256);
var messageBuffer = WinRTCrypto.CryptographicBuffer.ConvertStringToBinary(toSign, Encoding.UTF8);
var keyBuffer = WinRTCrypto.CryptographicBuffer.ConvertStringToBinary(SasKeyValue, Encoding.UTF8);
var hmacKey = hasher.CreateKey(keyBuffer);
var signedMessage = WinRTCrypto.CryptographicEngine.Sign(hmacKey, messageBuffer);
string signature = Uri.EscapeDataString(WinRTCrypto.CryptographicBuffer.EncodeToBase64String(signedMessage));
var token = "SharedAccessSignature sig=" + signature + "&se=" + expires_seconds + "&skn=" + SasKeyName + "&sr=" + targetUri;
return token;
}
public async Task SendNotificationMessage(string message)
{
try
{
// basic http client (if needed)
var httpClient = new HttpClient();
httpClient.MaxResponseContentBufferSize = 1024000;
var notificationPayload = "{\"aps\":{\"alert\":\"" + message + "\"}}";
var notificationHubUrl = $"{Endpoint}{HubName}/messages/{ApiVersion}";
var authToken = GetSaSToken(notificationHubUrl, 10);
var request = new HttpRequestMessage(HttpMethod.Post, notificationHubUrl);
//request.Headers.Add("Content-Type", "application/json;charset=utf-8");
request.Headers.Add("ServiceBusNotification-Format", "apple");
request.Headers.Add("ServiceBusNotification-Apns-Expiry", DateTime.UtcNow.AddYears(1).ToString("YYYY-MM-DDThh:mmTZD"));
request.Headers.Add("Authorization", authToken);
var requestBody = new StringContent(notificationPayload, Encoding.UTF8, "application/json");
request.Content = requestBody;
var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead);
}
catch (Exception ex)
{
Console.Error.WriteLine(#"ERROR - Sending Notification {0}", ex.Message);
}
}
}
Example of Connection String:
Endpoint=sb://company-name.servicebus.windows.net/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=00000000000000011111111111111122222222222222
Environment and Assumptions:
Xamarin.iOS solution using C#
Using PCLCrypto library for encryption
I am attempting to recreate the solution demonstrated in an Example in Azure examples github repo but using Xamarin.iOS
The connection string is taken directly from Azure portal
The code for generating SaS token is adapted from Azure NotificationHub REST Api Documentation
Notification hub works, I am able to send a test push notification through the hub UI and i see it come in on the device
What am I missing here? I wasn't able to find much relevant documentation for this error online. Any help would be greatly appreciated.
Update with Fix:
The following 2 changes to the code above fixed the issue for me:
Changed
ApiVersion = "?api-version=2014-09-01";
to
ApiVersion = "?api-version=2016-07";
Changed
request.Headers.Add("ServiceBusNotification-Apns-Expiry", DateTime.UtcNow.AddYears(1).ToString("YYYY-MM-DDThh:mmTZD"));
to
request.Headers.Add("ServiceBusNotification-Apns-Expiry", DateTime.UtcNow.AddYears(1).ToString("yyyy-MM-ddTHH:mm:sszzz"));
The Api Version 2014-09-01 is not correct. Please use 2016-07 as the Api version and you should be good.
Thanks
Sohrab
I've figured out the issue(s):
+1 to Sohrab for pointing out the Api Version, I updated it to ApiVersion = "?api-version=2016-07";
There was an error in the ServiceBusNotification-Apns-Expiry header value format, the date was not being correctly formatted to string. Corrected format string is this ToString("yyyy-MM-ddTHH:mm:sszzz")

Categories