提供了 getaccesstoken 身份验证方案名称,但说不是

问题描述 投票:0回答:1

在 Blazor 组件中,我想调用目前托管在同一应用程序中的 API。在我添加身份验证和授权之前,这一直有效。从我的浏览器调用 API 工作正常。当我尝试调用相同的 API 时,返回的响应内容是 Microsoft OIDC 登录的 HTML。

当我尝试让用户访问令牌作为不记名令牌传递时,我收到一条错误消息

System.InvalidOperationException: IDW10503: Cannot determine the cloud Instance. The provided authentication scheme was 'MicrosoftOidc'. Microsoft.Identity.Web inferred 'MicrosoftOidc' as the authentication scheme. Available authentication schemes are 'MicrosoftOidc,Bearer,Cookies,OpenIdConnect'. See https://aka.ms/id-web/authSchemes. 

所以它说提供的身份验证方案是MicrosoftOidc,被推断为MicrosoftOidc,并且可用的方案包括Microsoft Oidc。我的用户登录并获得分配的角色。我在这一行收到此错误

var token = await tokenAcquisition.GetAccessTokenForUserAsync(new[] { "Spec.Read" }, authenticationScheme: "MicrosoftOidc");

这是我在程序中设置身份验证的方法。cs

builder.Services.AddAuthentication("MicrosoftOidc")
    .AddOpenIdConnect("MicrosoftOidc", oidcOptions =>
    {
        oidcOptions.Scope.Add(OpenIdConnectScope.OfflineAccess);
        oidcOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        oidcOptions.SaveTokens = true;
        oidcOptions.Authority = builder.Configuration["AzureAd:Instance"];
        oidcOptions.ClientId = builder.Configuration["AzureAd:ClientId"];
        oidcOptions.ClientSecret = builder.Configuration["AzureAd:ClientSecret"];
        oidcOptions.ResponseType = OpenIdConnectResponseType.Code;
        oidcOptions.MapInboundClaims = false;
        oidcOptions.TokenValidationParameters.NameClaimType = JwtRegisteredClaimNames.Name;
        oidcOptions.TokenValidationParameters.RoleClaimType = "roles";
        oidcOptions.TokenValidationParameters.IssuerValidator = AadIssuerValidator.GetAadIssuerValidator(
            oidcOptions.Authority,
            oidcOptions.Backchannel).Validate;
    }).AddJwtBearer()
    .AddMicrosoftIdentityWebApp(builder.Configuration)
    .EnableTokenAcquisitionToCallDownstreamApi(builder.Configuration.GetValue<string[]>("DownstreamApi:Scopes"))
    .AddInMemoryTokenCaches();

var map = JsonWebTokenHandler.DefaultInboundClaimTypeMap;

builder.Services.AddAuthorization();

builder.Services.AddCascadingAuthenticationState();

builder.Services.AddRazorPages().AddMvcOptions(options=>
{
    var policy = new AuthorizationPolicyBuilder()
                    .RequireAuthenticatedUser()
                    .Build();
    options.Filters.Add(new AuthorizeFilter(policy));
}).AddMicrosoftIdentityUI();

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost;

    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();

    options.AllowedHosts = builder.Configuration.GetSection("AllowedHosts").Get<List<string>>();
});

builder.Services.AddServerSideBlazor();
builder.Services.AddControllers();

...
var app = builder.Build();

app.UseRewriter(
    new RewriteOptions().Add(context =>
    {
        if (context.HttpContext.Request.Path == "/signout-oidc")
            context.HttpContext.Response.Redirect("https://Corrugated.io");
    }));

app.UseForwardedHeaders();
app.UseHttpsRedirection();
app.UseDefaultFiles();
app.UseStaticFiles();

app.UseRouting();
app.MapControllers();

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

app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

app.Run();

这是相关应用程序设置

{
"AzureAd": {
  "Instance": "https://login.microsoftonline.com/common/",
  "TenantId": "be664a41-9b40-4c81-b11e-e52879b29fe9",
  "ClientId": "f75fae40-5787-48f4-8834-7354c224e530",
  "CallbackPath": "/signin-oidc",
  "ClientSecret": "super secret"
},

"DownstreamApi": {
  "BaseUrl": "https://graph.microsoft.com/v1.0",
  "RelativePath": "me",
  "Scopes": [
    "user.read"
  ]
}
}

我在这里做错了什么?访问链接的又名站点并不能真正帮助我,因为它以一种有点不同的方式配置身份验证,但差别不大,基本上只是传递默认值。

此外,我可能尝试以错误的方式调用 API。我的其他组件之一正在对我的另一个 API 控制器进行 js 互操作调用,并且经过了正确的身份验证。我应该这样做吗?我认为传递不记名令牌会更正确。特别是如果我稍后要尝试突破 API。

c# asp.net-core azure-active-directory blazor openid-connect
1个回答
0
投票

