不记名令牌失败 - 关联失败 - 未找到 Cookie

Bearer Token Fails - Correlation Failed - Cookie Not Found

我正在尝试使用 Angular 前端和核心 MVC API 实现 Microsoft 的 MSAL。

我已经能够在 Angular 应用程序上成功进行身份验证并调用 Graph API 端点。 Angular 应用程序使用以下代码:

app.module.ts

@NgModule({
    declarations: [...],
    imports: [
        ...
        MsalModule.forRoot(
            new PublicClientApplication({
                auth: {
                    clientId: '47...',
                    authority: 'https://login.microsoftonline.com/6d...',
                    redirectUri: 'https://localhost:4200',
                },
                cache: {
                    cacheLocation: 'localStorage',
                    storeAuthStateInCookie: false
                },
            }),
            {
                interactionType: InteractionType.Redirect,
                authRequest: { scopes: ['user.read'] },
            },
            {
                interactionType: InteractionType.Redirect,
                protectedResourceMap: new Map([
                    ['https://graph.microsoft.com/v1.0/me', ['user.read']],
                    ['https://localhost:44333/Auth/Index', ['user.read']]
                ]),
            },
        ),
        RouterModule,
    ],
    providers: [{
        provide: HTTP_INTERCEPTORS,
        useClass: MsalInterceptor,
        multi: true,
    }],
    bootstrap: [AppComponent, MsalRedirectComponent],
})
export class AppModule {}

我可以通过将 HttpClient 与配置的 MsalInterceptor

一起使用来成功调用图 API
this.http.get('https://graph.microsoft.com/v1.0/me')
    .subscribe(profile => { this.profile = profile; });

接下来我尝试用类似的代码调用我自己的 API

this.http.get('https://localhost:44333/Auth/Index')
    .subscribe(token => { this.token = token; });

但是,此请求失败并出现以下错误:

An unhandled exception occurred while processing the request.
    Exception: Correlation failed.
    Unknown location

Exception: An error was encountered while handling the remote login.
    Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler.HandleRequestAsync()

我假设问题出在我的 API 上。我在我的 .Net 5.0 MVC API 中配置了身份验证,如下所示:

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
  services.AddCors();
  services.AddOptions();

  services.AddMvc()
   .SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
   .AddNewtonsoftJson(x => x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);

  services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
   .AddMicrosoftIdentityWebApp(Configuration)
   .EnableTokenAcquisitionToCallDownstreamApi(new[] { "user.read" })
   .AddInMemoryTokenCaches();
}

appsettings.json

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com",
    "TenantId": "6d...",
    "ClientId": "47...",
    "ClientSecret": "..."
  },
}

AuthController.cs

namespace Auth.Services
{
  [Route("{controller}/{action}")]
  [Authorize]
  public class AuthController : Controller
  {
    private readonly ITokenAcquisition tokenAcquisition;
    public AuthController(ITokenAcquisition tokenAcquisition) => this.tokenAcquisition = tokenAcquisition;

    [HttpGet]
    public async Task<IActionResult> Index()
    {
      var scopes = new string[] { "user.read" };
      var accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(scopes);
      //...
      return Ok(...);
    }
  }
}

应用程序似乎从未到达控制器。 None 的断点曾经被击中,当我检查事件时,抛出了两个异常:

Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler: Warning: '.AspNetCore.Correlation.XXX' cookie not found.    
System.Exception: An error was encountered while handling the remote login.
 ---> System.Exception: Correlation failed.
   --- End of inner exception stack trace ---
   at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1.HandleRequestAsync()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context) 

我感兴趣的部分是“未找到 cookie”异常。我发送的不是 cookie,而是 Bearer Token。

我的猜测是我的 API 配置中缺少某些东西会告诉中间件查找 Bearer 令牌而不是 cookie。但是,当我尝试将中间件配置为使用 AddJwtBearer(...) 时,我遇到了一系列全新的错误。所以问题是,要从 Angular 前端获取身份验证 MSAL 令牌以在 API 后端工作,我缺少什么?

@weichch 的评论让我走上了正确的道路,但是这个例子使用了过时的代码。我需要做的就是将我的 Startup.cs 文件更改为类似于以下内容:

public void ConfigureServices(IServiceCollection services)
{
  services.AddCors();
  services.AddOptions();

  services.AddMvc()
   .SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
   .AddNewtonsoftJson(x => x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);

  // Note: the middleware is Api not App... I accidentally called App, and it took
  // me a minute to realize my mistake.
  services.AddMicrosoftIdentityWebApiAuthentication(Configuration);
  services.AddControllers();
}

我的 Angular 申请中也有错误需要更正。我的 API 的范围需要更改为我在 Azure 门户 'api://47.../access_as_user' 中注册的 API 在更改范围之前,我收到“Bearer error=invalid_token” , error_description="签名无效""错误。因此,app.module.ts 类似于以下内容:

@NgModule({
    declarations: [...],
    imports: [
        ...
        MsalModule.forRoot(
            new PublicClientApplication({
                auth: {
                    clientId: '47...',
                    authority: 'https://login.microsoftonline.com/6d...',
                    redirectUri: 'https://localhost:4200',
                },
                cache: {
                    cacheLocation: 'localStorage',
                    storeAuthStateInCookie: false
                },
            }),
            {
                interactionType: InteractionType.Redirect,
                // !!! Changed Here !!!
                authRequest: { scopes: ['user.read', 'api://47.../access_as_user'] },
            },
            {
                interactionType: InteractionType.Redirect,
                protectedResourceMap: new Map([
                    ['https://graph.microsoft.com/v1.0/me', ['user.read']],
                    // !!! Changed Here !!!
                    ['https://localhost:44333/Auth/Index', ['api://47.../access_as_user']]
                ]),
            },
        ),
        RouterModule,
    ],
    providers: [{
        provide: HTTP_INTERCEPTORS,
        useClass: MsalInterceptor,
        multi: true,
    }],
    bootstrap: [AppComponent, MsalRedirectComponent],
})
export class AppModule {}