401 未经授权在 Xamarin.Forms 上使用 Azure b2c

Posted

技术标签:

【中文标题】401 未经授权在 Xamarin.Forms 上使用 Azure b2c【英文标题】:401 Unauthorized with Azure b2c on Xamarin.Forms 【发布时间】:2020-06-21 00:55:33 【问题描述】:

我有一个用于连接到应用服务后端的 Xamarin.Forms 应用程序,并且我正在尝试使用 Auzre B2C JWT 令牌进行身份验证。

通过各种教程,我设法使用 Microsoft 帐户进行 B2C 设置,并且能够创建用户、更改密码和生成访问令牌。

我的下一步是将 [Authorize] 属性添加到我的控制器并尝试将该令牌传递给我的应用服务并授权用户,但无论我尝试什么,我都会从我的服务中收到 401 Unauthorized 响应。

我将 JWT 令牌添加到我的 HttpClient 的 Authorization 标头中,它正在访问服务。 我可以将我的令牌粘贴到https://jwt.ms/,它会正确地告诉我我的令牌中有什么。 我已实施 this code 以试图找出问题所在。

startup.cs 中的ConfigureServices 如下所示:

public void ConfigureServices(IServiceCollection services) 

            services.AddAuthentication(options =>                 
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            )
                .AddJwtBearer(options => 

                    options.Audience = Configuration["Authentication:AzureAd:ClientId"];

                    options.Events = new JwtBearerEvents 
                        OnAuthenticationFailed = AuthenticationFailed
                    ;

                    options.Authority = $"https://tenant name.b2clogin.com/tenant id/Configuration["Authentication:AzureAd:Policy"]";

                    options.Events = new JwtBearerEvents 

                        OnAuthenticationFailed = ctx =>
                        
                            ctx.Response.StatusCode = StatusCodes.Status401Unauthorized;
                            message += "From OnAuthenticationFailed:\n";
                            message += FlattenException(ctx.Exception);
                            return Task.CompletedTask;
                        ,
                        OnChallenge = ctx =>
                        
                            message += "From OnChallenge:\n";
                            ctx.Response.StatusCode = StatusCodes.Status401Unauthorized;
                            ctx.Response.ContentType = "text/plain";
                            return ctx.Response.WriteAsync(message);
                        ,
                        OnMessageReceived = ctx =>
                        
                            message = "From OnMessageReceived:\n";
                            ctx.Request.Headers.TryGetValue("Authorization", out var BearerToken);
                            if (BearerToken.Count == 0)
                                BearerToken = "no Bearer token sent\n";
                            message += "Authorization Header sent: " + BearerToken + "\n";
                            return Task.CompletedTask;
                        ,
                        OnTokenValidated = ctx =>
                        
                            Debug.WriteLine("token: " + ctx.SecurityToken.ToString());
                            return Task.CompletedTask;
                        
                    ;

                );

            services.AddMvc();
        

配置如下:

        public void Configure(IApplicationBuilder app, IHostingEnvironment env) 
            if (env.IsDevelopment()) 
                app.UseDeveloperExceptionPage();

                IdentityModelEventSource.ShowPII = true;
             else 
                app.UseHsts();
            

            app.UseHttpsRedirection();
            app.UseAuthentication();
            app.UseMvc();
        

我还将此调用添加到 AuthenticationFailed,所以我会知道我的身份验证是否有效:


        Task AuthenticationFailed(AuthenticationFailedContext arg) 
            Console.WriteLine(arg.Exception.Message);
            return Task.FromResult(0);
        

使用我当前的设置,我从服务器收到 401 错误,这是在它遇到 Startup.cs 中连接的 OnChallenge 事件之后。根据上面的链接,这就是在它向用户返回 401 之前被调用的内容,所以看起来服务正在接收正确的令牌并进行身份验证,但也许我没有设置正确的权限?

我不确定从这里去哪里,但如果有任何指导,我们将不胜感激。

编辑:

