Azure App Service with websockets and AD authentication

Viewed 1176

we got an application deployed as App Service and we are using SignalR for communication. After enabling AAD authentication - in browsers we started receiving 302 responses with redirect location to Azure AD.

Seems like the authentication layer on App Service is ignoring access_token passed by query string.

Request

Request URL: wss://<url>/hubs/chat?access_token=<token>
Request Method: GET

Response

Status Code: 302 Redirect
Location: https://login.windows.net/common/oauth2/authorize?...

After looking everywhere we couldn't find any solution to make this work. The only solution to this issue that we see is either to disable authentication on App Service or use Long-Pooling, but both options are not acceptable in our situation.

3 Answers

By default, you web application will not get the access token from query string. Commonly, it will get the access token from authorization header or the cookie.

To get the access token from query string, you need to implement your custom authentication way.

  1. Install Microsoft.Owin.Security.ActiveDirectory NuGet package.
  2. Create an authentication provider which will get access token from query string.

public class QueryStringOAuthBearerProvider : OAuthBearerAuthenticationProvider { public override Task RequestToken(OAuthRequestTokenContext context) { var value = context.Request.Query.Get("access_token"); if (!string.IsNullOrEmpty(value)) { context.Token = value; } return Task.FromResult<object>(null); } }

  1. Add map in .

app.Map("/yourpath", map => { map.UseWindowsAzureActiveDirectoryBearerAuthentication(new WindowsAzureActiveDirectoryBearerAuthenticationOptions { Provider = new QueryStringOAuthBearerProvider(), Tenant = tenantId, TokenValidationParameters = new TokenValidationParameters { ValidAudience = clientId } }); map.RunSignalR(hubConfiguration); });

Using a custom middleware, I was able to update the request prior to authorization occurring:

using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace Stackoverflow.Example.Security.Middleware
{
    public class BearerTokenFromQueryToHeaderMiddleware
    {
        private readonly RequestDelegate _next;

        public BearerTokenFromQueryToHeaderMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            var token = context.Request.Query["access_token"];
            if (!string.IsNullOrWhiteSpace(token))
            {
                context.Request.Headers.Add("Authorization", $"Bearer {token}");
            }
            await _next(context);
        }
    }
}

I didn't try to get this working with the OpenID framework, but I did test using a custom policy. As long as this is registered earlier than the authentication, then this middleware should execute prior to the framework looking for the token in the header.

After multiple calls with Microsoft Technical Support, MS confirmed that App Service Authentication layer doesn't support access token passed in query string and there are no plans for this support yet. So there are two options:

  • Use different protocol for SignalR (long pooling works just fine)
  • Drop App Service Authentication
Related