Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I connect to the Apple News API from c#?

Tags:

c#

apple-news

I'm trying to connect to Apple's News API. When generating the signature the results all appear the same from each of the different examples. However, I keep getting this error:

 {"errors":[{"code":"WRONG_SIGNATURE"}]}

Here is my c# code to generate the auth header.

public class Security
{
  public static string AuthHeader(string method, string url, object content=null)
  {
    var apiKeyId = "<YOUR KEY HERE...>"; //we get this value from our settings file.
    var apiKeySecret = "<YOUR SECRET HERE...>"; //we get this value from our settings file.
    if ( string.IsNullOrEmpty(apiKeyId) || string.IsNullOrEmpty(apiKeySecret)) return string.Empty;
    var encoding = new ASCIIEncoding();
    var dt = DateTime.Now.ToString(Constants.DateFormat);
    var canonicalRequest = string.Format("{0}{1}{2}", method, url, dt);
    var key = Convert.FromBase64String(apiKeySecret);
    var hmac = new HMACSHA256(key);
    var hashed = hmac.ComputeHash(encoding.GetBytes(canonicalRequest));
    var signature = Convert.ToBase64String(hashed);
    var authorizaton = string.Format(@"HHMAC; key={0}; signature={1}; date={2}", apiKeyId, signature, dt);
    return authorizaton;
  }
}

Short version of Constants class

public static class Constants
{
  public static readonly string ChannelId = "<YOUR CHANNEL ID HERE...>"; //again from our settings file
  public static readonly string BaseUrl = "https://news-api.apple.com";
  public static readonly string DateFormat = "yyyy-MM-ddTHH:mm:ssK";    
}

Short version of Actions class (SendCommand is the method that performs the request)

public class Actions
{
  public static string SendCommand(string action, string method)
  {
    var url = $"{Constants.BaseUrl}{action}";      
    var authheader = Security.AuthHeader(method, url, null);
    var request = (HttpWebRequest)WebRequest.Create(url);
    request.Method = method;
    request.Timeout = 1000;
    request.Headers.Add("Authorization", authheader);
    request.Accept = "application/json";
    request.ContentType = "application/json";
    var output = string.Empty;
    try
    {
      using (var response = request.GetResponse())
      {
        using (var reader = new StreamReader(response.GetResponseStream()))
          output = reader.ReadToEnd();
      }
    }
    catch (WebException e)
    {
      using (var reader = new StreamReader(e.Response.GetResponseStream()))
      {
        output = reader.ReadToEnd();
      }
    }          
    return output;
  }

  public static string ReadChannel()
  {
    var action = $"/channels/{Constants.ChannelId}";
    const string method = "GET";
    return SendCommand(action, method);
  }
}

I'm using the ReadChannel method for testing.

I've also tried examples in php and ruby with no luck.

Any ideas how to do this correctly?

like image 606
DJackson Avatar asked Nov 22 '25 00:11

DJackson


1 Answers

Pasted the authorization string generated from the original code on this post into fiddler and I was able to get a successful response from the Apple News API. It seems like HttpWebRequest isn't including the Authorization header correctly and submitting the same request with the property PreAuthenticate = true corrects this issue (HttpWebRequest.PreAuthenticate). Also, with a GET request the ContentType needs to be omitted so I've added a conditional statement to account for this too.

public class Actions
{
  public static string SendCommand(string action, string method)
  {
    var url = $"{Constants.BaseUrl}{action}";      
    var authheader = Security.AuthHeader(method, url, null);
    var request = (HttpWebRequest)WebRequest.Create(url);
    request.Method = method;
    request.Timeout = 1000;
    request.PreAuthenticate = true;
    request.Headers.Add("Authorization", authheader);
    request.Accept = "application/json";
    if(method.Equals("post", StringComparison.InvariantCultureIgnoreCase)) 
       request.ContentType = "application/json";
     var output = string.Empty;
    try
    {
      using (var response = request.GetResponse())
      {
        using (var reader = new StreamReader(response.GetResponseStream()))
          output = reader.ReadToEnd();
      }
    }
    catch (WebException e)
    {
      using (var reader = new StreamReader(e.Response.GetResponseStream()))
      {
        output = reader.ReadToEnd();
      }
    }          
    return output;
  }

  public static string ReadChannel()
  {
    var action = $"/channels/{Constants.ChannelId}";
    const string method = "GET";
    return SendCommand(action, method);
  }
}
like image 56
Benjamin Crispin Avatar answered Nov 24 '25 15:11

Benjamin Crispin