Add Multiple WWW-Authenticate headers with OWIN

2019-07-24 05:28发布

I want our service to advertise more than one authentication scheme: for example both Bearer and some custom scheme, say X-Custom. (I have an OWIN middleware component for each scheme). I take if from RFC 2616, sec 14.47 there is more than one way to do it:

Option a) multiple headers WWW-Authenticate: Bearer WWW-Authenticate: X-Custom

Option b) comma-separated list WWW-Authenticate: Bearer, X-Custom

My preference would be option a) so a client only has to do something like Response.Headers.Exists("WWW-Authenticate", preferredScheme) instead of comma parsing the header (which the RFC says they should, but...)

However, Katana uses a dictionary for headers. Trying to add the second header throws an exception with "The key 'WWW-Authenticate' is already present in the dictionary."

Is there a way for a middleware component to inject more than one WWW-Authenticate header?

1条回答
够拽才男人
2楼-- · 2019-07-24 06:09

It is IDictionary<string, string[]>. Key is a string but value is an array of string. So, you just need to set the header like this.

app.Run(async (IOwinContext context) =>
{
    context.Response.Headers.Add("WWW-Authenticate",
                                    new[] { "Bearer", "X-Custom" });
    // Some other code
});

UPDATE I believe you are very kind to accept my answer as answer :). Thanks but not sure it answered your question and hence the edit. First of all, I did not get the point you tried to make, which is to add the different headers from different middleware and yet wanting to see them in different lines in the response. I do not think there is anyway to do this for standard HTTP headers like WWW-Authenticate. In fact, before I answered your question, I quickly wrote a small program to verify but the mistake I made was to misspell this header. Because of that, I was actually getting the header values like this.

WWW-Authentciate: X-Custom
WWW-Authentciate: Bearer

Anyways, the following works in getting the header values in two lines.

app.Use(async (IOwinContext context, Func<Task> next) =>
{
    context.Response.Headers.Set("WWW-Authenticate", "Bearer");

    await next.Invoke();
});

app.Run(async (IOwinContext context) =>
{
    var x = context.Response.Headers.Get("WWW-Authenticate");
    context.Response.Headers.Remove("WWW-Authenticate");
    context.Response.Headers.Add("WWW-Authenticate", new[] { "X-Custom", x });
});

However, this does not work for standard headers. Nonetheless, this is an interesting exercise but at the end of the day, there is no accepted standard in terms of the API here (as far as I know). Even if you somehow get this working the way you want, the moment you change an underlying OWIN component, say the server or host, you could get different behavior. After all, option a and option b are exactly the same and you should not see any difference if you are working on top of some library to read the headers, unless you do some low-level stuff.

查看更多
登录 后发表回答