Authorization header issue with v6

I’m attempting to use ServiceStack v6 with a particular API and HTTP Utils, the old code in 5.x used to work fine:

loginResponse = (server + "/api/logon")
.PostJsonToUrl(loginRequest, requestFilter: req => req.Headers["Authorization"] = SessionToken)
.FromJson<LogonResponse>();

I’ve converted the code to v6 with the following statements:

loginResponse = (server + "/api/logon")
.PostJsonToUrl(loginRequest, requestFilter: req => req.With(c => c.AddHeader("Authorization", SessionToken)))
.FromJson<LogonResponse>();

However I’m getting errors with the API where the Authorization header value is being repeated:

POST [/api/logon] HTTP/1.1
Accept: application/json
Authorization: 028e0480a79094aadccd7e08072724da17ad05b2e2c0b9d8195b9f5576b1e47d 028e0480a79094aadccd7e08072724da17ad05b2e2c0b9d8195b9f5576b1e47d
Accept-Encoding: gzip, deflate, br
Content-Type: application/json
Content-Length: 46

Any ideas?

There’s something missing in your Authorization header. What does SessionToken contain, a Bearer Token?

The double header is odd, are you using this in .NET 6?

The API requires the client to perform a register call first and returns:

{
"errorCode": "NO_ERROR",
"errorText": "NO_ERROR",
"sessionToken": "028e0480a79094aadccd7e08072724da17ad05b2e2c0b9d8195b9f5576b1e47d"
}

You then use the returned sessionToken value with future requests, for example:

POST /api/keepalive HTTP/1.1
Content-Type: application/json; charset=utf-8
Authorization: 028e0480a79094aadccd7e08072724da17ad05b2e2c0b9d8195b9f5576b1e47d
Cache-Control: no-cache

Hope that helps. I am using it in .NET 6.

It’s because it’s expected the HTTP Authorization Header contains 2 parts, it’s invalid to send it in HttpClient without specifying the scheme.

Since it looks like a bearer token can you try:

loginResponse = (server + "/api/logon")
.PostJsonToUrl(loginRequest, requestFilter: req => 
    req.With(c => c.AddHeader("Authorization", "Bearer " + SessionToken)))
.FromJson<LogonResponse>();

This is an alternative for the recommended API for sending Bearer Tokens:

var loginResponse = (server + "/api/logon")
	.PostJsonToUrl(loginRequest, requestFilter: req => 
        req.With(c => c.SetAuthBearer(SessionToken)))
	.FromJson<LogonResponse>();

If I’m correct won’t that send “Bearer (token)” in the header?

Unfortunately the API will reject the request since the auth header needs to just contain the returned token.

Did you try? Because HttpWebRequest is deprecated and HttpClient wont let you send an invalid request.

I’m going to try it now and will report back shortly.

1 Like

Ok, the API I’m calling rejected the logon request (401 Unauthorized) with that code which is the error which caused this investigation.

Authorization: Bearer c99c77032990c1cff34544de7d0d60909ad21301b9bcfe72a0507545a51e99ef

Frustrating that APIs don’t follow the rules!

Then the only way you’re going to be able to make that request in .NET 6 is to use the obsolete HttpWebRequest. I’ve extracted the PostJsonToUrl API into this helper:

public static class LegacyHttpUtils
{
    public static string PostJsonToUrl(string url, object data,
	    Action<HttpWebRequest> requestFilter = null, Action<HttpWebResponse> responseFilter = null)
    {
	    return SendStringToUrl(WebRequest.CreateHttp(url), method: "POST", requestBody: data.ToJson(), contentType: MimeTypes.Json,
		    accept: MimeTypes.Json,
		    requestFilter: requestFilter, responseFilter: responseFilter);
    }

    public static string SendStringToUrl(HttpWebRequest webReq, string method, string requestBody, string contentType,
	    string accept, Action<HttpWebRequest> requestFilter, Action<HttpWebResponse> responseFilter)
    {
	    if (method != null)
		    webReq.Method = method;
	    if (contentType != null)
		    webReq.ContentType = contentType;

	    webReq.Accept = accept;
	    PclExport.Instance.AddCompression(webReq);

	    requestFilter?.Invoke(webReq);

	    if (requestBody != null)
	    {
		    using var reqStream = PclExport.Instance.GetRequestStream(webReq);
		    using var writer = new StreamWriter(reqStream, HttpUtils.UseEncoding);
		    writer.Write(requestBody);
	    }
	    else if (method != null && HttpUtils.HasRequestBody(method))
	    {
		    webReq.ContentLength = 0;
	    }

	    using var webRes = webReq.GetResponse();
	    using var stream = webRes.GetResponseStream();
	    responseFilter?.Invoke((HttpWebResponse)webRes);
	    return stream.ReadToEnd(HttpUtils.UseEncoding);
    }
}

Which you should be able to call with:

var loginResponse = LegacyHttpUtils.PostJsonToUrl(server + "/api/logon",
		loginRequest, requestFilter: req => req.Headers["Authorization"] = SessionToken)
	.FromJson<LogonResponse>();

Thanks so much for your help!

1 Like