简体   繁体   English

c# .NET 6.0 API 使用 Azure AD 令牌(从 Blazor 服务器端调用)返回 401 未授权

[英]c# .NET 6.0 API using Azure AD token (called from Blazor server side) returns 401 unautorized

I have a Blazor server application that uses Azure AD login.我有一个 Blazor 服务器应用程序,它使用 Azure AD 登录。 This works fine, but when setting [Authorize] on my API controller, the controller returns 401 unauthorized event though the Bearer token is passed.这工作正常,但是当在我的 API controller 上设置 [Authorize] 时,controller 返回 401 未授权事件,尽管 Bearer 令牌已传递。

I can see the token being passed in the API, so I am kind of stuck.我可以看到令牌在 API 中传递,所以我有点卡住了。 Problem with config or some code error?配置问题或某些代码错误? Any help would be appreciated.任何帮助,将不胜感激。

Azure config for Blazor project: Blazor 项目的 Azure 配置:

Azure setup for Blazor (server side project): Azure Blazor 设置(服务器端项目):

Azure setup for Blazor (server side project) Azure 设置为 Blazor(服务器端项目)

Auth setup for Blazor: Auth setup for Blazor Blazor 的授权设置: Blazor 的授权设置

Secret added for Blazor: Secret added for Blazor为 Blazor 添加的秘密:为 Blazor 添加的秘密

Api permissions for Blazor: Api permissions for Blazor Api Blazor 权限: Api Blazor 权限

Azure config for API project: API 项目的 Azure 配置:

Azure setup for API: Azure setup for API Azure 设置为 API: Azure 设置为 API

Auth setup for API: Auth setup for API API 的身份验证设置: API 的身份验证设置

API permissions for API (probably too many, found a guide that suggested adding more): API permissions for API (probably too many, found a guide that suggested adding more) API permissions for API (probably tooman, found a guide that suggested adding more): API permissions for API (probably tooman, found a guide that suggested adding more)

Exposed an API for API (type-o in admin.accsess): Exposed an API for API (type-o in admin.accsess) Exposed an API for API (type-o in admin.accsess): Exposed an API for API (type-o in admin.accsess)

Code for blazor project: blazor项目代码:

appsettings.json (new secret will be made and moved to keyvault as soon as this works): ` appsettings.json(一旦这个工作,新的秘密将被制作并移动到 keyvault):`

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "ClientId": "3c                           8c",
    "TenantId": "eb                            a0",
    "Scopes": "api://3c                        8c/admin.accsess" 
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

` `

Startup.cs ` Startup.cs`

public void ConfigureServices(IServiceCollection services)
{
    services.AddMicrosoftIdentityWebAppAuthentication(Configuration)
        .EnableTokenAcquisitionToCallDownstreamApi(new string[] { Configuration["AzureAd.Scopes"] })
        .AddInMemoryTokenCaches();
    
    services.AddHttpContextAccessor();

    services.AddControllersWithViews(options =>
    {
        var policy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build();
        options.Filters.Add(new AuthorizeFilter(policy));
    }).AddMicrosoftIdentityUI();

    services.AddAuthorization(options =>
    {
        // By default, all incoming requests will be authorized according to the default policy
        options.FallbackPolicy = options.DefaultPolicy;
    });
    services.AddRazorPages();
    services.AddServerSideBlazor()
        .AddMicrosoftIdentityConsentHandler();
    
    
    [...]
}

` `

` `

 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

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

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                endpoints.MapBlazorHub();
                endpoints.MapFallbackToPage("/_Host");
            });
        }

` `

Test controller for attempting the api call (always get 401 from api): `测试 controller 以尝试调用 api(始终从 api 获得 401):`

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Diagnostics;
using Microsoft.Identity.Web;
using Newtonsoft.Json.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace S********.Controllers
{
    [Authorize]
    public class TenantController : Controller
    {
        private readonly IHttpClientFactory _clientFactory;
        private readonly ITokenAcquisition _tokenAcquisitionService;
        private HttpClient _client;

        public TenantController(IHttpClientFactory clientFactory, ITokenAcquisition tokenAcquisitionService)
        {
            _clientFactory = clientFactory;
            _tokenAcquisitionService = tokenAcquisitionService;
        }
        public IActionResult Index()
        {
            return View();
        }

        public async Task<IActionResult> GetStuff()
        {
            string[] scopes = { "api://3c*******************c8c/admin.accsess" };
            string accessToken = await _tokenAcquisitionService.GetAccessTokenForUserAsync(scopes);
            
            _client = _clientFactory.CreateClient();
            _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
            var response = await _client.GetAsync("https://localhost:44394/api/SystemConfig/GetByName/System.Tenants");
            return Ok(response);
        }
    }
}

` `

Code for API project: API项目代码:

appsettings.json:应用设置.json:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "custodisas.onmicrosoft.com",
    "TenantId": "eb                      a0",
    "ClientId": "7d                       de",
    "ClientSecret": "G                     5",
    "CallbackPath": "/auth-response"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Startup.cs:启动.cs:

` `

public void ConfigureServices(IServiceCollection services)
{
    var NlogSetup = new SetupLogger();
    NLogConfig = NlogSetup.SetUpLogger("C://Logtest//Skynet//SiloApi.txt").Result;
    LogManager.Configuration = NLogConfig;
    Logger = LogManager.GetLogger("SkynetAPI");
    Logger.Info("Logger up and running");

    services.AddAuthentication(AzureADDefaults.JwtBearerAuthenticationScheme)
        .AddAzureADBearer(options => Configuration.Bind("AzureAd", options));

    // Have tried both with and without scope as required claim
    var scopePolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        //.RequireClaim("scope", "api://3c7*****************c8c/admin.accsess")
        .Build();

    // Have tried both with and without adding scopepolicy as filter
    services.AddMvc(options =>
    {
        //options.Filters.Add(new AuthorizeFilter(scopePolicy));
        options.EnableEndpointRouting = false;
    });

    [...other services...]

    services.AddControllers();
    
    [...other services...]
}

