Description
Core Library
MSAL.js (@azure/msal-browser)
Core Library Version
4.11.0
Wrapper Library
MSAL Angular (@azure/msal-angular)
Wrapper Library Version
4.0.10
Public or Confidential Client?
Public
Description
New angular application using Module Federation provides route guard using msal however any api calls return a 401 unauthorised error. When the same code is used in an angular application that does not use Module Federation, the route guard and the api calls authenticate as expected. I am using MSAL and angular 19 and have tried this a number of times (creating new applications each time). My application is authenticating using b2c.
Error Message
HttpErrorResponse {headers: _HttpHeaders, status: 401, statusText: 'OK', url: 'https://localhost:55001/WeatherForecast', ok: false, …}
error
:
null
headers
:
_HttpHeaders {headers: undefined, normalizedNames: Map(0), lazyUpdate: null, lazyInit: ƒ}
message
:
"Http failure response for https://localhost:55001/WeatherForecast: 401 OK"
name
:
"HttpErrorResponse"
ok
:
false
status
:
401
statusText
:
"OK"
type
:
undefined
url
:
"https://localhost:55001/WeatherForecast"
[[Prototype]]
:
HttpResponseBase
MSAL Logs
No log is produced. A log is produced when not using Module Federation
Network Trace (Preferrably Fiddler)
- Sent
- Pending
MSAL Configuration
adb2cConfig: {
logOutput: true,
clientId: 'clientId',
readScopeUrl: 'https://scpusers.onmicrosoft.com/weather/api/Weather.Read',
writeScopeUrl: 'https://scpusers.onmicrosoft.com/weather/api/Weather.Write',
apiEndpointUrls: [
currentApiServerUrl + 'WeatherForecast'
],
authorities: {
signUpSignIn: {
authority: 'https://scpusers.b2clogin.com/scpusers.onmicrosoft.com/b2c_1_si'
},
resetPassword: {
authority: 'https://scpusers.b2clogin.com/scpusers.onmicrosoft.com/b2c_1_reset'
},
editProfile: {
authority: "https://scpusers.b2clogin.com/scpusers.onmicrosoft.com/b2c_1_edit"
}
}
}
Relevant Code Snippets
import { ApplicationConfig, importProvidersFrom, provideZoneChangeDetection } from '@angular/core';
import { provideRouter, withDisabledInitialNavigation, withEnabledBlockingInitialNavigation } from '@angular/router';
import { provideNoopAnimations } from '@angular/platform-browser/animations';
import { routes } from './app.routes';
import { BrowserCacheLocation, BrowserUtils, Configuration, InteractionType, IPublicClientApplication, LogLevel, PublicClientApplication } from '@azure/msal-browser';
import { environment } from '../environments/environment';
import { MSAL_GUARD_CONFIG, MSAL_INSTANCE, MSAL_INTERCEPTOR_CONFIG, MsalBroadcastService, MsalGuard, MsalGuardConfiguration, MsalInterceptor, MsalInterceptorConfiguration, MsalService, ProtectedResourceScopes } from '@azure/msal-angular';
import { BrowserModule } from '@angular/platform-browser';
import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
const isIE = window.navigator.userAgent.indexOf("MSIE ") > -1 || window.navigator.userAgent.indexOf("Trident/") > -1;
export const b2cPolicies = {
names: {
signUpSignIn: 'B2C_1_si',
resetPassword: 'B2C_1_reset',
editProfile: 'B2C_1_edit',
},
authorities: {
signUpSignIn: {
authority: 'https://scpusers.b2clogin.com/scpusers.onmicrosoft.com/B2C_1_si',
},
resetPassword: {
authority: 'https://scpusers.b2clogin.com/scpusers.onmicrosoft.com/B2C_1_reset',
},
editProfile: {
authority: 'https://scpusers.b2clogin.com/scpusers.onmicrosoft.com/B2C_1_edit',
},
},
authorityDomain: 'scpusers.b2clogin.com',
};
export const msalConfig: Configuration = {
auth: {
clientId: environment.adb2cConfig.clientId, // This is the ONLY mandatory field that you need to supply.
authority: b2cPolicies.authorities.signUpSignIn.authority, // Defaults to "https://login.microsoftonline.com/common"
knownAuthorities: [b2cPolicies.authorityDomain], // Mark your B2C tenant's domain as trusted.
redirectUri: '/', // Points to window.location.origin by default. You must register this URI on Azure portal/App Registration.
postLogoutRedirectUri: '/', // Points to window.location.origin by default.
},
cache: {
cacheLocation: BrowserCacheLocation.LocalStorage, // Configures cache location. "sessionStorage" is more secure, but "localStorage" gives you SSO between tabs.
storeAuthStateInCookie: isIE, // Set this to "true" if you are having issues on IE11 or Edge. Remove this line to use Angular Universal
},
system: {
/**
* Below you can configure MSAL.js logs. For more information, visit:
* https://docs.microsoft.com/azure/active-directory/develop/msal-logging-js
*/
loggerOptions: {
loggerCallback(logLevel: LogLevel, message: string) {
if (environment.adb2cConfig.logOutput) {
console.log(message);
}
},
logLevel: LogLevel.Verbose,
piiLoggingEnabled: false
}
}
}
export const loginRequest = {
scopes: []
};
export function MSALInstanceFactory(): IPublicClientApplication {
return new PublicClientApplication(msalConfig);
}
export function MSALGuardConfigFactory(): MsalGuardConfiguration {
return {
interactionType: InteractionType.Redirect,
authRequest: loginRequest
};
}
export function protectedResources(): any[] {
let ret: any[] = [];
environment.adb2cConfig.apiEndpointUrls.forEach(endpoint => {
let api = {
api: {
endpoint: endpoint,
scopes: {
read: [environment.adb2cConfig.readScopeUrl],
write: [environment.adb2cConfig.writeScopeUrl]
}
}
};
ret.push(api);
});
return ret;
}
export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
const protectedResourceMap = new Map<string, Array<string | ProtectedResourceScopes> | null>();
protectedResources().forEach(resource => {
protectedResourceMap.set(resource.api.endpoint, [
{
httpMethod: 'GET',
scopes: [...resource.api.scopes.read]
},
{
httpMethod: 'POST',
scopes: [...resource.api.scopes.write]
},
{
httpMethod: 'PUT',
scopes: [...resource.api.scopes.write]
},
{
httpMethod: 'PATCH',
scopes: [...resource.api.scopes.write]
},
{
httpMethod: 'DELETE',
scopes: [...resource.api.scopes.write]
}
]);
});
return {
interactionType: InteractionType.Redirect,
protectedResourceMap
};
}
const initialNavigation = !BrowserUtils.isInIframe() && !BrowserUtils.isInPopup()
? withEnabledBlockingInitialNavigation() // Set to enabledBlocking to use Angular Universal
: withDisabledInitialNavigation();
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes, initialNavigation),
importProvidersFrom(BrowserModule),
provideNoopAnimations(),
provideHttpClient(withInterceptorsFromDi()),
{
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
]
};
Reproduction Steps
- Create new angular workspace.
- Add shell application
- Install native federation npm
i @angular-architects/native-federation -D
- Activate native federation for each application
- Configure project for MSAL using an existing configuration that works in projects that do not use Module Federation
- Log in to http://localhost:4200
- Access page that makes call to b2c protected api (which normally works in non Module Federation project)
Expected Behavior
A 200 Ok success with weather forecast results is returned from Api call
Identity Provider
Azure B2C Basic Policy
Browsers Affected (Select all that apply)
Chrome, Edge
Regression
No response