[ ] Regression (a behavior that used to work and stopped working in a new release)
[X] Bug report
[ ] Performance issue
[ ] Feature request
[ ] Documentation issue or request
[ ] Other... Please describe:
Library version: 0.1.2
## Current behavior
Routes NOT protected by router guards are pushed in to the array of unprotectedResources causing 401 when a protected resource url is matched against the route path.
The route path is not the compelte url i.e. "https://example.xyz/banana" but "banana" causing the string "banana" to be pushed to unprotectedResources making ANY resource with the word "banana" in it treated as a unprotectedResource and the interceptor wont attach the bearer token to the request.
## Expected behavior
Partial names of route config should not be pushed in to the unprotectedResources array.
Resources https://example.xyz/api/bananas should not be treated as a unprotected just because there is a route without a guard thats named banana.
## Minimal reproduction of the problem with instructions
Setup MSAL and interceptor
Setup routes protected with route guards
Create one route without a guard that contains a partial match of any of the protected backend API calls
Make a request to API where the is a partial match.
const routes: Routes = [
{ path: '', component: HomeComponent, pathMatch: 'full' },
{ path: 'coconuts', loadChildren: './coconuts/coconuts.module#CoconutsModule', canActivate: [RoleGuard], data: { roles: ['FruitAdmin'] } },
{ path: 'banana', loadChildren: './banana/banana.module#BananaModule' }, // this will make the API call to api/bananas/ fail with 401
{ path: 'bananas', loadChildren: './bananas/bananas.module#BananasModule', canActivate: [RoleGuard], data: { roles: ['FruitAdmin'] }, pathMatch: 'full' }
];
// this API endpoints is protected and
function getAllBananas(): void {
this.http.get<Banana[]>(this.baseUrl + 'api/bananas/').subscribe(results => {
console.log(results);
},
error => console.error(error));
}
// this API is unprotected by anyone with the specific id
function getSpecificBanana(id: number): void {
this.http.get<Banana>(this.baseUrl + 'api/banana/' + id).subscribe(results => {
console.log(results);
},
error => console.error(error));
}
Since the banana route is setup in the route config without a guard this will be pushed to the unprotectedResources array in the MSAL.service (line 55)
this.router.events.subscribe(event => {
for (var i = 0; i < router.config.length; i++) {
if (!router.config[i].canActivate) {
if (this.config && this.config.unprotectedResources) {
if (!this.isUnprotectedResource(router.config[i].path) &&
this.isEmpty(router.config[i].path)) {
this.config.unprotectedResources.push(router.config[i].path);
}
}
}
}
});
and map to ANY url containing"banana" (line 220)
isUnprotectedResource(url) {
if (this.config && this.config.unprotectedResources) {
for (var i = 0; i < this.config.unprotectedResources.length; i++) {
if (url.indexOf(this.config.unprotectedResources[i]) > -1) { //Will match anything containing banana
return true;
}
}
}
return false;
}
Is the app you tested with registered on Azure as a single tenant or as a multi-tenant (using the common endpoint)?
@visualjeff It is not using the common endpoint, its targeting our instance of Azure B2C https://login.microsoftonline.com/tfp/xyz.onmicrosoft.com/B2C_1A_yyyxxxzzz/v2
Found a easy workaround for this issue and its to simply create a Guard that returns true directly and assign it to any route that you want anon access.
export class AnonGuard implements CanActivate {
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
return true;
}
}
and then use it in the route configuration like
{ path: "banana", loadChildren: "./banana/banana.module#BananaModule", canActivate: [AnonGuard], pathMatch: "full" },
{ path: "bananas", loadChildren: "./bananas/bananas.module#BananasModule", canActivate: [RoleGuard], data: { roles: ["BananaAdmin"] }, pathMatch: "full" }
This prevents the router path for the previous unguarded route to be pushed to unprotectedResoures.
Another simple workaround if you do not have unprotected resources: you can simply leave config.unprotectedResources as undefined. No routes will get added to the list if undefined. This works for the case when you are only protecting API endpoints.
@Weschk Thanks for raising this issue. Linking this with #786 -> will add this as one of the use cases to test when the new angular wrapper work starts.
@negoe @DarylThayil FYI.
Most helpful comment
Another simple workaround if you do not have unprotected resources: you can simply leave config.unprotectedResources as undefined. No routes will get added to the list if undefined. This works for the case when you are only protecting API endpoints.