Stuart.Sklinar
Stuart.Sklinar

Reputation: 3761

DelegatingHandler to add Authorization token to request

For the purpose of downloading files I need to use a GET: /API/File/ID?bearerToken=XYZ... method.

I've created a DelegatingHandler to add my token to the AuthorizationHeader, but it appears the token validation may be done before this point...

All of the tokens at current at added by Angular adding the token to the HTTP header before the request.

public void Configuration(IAppBuilder app)
    {
        var config = new HttpConfiguration();
        ConfigureOAuth(app);
        WebApiConfig.Register(config);
        GlobalFilters.Add(config);
        app.UseWebApi(config);

        config.MessageHandlers.Insert(0, new QueryStringBearerToken());
    }

..

public class QueryStringBearerToken : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            var bearerToken = request.GetQueryNameValuePairs().
                Where(kvp => kvp.Key == "bearertoken")
                .Select(kvp => kvp.Value)
                .FirstOrDefault();

            //QueryString exists and Header doesn't
            if (!string.IsNullOrWhiteSpace(bearerToken) && !request.Headers.Any(x=>x.Key == "Authorization")) 
            {
                request.Headers.Add("Authorization", "Bearer " + bearerToken);
            }

            return base.SendAsync(request, cancellationToken);
        }
    }

Upvotes: 2

Views: 3838

Answers (1)

Filip W
Filip W

Reputation: 27187

I presume you are using Katana's Bearer middleware? (judging by your call to ConfigureAuth?)

If so, the Katana middleware will indeed run before the Web API handlers and reject your request before it even gets a chance to be processed by the handler.

Instead of creating a handler you should move your functionality to Katana middleware.

Here's an example:

public class QueryBearerMiddleware : OwinMiddleware
{
    public QueryBearerMiddleware(OwinMiddleware next)
        : base(next)
    {
    }

    public override async Task Invoke(IOwinContext context)
    {
        string bearerToken = null;
        if (context.Request.QueryString.HasValue)
        {
            var queryPairs = context.Request.QueryString.ToUriComponent()
                .Substring(1)
                .Split(new [] {'&'}, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Split('=')).ToDictionary(x => x[0], x => x[1]);
            if (queryPairs.ContainsKey("bearertoken"))
            {
                bearerToken = queryPairs["bearertoken"];
            }
        }

        //QueryString exists and Header doesn't
        if (!string.IsNullOrWhiteSpace(bearerToken) && context.Request.Headers.All(x => x.Key != "Authorization"))
        {
            context.Request.Headers.Add("Authorization", new [] { "Bearer " + bearerToken });
        }

        await Next.Invoke(context);
    }
}

You should register this middleware to run before the Bearer middlware.

Somewhere in your ConfigureAuth you should have a call to app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());. This new middleware we just created, should be registered before, i.e:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.Use(typeof(QueryBearerMiddleware));
        var config = new HttpConfiguration();
        ConfigureOAuth(app);
        WebApiConfig.Register(config);
        GlobalFilters.Add(config);
        app.UseWebApi(config);
    }
}

Upvotes: 2

Related Questions