Search code examples
c#oauthuwpwns

WNS push notification cannot be sent


I'm using the following code to send a push raw notification to my app. The code is taken from the examples given in the official documentation.

public string PostToWns(string secret, string sid, string uri, string xml, string notificationType, string contentType)
{
    try
    {
        var accessToken = GetAccessToken(secret, sid);

        byte[] contentInBytes = Encoding.UTF8.GetBytes(xml);

        HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest;
        request.Method = "POST";
        request.Headers.Add("X-WNS-Type", notificationType);
        request.ContentType = contentType;
        request.Headers.Add("Authorization", String.Format("Bearer {0}", accessToken.AccessToken));

        using (Stream requestStream = request.GetRequestStream())
            requestStream.Write(contentInBytes, 0, contentInBytes.Length);

        using (HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse())
        //                              ^^^^^ This throws an exception ^^^^^
            return webResponse.StatusCode.ToString();
    }

    catch (WebException webException)
    {
        HttpStatusCode status = ((HttpWebResponse)webException.Response).StatusCode;

        if (status == HttpStatusCode.Unauthorized)
        {
            GetAccessToken(secret, sid);
            return PostToWns(uri, xml, secret, sid, notificationType, contentType);
        }
        else if (status == HttpStatusCode.Gone || status == HttpStatusCode.NotFound)
        {
            return "";
        }
        else if (status == HttpStatusCode.NotAcceptable)
        {
            return "";
        }
        else
        {
            string[] debugOutput = {
                                status.ToString(),
                                webException.Response.Headers["X-WNS-Debug-Trace"],
                                webException.Response.Headers["X-WNS-Error-Description"],
                                webException.Response.Headers["X-WNS-Msg-ID"],
                                webException.Response.Headers["X-WNS-Status"]
                            };
            return string.Join(" | ", debugOutput);
        }
    }

    catch (Exception ex)
    {
        return "EXCEPTION: " + ex.Message;
    }
}

// Authorization
[DataContract]
public class OAuthToken
{
    [DataMember(Name = "access_token")]
    public string AccessToken { get; set; }
    [DataMember(Name = "token_type")]
    public string TokenType { get; set; }
}

private OAuthToken GetOAuthTokenFromJson(string jsonString)
{
    using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(jsonString)))
    {
        var ser = new DataContractJsonSerializer(typeof(OAuthToken));
        var oAuthToken = (OAuthToken)ser.ReadObject(ms);
        return oAuthToken;
    }
}

protected OAuthToken GetAccessToken(string secret, string sid)
{
    var urlEncodedSecret = HttpUtility.UrlEncode(secret);
    var urlEncodedSid = HttpUtility.UrlEncode(sid);

    var body = String.Format("grant_type=client_credentials&client_id={0}&client_secret={1}&scope=notify.windows.com",
                                urlEncodedSid,
                                urlEncodedSecret);

    string response;
    using (var client = new WebClient())
    {
        client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
        response = client.UploadString("https://login.live.com/accesstoken.srf", body);
    }
    return GetOAuthTokenFromJson(response);
}

The server always returns Unauthorized. However, I am able to obtain a token without problems and the channel URL used is obtained as in the official example.

The entire code executes at once, so I doubt the token expires every time.

I tried using online tools to check if push notifications are working and the same Unauthorized error happens there.

Do I have to register for WNS somewhere? My app is already in the Store and I have the ID and password from the Live Services website. I use these as credentials to get the token and the token is obtained every time. I described this as part of a related question: Proper WNS credentials.

Does anyone have any ideas or solutions?


Solution

  • I found an answer in a different question: Windows Notifications Service: 401 Invalid Token when trying to create a Toast notification in PHP.

    The client_id must be the full SID (including the protocol prefix) and the client_secret the password.