I want to set up some end to end tests for my Angular application, which requires the use of the MSAL library to authenticate with some downstream services. When I try to run my e2e tests locally, the MSAL library is forcing me to authenticate with a username/password.
This is a problem because our CI/CD e2e testing should not have any human-intervention; thus I am looking for a way to either bypass the MSAL authentication or set up a service-account to login. Unfortunately there is not a lot of documentation surrounding MSAL for Angular (especially when it comes to e2e testing), but this seems like a common issue that others may have run into.
I've tried to disable the MsalModule from our app.module.ts file but I am still prompted for login when I try to run the application. I've also seen some articles trying to programmatically log in, but this does not work for us as MSAL is technically not an Angular component we are able to touch.
app.module.ts:
@NgModule({
...
imports: [
...
MsalModule.forRoot({
clientID: '<client_id>',
authority: <microsoft_authority_url>,
validateAuthority: true,
redirectUri: "http://localhost:4200/",
cacheLocation : "localStorage",
postLogoutRedirectUri: "http://localhost:4200/",
navigateToLoginRequestUrl: true,
popUp: true,
consentScopes: [ "user.read"],
unprotectedResources: ["https://www.microsoft.com/en-us/"],
protectedResourceMap: protectedResourceMap,
logger: loggerCallback,
correlationId: '1234',
level: LogLevel.Info,
piiLoggingEnabled: true
})
],
entryComponents: [SaveDialogComponent,
GenericDialog, MassChangeDialogComponent],
providers: [TitleCasePipe,
{provide: HTTP_INTERCEPTORS, useClass: MsalInterceptor, multi: true}],
bootstrap: [AppComponent]
})
export class AppModule { }
Expected result: removing the MSAL authentication module should allow our application to run without needing to log in.
Actual result: application is still prompting for log in, or does not render correctly.
I solved that by adding a property enableMsal
into my environment.test.ts (and same property with value true
in prod environment):
export const environment = {
production: false,
enableMsal: false,
};
Then used it in routing module (by default called app-routing.module.ts, like this:
//...
const guards: any[] = environment.enableMsal ? [MsalGuard] : [];
const routes: Routes = [
{path: '', redirectTo: '/main', pathMatch: 'full'},
{path: 'main', component: MainComponent, canActivate: guards},
{path: 'other', component: OtherComponent, canActivate: guards},
];
//...
In case you don't know how to configure multiple environments, angular has a good guide here.
To Bypass MSAL you can mock the implementation of MsalGuard
, MsalService
and MsalInterceptor
inside main-skip-login.ts
which is a replica of main.ts
file.
import { MsalGuard, MsalInterceptor, MsalService } from '@azure/msal-angular';
MsalGuard.prototype.canActivate = () => true;
MsalInterceptor.prototype.intercept = (req, next) => {
const access = localStorage.getItem('access_token');
req = req.clone({
setHeaders: {
Authorization: `Bearer ${access}`
}
});
return next.handle(req);
};
MsalService.prototype.getAccount = (): any => {
if (!localStorage.getItem('access_token')) return undefined;
return {
idToken: {
scope: [],
// other claims if required
}
};
};
Then create a configuration named e2e inside angular.json
and replace main.ts
with main-skip-login.ts
.
"configurations": {
"e2e": {
"fileReplacements": [
{
"replace": "src/main.ts",
"with": "src/main-skip.login.ts"
}
]
}}
Now you can run project with this configuration and set localStorage with authentic token to bypass MSAL auth flow. You can also play with mocking logic to get the desired outcome.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With