正如下面评论中提到的,我能够使用通过我的应用程序登录后生成的访问令牌来卷曲我的网站,如下所示:

curl https://mywebsite.azurewebsites.net/api/Values -i --header "Authorization: Bearer [TOKEN]"

这似乎没有问题,所以这似乎与我通过我的应用程序调用控制器的方式有关,而不是身份验证本身。

编辑 2(解决方案):

因此,根据编辑 1,我是正确的,因为这就是我将令牌添加到授权标头的方式。这不是我最聪明的时刻,但我并没有在包含我的访问令牌的声明上调用 .Value。我只是在声明本身上调用 .ToString() ,因此“令牌”实际上是整个声明文本“访问令牌:”。我在调试服务的时候并没有想太多,因为我没有意识到它不应该有那个文本。

一旦我纠正了该问题,服务就会开始按预期工作。

所以,最后,我想这一切都按预期工作。事实上,我没有发送预期的令牌,所以我......未经授权。

根据要求,我必须更改的代码行是:

因此,这不会 100% 适用于大多数人,因为我使用的是一个名为 CSLA 的业务库,但无论如何这个想法都是一样的。

在我的 b2c 调用返回令牌后,我将其存储在 CSLA 库中内置的 ApplicationContext.User.Identity 中。这允许我稍后获得访问令牌声明。重要的是,我将令牌存储在某个地方,以便稍后我想将其添加到授权标头时访问它。

后来,当我使用我的 httpclient 进行调用时,我需要获取该令牌,所以最初,我是这样做的:

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ((ClaimsIdentity)ApplicationContext.User.Identity).Claims.FirstOrDefault(c => c.Type == "AccessToken").ToString() );

这是不正确的。这是发送带有值“访问令牌:[令牌值]”的“令牌”。本质上,它是将“访问令牌”添加到我需要进行身份验证的令牌中,但这是失败的,因为“访问令牌”这个词实际上不应该是您用于身份验证的令牌的一部分。

在我改变我的电话后:

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ((ClaimsIdentity)ApplicationContext.User.Identity).Claims.FirstOrDefault(c => c.Type == "AccessToken").Value强>);

它开始只获取令牌值,当它被添加到授权标头时,它工作得很好。

【问题讨论】:

您可以参考此示例以使用 azure ad b2c 保护您的 api。 docs.microsoft.com/en-us/samples/azure-samples/… 您可以解码访问令牌以检查受众是否是保护您的应用服务的正确受众。 这个问题有什么更新吗? 我正在查看您链接的教程,并尝试将演示中的值替换为我的 b2c 设置的值。当我将演示链接中的 url 替换为具有我需要的值的 URL 时,它告诉我不支持我提供的范围(错误:AADB2C90012)。所以,现在我正在尝试确定这是否是我原始项目中的问题(即范围不好),或者我是否只是错误地设置了演示应用程序。另外,我检查了我的令牌上的观众,它似乎是正确的。 对于它的价值,我能够使用通过我的应用程序登录后生成的访问令牌来卷曲我的网站,如下所示: curl mywebsite.azurewebsites.net/api/Values -i --header "Authorization: Bearer [TOKEN] " 这似乎没有问题,所以这似乎与我通过我的应用程序调用控制器的方式有关,而不是身份验证本身。 【参考方案1】:

Edit 2 解释了我的问题的答案。

我没有将令牌正确添加到授权标头,因此服务无法验证令牌,或者更确切地说,它认为令牌无效。

【讨论】:

以上是关于401 未经授权在 Xamarin.Forms 上使用 Azure b2c的主要内容,如果未能解决你的问题,请参考以下文章

WCF 错误 401 未经授权仅在某些电脑上

Android推送通知未经授权的错误401

Keycloak Kubernetes 401 未经授权

在 bintray 上上传二进制文件时 HTTP/1.1 401 未经授权

AngularJS 中的 401 未经授权的错误处理

Rapidapi & 401 未经授权的响应