0
votes

I have an Angular application that is deployed as a Web Resource in D365 using MSAL library for Auth/Auhtz to access an external Web API hosted in Azure. When running the app outside Dynamics all works fine, the MSAL library retrieves the acccess token and attaches it to the request for Authorization to the Azure Web API. The problem occurs when I deploy the files as web resources to Dynamics. See below is the error and MSAL config in my app module.

MSAL Logging: Fri, 16 Oct 2020 13:14:53 GMT:943d26e4-f4b2-48b7-a882-f1a835959476-1.4.0-Error Error when acquiring token for scopes: https://...azurewebsites.net/default ClientAuthError: User login is required. For silent calls, request must contain either sid or 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
    }
]

I've been struggling for 2 days now without any luck.

Help Please!!!

1
can you send code of app.component.ts with msal service callPiva Gregory

1 Answers

0
votes

Try the following custom interceptor. For this you need to enable the 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>>;
    
  }
}