首先,我不太确定

my API which is hosted in the same application
,因为我们可以看到
builder.Services.AddServerSideBlazor();
中有
Program.cs
,所以这是一个带有 .net 8 .net 8 使用 blazor web 应用程序来替换 blazor 服务器,所以恐怕您正在将旧的 blazor 服务器应用程序迁移到 .net 8,但不喜欢使用 blazor Web 应用程序功能,但这对于此问题并不重要。因此,我们现在有一个具有 UI 的应用程序,并且 UI 在服务器端呈现,并且我们在同一项目中公开了 API。现在我们也在项目中调用API。这让我很困惑。

让我们回到您的代码和错误。首先,我们现在使用 MSAL 库进行 Microsoft 身份验证。因此,我们应在 blazor 服务器应用程序中使用

AddMicrosoftIdentityWebAppAuthentication
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme).AddMicrosoftIdentityWebApp
代替
AddAuthentication("MicrosoftOidc").AddOpenIdConnect()
。这里我们有一些针对不同场景的官方示例

然后我注意到您的 appsettings.json 中有

"BaseUrl": "https://graph.microsoft.com/v1.0",
,所以我担心您正在尝试在 blazor 服务器应用程序中调用 MS Graph API,那么步骤应该是 1. 使用 MS 帐户登录您的应用程序 - > 2. 通过 tokenAcquisition 生成访问令牌 -> 3. 调用 API url 获取响应或 2. 使用 Graph SDK 调用 Graph API 并获取响应。您收到的错误意味着身份验证出现问题。我将向您展示我在我身边测试的样本。

程序.cs

using BlazorServerGraph5.Data;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi"))
    .AddInMemoryTokenCaches();
builder.Services.AddControllersWithViews()
    .AddMicrosoftIdentityUI();


builder.Services.AddAuthorization(options =>
{
    // By default, all incoming requests will be authorized according to the default policy
    options.FallbackPolicy = options.DefaultPolicy;
});

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor()
    .AddMicrosoftIdentityConsentHandler();
builder.Services.AddSingleton<WeatherForecastService>();

var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.MapControllers();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();

FetchData.razor

@page "/fetchdata"
@using BlazorServerGraph5.Data
@inject WeatherForecastService ForecastService
@using Microsoft.Graph
@using Microsoft.Graph.Models
@inject GraphServiceClient graphServiceClient

<PageTitle>Weather forecast</PageTitle>
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from a service.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Temp. (C)</th>
                <th>Temp. (F)</th>
                <th>Summary</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast.Date.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.TemperatureF</td>
                    <td>@forecast.Summary</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    private WeatherForecast[]? forecasts;

    protected override async Task OnInitializedAsync()
    {
        var me = await graphServiceClient.Me.GetAsync();
        forecasts = await ForecastService.GetForecastAsync(DateOnly.FromDateTime(DateTime.Now));
    }
}

应用程序设置.json

"AzureAd": {
  "Instance": "https://login.microsoftonline.com/",
  "Domain": "tenant_id",
  "TenantId": "tenant_id",
  "ClientId": "client_id",
  "CallbackPath": "/signin-oidc",
  "ClientSecret": "client_secret"//authentication Graph SDK requires client secret
},
"DownstreamApi": {
  "BaseUrl": "https://graph.microsoft.com/v1.0",
  "Scopes": "User.ReadWrite.All"
},

nuget 包

<ItemGroup>
  <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.13" NoWarn="NU1605" />
  <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.13" NoWarn="NU1605" />
  <PackageReference Include="Microsoft.Graph" Version="5.40.0" />
  <PackageReference Include="Microsoft.Graph.Core" Version="3.1.6" />
  <PackageReference Include="Microsoft.Identity.Web" Version="2.16.1" />
  <PackageReference Include="Microsoft.Identity.Web.GraphServiceClient" Version="2.16.1" />
  <PackageReference Include="Microsoft.Identity.Web.UI" Version="2.16.1" />
</ItemGroup>

创建登录部分组件

<AuthorizeView>
    <Authorized>
        Hello, @context.User.Identity?.Name!
        <a href="MicrosoftIdentity/Account/SignOut">Log out</a>
    </Authorized>
    <NotAuthorized>
        <a href="MicrosoftIdentity/Account/SignIn">Log in</a>
    </NotAuthorized>
</AuthorizeView>

并将其放入MainLayout

<div class="top-row px-4 auth">
    <LoginDisplay />
    <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
</div>

在 App.razor 中,用

<CascadingAuthenticationState>

包围
<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(App).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
            <FocusOnNavigate RouteData="@routeData" Selector="h1" />
        </Found>
        <NotFound>
            <PageTitle>Not found</PageTitle>
            <LayoutView Layout="@typeof(MainLayout)">
                <p role="alert">Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>
© www.soinside.com 2019 - 2024. All rights reserved.