简体   繁体   English

如何配置.net内核angular azure AD认证?

[英]How to configure .net core angular azure AD authentication?

I'm currently working on Azure AD authenticaton integration to Angular -.Net core 3.1 project.我目前正在研究 Azure AD 身份验证集成到 Angular -.Net core 3.1 项目。 This is a project which was generated from Visual Studio 2019 template (ASP.NET Core Web App).这是一个从 Visual Studio 2019 模板(ASP.NET Core Web App)生成的项目。
In Azure portal, I registered 2 application and configured by MS tutorial and this .在 Azure 门户中,我注册了 2 个应用程序并通过MS 教程配置。

Two registed app:两个注册的应用程序:

  1. frontend_app (client id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx16e3) frontend_app(客户端ID:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx16e3)
  2. backend_api (client id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxfcc1) backend_api(客户端ID:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxfcc1)

But I published only one App service, which contains both SPA and API.但是我只发布了一个App服务,其中包含SPA和API。 After login, I got a token, which append to every api call with MSAL interceptor.登录后,我得到一个令牌,它是 append 到每个 api 调用与 MSAL 拦截器。

The problem is all of the calls return is: 401, due to 'audience is invalid'.问题是所有的调用返回都是:401,由于“观众无效”。 in the auth token the audience value the client id of frontend_app.在 auth 令牌中,受众重视 frontend_app 的客户端 ID。

How can I solve to accept the the audience?我该如何解决才能接受观众? Is it correct to use 2 app registration for only one app service?仅对一项应用服务使用 2 个应用注册是否正确?

I was having the same problem as you and believe I have come up with a solution.我和你有同样的问题,相信我已经想出了一个解决方案。 All the guides I originally followed were using the implicit flow.我最初遵循的所有指南都使用隐式流程。 As Carl pointed out in his answer (which I don't believe properly addresses your issue), there's an auth flow which is the recommended way to go.正如卡尔在他的回答中指出的那样(我认为这不能正确解决您的问题),有一个身份验证流程,这是 go 的推荐方式。 Unfortunately the standard MSAL libraries from all the samples and guides are 1.x and don't support auth flow.不幸的是,所有示例和指南中的标准 MSAL 库都是 1.x,不支持身份验证流程。 Instead, you'll need to use MSAL.js 2.0 .相反,您需要使用MSAL.js 2.0 The catch is that the angular library is still in alpha问题是 angular 库仍处于alpha阶段

So, here's what I did to make it all work.所以,这就是我所做的一切。 I'm using an Angular 10 front-end with an ASP.NET Core 3.1 backend.我正在使用 Angular 10 前端和 ASP.NET Core 3.1 后端。

First, you create your backend api app registration (which you may not need to change).首先,您创建后端 api 应用注册(您可能不需要更改)。 Here's the documentation for that: Register Web API .这是相关的文档: 注册 Web API Important notes:重要笔记:

  • Using this method you do NOT need to add your front-end client id as an authorized application under the 'Expose an API' section.使用此方法,您无需将前端客户端 ID 添加为“公开 API”部分下的授权应用程序。 We'll handle that differently using auth flow.我们将使用身份验证流程以不同方式处理。
  • No redirect URI is needed since your backend will not be logging the user in不需要重定向 URI,因为您的后端不会让用户登录
  • You need at least one scope for everything to work您至少需要一个 scope 才能正常工作

Then follow the MSAL.js 2.0 documentation to create the frontend app registration.然后按照MSAL.js 2.0文档创建前端应用注册。 The important notes are as follows:重要说明如下:

  • Make sure you select the SPA platform and enter a valid redirect URI确保您的 SPA 平台 select 并输入有效的重定向 URI
  • DO NOT check the boxes for 'Implicit Grant'不要选中“隐式授予”复选框
  • Under 'API permissions', give your front-end application access to your backend api:在“API 权限”下,让您的前端应用程序访问您的后端 api:
    • Under 'API permissions' click on 'Add permission', then click on the 'My APIs' tab在“API 权限”下单击“添加权限”,然后单击“我的 API”选项卡
    • Find your backend application and select the appropriate scope.找到您的后端应用程序和 select 和相应的 scope。
    • Click 'Add permissions'点击“添加权限”
    • Optionally grant consent for your APIs (可选)为您的 API 授予同意

Here's what your app registrations should look similar to:您的应用注册应类似于以下内容:

backend app registration expose an api后端应用程序注册公开 api

frontend app registration authentication前端应用注册认证

frontend app registration api permissions前端应用注册 api 权限

Now for the code.现在是代码。 For your angular app, first install the necessary modules:对于您的 angular 应用程序,首先安装必要的模块:

npm install @azure/msal-browser @azure/msal-angular@alpha

Then add this to your app module:然后将其添加到您的应用程序模块中:

import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { tap } from 'rxjs/operators';
import {
  IPublicClientApplication,
  PublicClientApplication,
  InteractionType,
  BrowserCacheLocation,
  LogLevel,
} from '@azure/msal-browser';
import {
  MsalGuard,
  MsalInterceptor,
  MsalBroadcastService,
  MsalInterceptorConfiguration,
  MsalModule,
  MsalService,
  MSAL_GUARD_CONFIG,
  MSAL_INSTANCE,
  MSAL_INTERCEPTOR_CONFIG,
  MsalGuardConfiguration,
} from '@azure/msal-angular';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

const PROTECTED_RESOURCE_MAP: Map<string, Array<string>> = new Map([
  ['https://graph.microsoft.com/v1.0/me', ['user.read']],
  [
    'api/admin/users',
    ['api://<backend app id>/access_as_admin'],
  ],
]);

const IS_IE =
  window.navigator.userAgent.indexOf('MSIE ') > -1 ||
  window.navigator.userAgent.indexOf('Trident/') > -1;

export function loggerCallback(logLevel, message) {
  console.log(message);
}

export function MSALInstanceFactory(): IPublicClientApplication {
  return new PublicClientApplication({
    auth: {
      clientId: '<frontend app id>',
      authority:
        'https://login.microsoftonline.com/<azure ad tenant id>',
      redirectUri: 'http://localhost:4200',
      postLogoutRedirectUri: 'http://localhost:4200/#/logged-out',
    },
    cache: {
      cacheLocation: BrowserCacheLocation.LocalStorage,
      storeAuthStateInCookie: IS_IE, // set to true for IE 11
    },
    system: {
      loggerOptions: {
        loggerCallback,
        logLevel: LogLevel.Verbose,
        piiLoggingEnabled: false,
      },
    },
  });
}

export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
  return {
    interactionType: InteractionType.Redirect,
    protectedResourceMap: PROTECTED_RESOURCE_MAP,
  };
}

export function MSALGuardConfigFactory(): MsalGuardConfiguration {
  return {
    interactionType: InteractionType.Redirect,
  };
}

export function initializeApp(appConfig: AppConfigService) {
  const promise = appConfig
    .loadAppConfig()
    .pipe(tap((settings: IAppConfig) => {}))
    .toPromise();
  return () => promise;
}

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    AppRoutingModule,
    HttpClientModule,
    MsalModule,
  ],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: MsalInterceptor,
      multi: true,
    },
    {
      provide: MSAL_INSTANCE,
      useFactory: MSALInstanceFactory,
    },
    {
      provide: MSAL_GUARD_CONFIG,
      useFactory: MSALGuardConfigFactory,
    },
    {
      provide: MSAL_INTERCEPTOR_CONFIG,
      useFactory: MSALInterceptorConfigFactory,
    },
    MsalService,
    MsalGuard,
    MsalBroadcastService,
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

Then you can simply toss the MsalGuard onto any route you want to protect.然后,您可以简单地将MsalGuard扔到您想要保护的任何路线上。

For the backend, first install the Microsoft.Identity.Web package:对于后端,首先安装 Microsoft.Identity.Web package:

dotnet add package Microsoft.Identity.Web --version 1.3.0

Here's the relevant code in my Startup.cs:这是我的 Startup.cs 中的相关代码:

public void ConfigureServices(IServiceCollection services)
{
  // other stuff...
  services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(options =>
    {
      Configuration.Bind("AzureAd", options);
    })
    .AddInMemoryTokenCaches();

  services.AddCors((options =>
  {
    options.AddPolicy("FrontEnd", builder =>
      builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
  }));
  // other stuff...
}
 
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
  // other stuff...
  app.UseCors("FrontEnd");
  app.UseAuthentication();
  app.UseAuthorization();
  // other stuff...
}

appsettings.json contains: appsettings.json 包含:

"AzureAd": {
  "Instance": "https://login.microsoftonline.com/",
  "Domain": "<azure ad domain>",
  "TenantId": "<azure ad tenant id>",
  "ClientId": "<backend app id>"
}

As I said in the comment, the 401 error is usually because your audience does not match the api you want to call, so you need to make sure that your target audience is your api, in your question it should be your backend_api , I Use auth code flow to make a simple demonstration for you:正如我在评论中所说,401错误通常是因为您的受众与您要调用的 api 不匹配,因此您需要确保您的目标受众是您的 api,在您的问题中它应该是您的backend_api ,我使用auth 代码流为您做一个简单的演示:

在此处输入图像描述

Get token:获取令牌: 在此处输入图像描述

Parse the token:解析令牌:

在此处输入图像描述

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM