在 Dynamics 365 资源中为 Auth/Authz 使用 MSAL 以访问外部 Azure API

Using MSAL for Auth/Authz in a Dynamics 365 resource to access external Azure API

我有一个 Angular 应用程序部署为 D365 中的 Web 资源,使用 MSAL 库 Auth/Auhtz 访问 Azure 中托管的外部 Web API。当 运行 Dynamics 之外的应用程序一切正常时,MSAL 库检索访问令牌并将其附加到对 Azure Web 的授权请求 API。当我将文件作为 Web 资源部署到 Dynamics 时会出现问题。下面是我的应用模块中的错误和 MSAL 配置。

MSAL 日志记录:2020 年 10 月 16 日,星期五 13:14:53 GMT:943d26e4-f4b2-48b7-a882-f1a835959476-1.4.0-Error 获取范围令牌时出错:https ://...azurewebsites.net/default ClientAuthError:需要用户登录。对于静默调用,请求必须包含 sid 或 login_hint

MsalModule.forRoot({
      auth: {
        clientId: config.auth.clientId,
        authority: config.auth.authority,
        redirectUri: config.auth.redirectUri
      },
      cache: {
        cacheLocation: <CacheLocation>config.cache.cacheLocation,
        storeAuthStateInCookie: isIE, // set to true for IE 11
      },
    },
      {
        popUp: !isIE,
        consentScopes: config.scopes.graphScopes,
        unprotectedResources: [],
        protectedResourceMap: [
          [config.baseuri, [config.baseuri.concat('/', config.scopes.gatewayScopes)]],
          [config.protectedResources.graphEndpoint, config.scopes.graphScopes]
        ],
        extraQueryParameters: {},        
      })

 providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: MsalInterceptor,
      multi: true
    }
]

我已经苦苦挣扎了2天了,一点运气都没有。

求助!!!

试试下面的自定义拦截器。为此,您需要启用 sid optional claim.

@Injectable()
export class CustomInterceptor implements HttpInterceptor {
  constructor(private auth: MsalService, private broadcastService: BroadcastService) { }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const scopes = this.auth.getScopesForEndpoint(req.url);
    this.auth.getLogger().verbose("Url: " + req.url + " maps to scopes: " + scopes);

    // If there are no scopes set for this request, do nothing.
    if (!scopes) {
      return next.handle(req);
    }

    let token: string;

    // Acquire a token for this request, and attach as proper auth header.
    const obs = from(
      this.auth.acquireTokenSilent({ scopes, sid: this.auth.getAccount().idTokenClaims["sid"] })
        .then((response: AuthResponse) => {
          token = response.tokenType === ServerHashParamKeys.ID_TOKEN ? response.idToken.rawIdToken : response.accessToken;
          const authHeader = `Bearer ${token}`;
          return req.clone({
            setHeaders: {
              Authorization: authHeader,
            }
          });
        })
    )
      .pipe(
        mergeMap((nextReq: HttpRequest<any>) => next.handle(nextReq)),
        tap(
          event => { }, // tslint:disable-line
          err => {
            if (err instanceof HttpErrorResponse && err.status === 401) {
              this.auth.clearCacheForScope(token);
              this.broadcastService.broadcast("msal:notAuthorized", err.message);
            }
          }
        )
    );

    return obs as Observable<HttpEvent<any>>;
    
  }
}