` `

` `

   public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

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

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });

            app.UseMvc(routes =>
            {
                routes.MapRoute(name: "default", template: "api/{controller}/{action}/{id?}");
            });

        }

` `

Controller beind called (returns correct info when removing [Authorize]): ` Controller 被调用(删除 [Authorize] 时返回正确的信息):`

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Web.Resource;
using Newtonsoft.Json;
using NLog;
using Orleans;
using *****.Grains.Interfaces;
using *****.Grains.Interfaces.Core.ApplicationScheduler;
using *****.Shared.Core;
using *****.Shared.Core.ApplicationScheduler;
using *****.Shared.Core.GrainStates;
using *****.Shared.Core.RunApplication;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Identity.Web;
using Microsoft.AspNetCore.Cors;
using System.IdentityModel.Tokens.Jwt;

namespace *****.API.Controllers
{

    [Authorize]
    [Route("api/[controller]")]
    [ApiController]
    public class SystemConfigController : MasterController
    {
        public SystemConfigController(Logger logger, IClusterClient clusterClient) : base(logger, clusterClient)
        {
        }

        [HttpGet]
        [Route("GetByName/{configGrainName}")]
        public async Task<ActionResult<string>> GetSystemConfigByName(string configGrainName)
        {
            // Tried adding this code to get the token, it is passed and I can see it when removing [Authorize]
            var re = Request;
            var headers = re.Headers;
            var tokenString = headers["Authorization"];
            Logger.Info("Token: \n\n" + tokenString + "\n\n");

            var tmpGrain = clusterClient.GetGrain<I*****>(0);
            return JsonConvert.SerializeObject(await tmpGrain.GetSystemGrainByName(configGrainName));
        }
    }
}

` `

MasterController: `主控制器:`

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using NLog;
using Orleans;

namespace *****.API.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class MasterController : ControllerBase
    {
        // TODO: Get from config.
        public Guid SystemID { get; set; } = Guid.Parse("a9****************71c");
        public Logger Logger;
        public IClusterClient clusterClient;

        public MasterController(Logger logger, IClusterClient client)
        {
            Logger = logger;
            clusterClient = client;
        }
    }
}

` `

Hope this is enough info.希望这是足够的信息。 I am completely stuck, so any help would be appreciated.我完全被卡住了,所以任何帮助将不胜感激。 Thank you:)谢谢:)

I tried to reproduce in my environment.我试图在我的环境中重现。

Given API permissions, to the API for you application and granting admin consent to your application.给定 API 权限,向 API 申请并授予管理员同意您的申请。

在此处输入图像描述

  • As you have given two scopes api://43xxxxxxx6b3edf/admin.access , api://438xxxxx-a788-xxxx/user.access in the portal expose an API section, both of them must be given in code.由于您在门户中给出了两个范围api://43xxxxxxx6b3edf/admin.accessapi://438xxxxx-a788-xxxx/user.access公开了一个 API 部分,因此它们都必须在代码中给出。

在此处输入图像描述

  • I tried giving only one scope using postman and got error as invalid scope. This is causing error 401 in your api Access.我尝试使用 postman 仅提供一个 scope,但出现无效 scope 错误。这导致您的 api 访问中出现错误 401。

在此处输入图像描述

***For client_credentials the scope to access your api must be api://<client_id>/.default ***对于 client_credentials scope 访问您的 api 必须是 api://<client_id>/.default

I could get the token successfully after giving the above scope.***给上面的scope后我就可以成功拿到token了。***

在此处输入图像描述

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 从C#调用Yelp Fusion API v3会返回401未经授权,并出现TOKEN_MISSING错误 - Yelp Fusion API v3 returns 401 Unauthorized with TOKEN_MISSING error when called from c# Azure AD - 在 C# Web API 中使用来自 Angular SPA 的图 API 访问令牌 - Azure AD - Using Graph API access token from Angular SPA in C# Web API 使用 Azure AD 验证返回 401 - Sending a GET request to an ASP.NET Core Web Application API using Azure AD Authentication returns 401 如何在 C# ASP.NET Core 6.0 中从 Azure AD 检索用户拥有的所有组? - How to retrieve all the groups that a user has from Azure AD in C# ASP.NET Core 6.0? AD登录给token,但是API返回401 - AD login gives a token, but API returns 401 Blazor 服务器端和 Azure B2C 使用邀请登录,如何使用返回的令牌进行身份验证? - Blazor Server Side and Azure B2C Login using invite, how to authenticate with returned token? 无法使用 Blazor 服务器应用程序调用安全下游 Web API 使用 Azure AD B2C - Cannot get a Blazor Server App to call a secure downstream Web API using Azure AD B2C 如何将Daemon或Server的Azure AD OAuth2访问令牌和刷新令牌转换为C#ASP.NET Web API - How do I get Azure AD OAuth2 Access Token and Refresh token for Daemon or Server to C# ASP.NET Web API Web API (.NET Framework) Azure AD 身份验证始终返回 401 Unauthorized - Web API (.NET Framework) Azure AD Authentication always returns 401 Unauthorized 将 blob 从一个 Azure 容器复制到另一个使用 ASP.NET Core 6.0 MVC 和 C# - Copy blobs from one Azure container to another using ASP.NET Core 6.0 MVC with C#
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM