如何使Azure AD身份验证接受url查询字符串访问令牌

如何使Azure AD身份验证接受url查询字符串访问令牌,azure,signalr,azure-active-directory,azure-web-app-service,Azure,Signalr,Azure Active Directory,Azure Web App Service,是否有办法将内置Azure AD身份验证配置为同时接受Authorization:Bearer头和access\u令牌=url查询字符串 我需要这个来允许从浏览器进行SingalR WebSocket连接 我的场景 我有一个ASP.Net核心后端托管SignalR核心集线器。该站点作为Azure应用程序服务托管,并启用Azure AD身份验证 无论是从.Net应用程序还是从web站点访问RESTAPI都可以正常工作 来自.Net 与.Net应用程序的信号器连接工作正常,并使用websocket。

是否有办法将内置Azure AD身份验证配置为同时接受
Authorization:Bearer
头和
access\u令牌=
url查询字符串

我需要这个来允许从浏览器进行SingalR WebSocket连接

我的场景

我有一个ASP.Net核心后端托管SignalR核心集线器。该站点作为Azure应用程序服务托管,并启用Azure AD身份验证

无论是从.Net应用程序还是从web站点访问RESTAPI都可以正常工作

来自.Net

与.Net应用程序的信号器连接工作正常,并使用websocket。通过以下HTTP请求建立websocket连接:

GET https://<url>/hubs/chat?id=rk-Adzl1EWGCv8wsVF5ayg HTTP/1.1
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: m02nzEFDAFVwCgUPlF2rEA==
Sec-WebSocket-Version: 13
X-Requested-With: XMLHttpRequest
Authorization: Bearer <token>
Host: evotrqhub.evopro-ag.de
Cookie: ARRAffinity=8666c8fbc8cd126e76e2b31c5880dd9c4968a103221108a610b408e12a15fa39
GEThttps:///hubs/chat?id=rk-Adzl1EWGCv8wsVF5ayg HTTP/1.1
连接:升级
升级:websocket
Sec WebSocket键:m02nzEFDAFVwCgUPlF2rEA==
Sec WebSocket版本:13
X-request-With:XMLHttpRequest
授权:持票人
主持人:evotrqhub.evopro-ag.de
Cookie:ARRAffinity=8666c8fbc8cd126e76e2b31c5880dd9c4968a103221108a610b408e12a15fa39
请注意,授权是通过
authorization:Bearer
标题完成的

从浏览器中

当我从浏览器(Chrome或Firefox)建立信号器连接时,无法建立websocket连接,长轮询用作回退。websocket连接的HTTP请求为:

GET https://evotrqhub.evopro-ag.de/hubs/coilthickness?id=QB5xx4MLGi6uSvVK4QmvZA&access_token=<token> HTTP/1.1
Host: evotrqhub.evopro-ag.de
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://localhost:5000
Sec-WebSocket-Version: 13
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36
Accept-Encoding: gzip, deflate, br
Accept-Language: de,en-US;q=0.9,en;q=0.8
Cookie: ARRAffinity=8666c8fbc8cd126e76e2b31c5880dd9c4968a103221108a610b408e12a15fa39
Sec-WebSocket-Key: K4hUR2QFyRxcDKxfoNwn2w==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
GEThttps://evotrqhub.evopro-ag.de/hubs/coilthickness?id=QB5xx4MLGi6uSvVK4QmvZA&access_token= HTTP/1.1
主持人:evotrqhub.evopro-ag.de
连接:升级
Pragma:没有缓存
缓存控制:没有缓存
升级:websocket
来源:http://localhost:5000
Sec WebSocket版本:13
用户代理:Mozilla/5.0(Windows NT 10.0;Win64;x64)AppleWebKit/537.36(KHTML,类似Gecko)Chrome/73.0.3683.86 Safari/537.36
接受编码:gzip,deflate,br
接受语言:de,en-US;q=0.9,en;q=0.8
Cookie:ARRAffinity=8666c8fbc8cd126e76e2b31c5880dd9c4968a103221108a610b408e12a15fa39
Sec WebSocket键:K4HUR2QFYRXCDKFONWN2W==
Sec WebSocket扩展:permessage deflate;客户端\u最大\u窗口\u位
在这里,授权是通过
auth\u token=
url查询参数完成的

来自服务器的响应是重定向(响应代码302)到Azure AD登录页面

建议使用
JwtBearerEvents
OnMessageReceived
来处理这种情况,但在这种情况下,Azure服务应用程序的Azure AD身份验证会在我的用户代码处理查询之前拒绝查询


那么,有没有一种方法可以配置Azure App Service AD Authentication以接受
访问令牌
参数?

如果您使用的是SignalR JavaScript客户端,那么您可以提供一种方法来返回访问令牌,该令牌将作为承载令牌包含在客户端的请求中。无论使用何种运输方式,这都应该有效(尽管我自己还没有尝试过)

文档()中的示例:


我不知道是否有一种内置的方法来管理(可能是的),无论如何,您可以在管道中添加一个middlware来检查请求上下文中是否存在承载令牌,如果不存在,检查querystring集合中是否存在access_token参数,然后将其作为承载令牌添加到新的授权标头中,如下面的代码片段所示

Startup.cs

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {

        ...  
      
        app.UseCors("CorsPolicy");

        // START BEARER TOKEN MIDDLEWARE ---------------------------------------------
        app.Use(async (context, next) =>
        {
            if (
                string.IsNullOrWhiteSpace(context.Request.Headers["Authorization"]) &&
                context.Request.QueryString.HasValue
            )
            {
                var token = context.Request.QueryString.Value
                    .Split('&')
                    .SingleOrDefault(x => x.Contains("access_token"))?.Split('=')[1];
                if (!string.IsNullOrWhiteSpace(token))
                {
                    context.Request.Headers.Add(
                        "Authorization", 
                        new[] { $"Bearer {token}" }
                    );
                }
            }
            await next.Invoke();
        });
        // END BEARER TOKEN MIDDLEWARE -----------------------------------------------

        app.UseAuthentication();
        app.UseAuthorization();

        ...

    }

显然,从安全角度来看,这不是最好的方案,因此请确保您的api通过https,并注意加密或完全避免请求日志。

我不熟悉SignalR,但您能检查一下这是否能解决您的问题吗?这在我的场景中不起作用,因为请求在呼叫路由到我的代码之前被Azure网站主机拒绝。但我认为我必须停用这个Azure身份验证,并在代码中处理它。那么这个方法就行了。啊,对不起,我没注意到那部分。那么你使用应用服务的认证/授权了吗?那样的话,这个建议肯定行不通。您的web应用程序是否为单页应用程序?如果是,您可以使用ADAL.js,但这是您部分的代码更改。我想你不能配置内置的Azure应用服务广告认证。这是我设置令牌的方式。但在文档的“身份验证和授权”页面中,它指出:“当使用WebSocket和服务器发送的事件时,令牌作为查询字符串参数传输。”因此我必须在后端处理它们。啊,的确如此。看起来这是Azure应用程序服务的内置授权功能可以处理的。(OAuth 2.0承载令牌规范在…)中也明确建议不要这样做,正如您所说:我想使用来自专业人士的现成解决方案进行身份验证。但你必须和你所得到的有关。
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {

        ...  
      
        app.UseCors("CorsPolicy");

        // START BEARER TOKEN MIDDLEWARE ---------------------------------------------
        app.Use(async (context, next) =>
        {
            if (
                string.IsNullOrWhiteSpace(context.Request.Headers["Authorization"]) &&
                context.Request.QueryString.HasValue
            )
            {
                var token = context.Request.QueryString.Value
                    .Split('&')
                    .SingleOrDefault(x => x.Contains("access_token"))?.Split('=')[1];
                if (!string.IsNullOrWhiteSpace(token))
                {
                    context.Request.Headers.Add(
                        "Authorization", 
                        new[] { $"Bearer {token}" }
                    );
                }
            }
            await next.Invoke();
        });
        // END BEARER TOKEN MIDDLEWARE -----------------------------------------------

        app.UseAuthentication();
        app.UseAuthorization();

        ...

    }