AzureAD / microsoft-authentication-library-for-js

Microsoft Authentication Library (MSAL) for JS
http://aka.ms/aadv2
MIT License
3.65k stars 2.65k forks source link

MsalInterceptor breaks ngx-translate #2515

Closed OPunktSchmidt closed 3 years ago

OPunktSchmidt commented 3 years ago

Library

Framework

Angular 10

Description

I use ngx-translate in my Angular 10 App. With uses the default HTTPLoader to load the language files.

export function HttpLoaderFactory(http: HttpClient) {
   return new TranslateHttpLoader(http);
}

When i add the MsalInterceptor to the Angular App every HTTP Request to get the language files seems to be intercepted by the MsalInterceptor. Because that the request fails and ngx-translate is not working. The Request gets intercepted even if protectedResourceMap is to null.

In my unterstanding, MsalInterceptor trys to get a token and add the token to the request for every call ngx-translate make. But when the user is not signed in, getting the token fails and the ngx-translate request is not made.

Ngx-Translate start working again as normal immediately when i remove the MsalInterceptor. When the user is logged in with his Microsoft-Account, ngx-translate also works. But if the user is not signed in the MsalInterceptor tries to get a token for every ngx-translate request and then fails.

    MsalModule.forRoot({
            auth: {
                clientId: 'xyz', // This is your client ID
                authority: 'https://login.microsoftonline.com/xyz', // This is your tenant info
                redirectUri: 'http://localhost:4200/login' // This is your redirect URI
            },
            cache: {
                cacheLocation: 'localStorage'
            }
        }, {
            popUp: true,
            consentScopes: [
                'user.read',
                'api://xyz/api.consume'
            ],
            unprotectedResources: null,
            protectedResourceMap: null,
            extraQueryParameters: {}
        }),

Error Message

core.js:4197 ERROR ClientAuthError: User login is required. For silent calls, request must contain either sid or login_hint

Browsers/Environment

jasonnutter commented 3 years ago

When i add the MsalInterceptor to the Angular App every HTTP Request to get the language files seems to be intercepted by the MsalInterceptor. Because that the request fails and ngx-translate is not working. The Request gets intercepted even if protectedResourceMap is to null.

What do the urls for the http requests from ngx-translate look like? Are they external paths, or local paths on your app?

OPunktSchmidt commented 3 years ago

@jasonnutter The language files are located in my asset-folder as .json files:

image

ngx-translate sends an additional http-request to get this files:

image

This works well, as long i'm signed in via the msal-library and the msal-library is able to get a token silently. When i'm not signed in the msal-library tries to get a token for ngx-translate http-request. This fails with this here and ngx-translate is not able to get the .json files:

image

mtvw commented 3 years ago

I'm struggling with the same issue.

This problem would not exist if the "unprotectedResources" were still available in the msal-angular setup. That way I could exclude the local translation files from msal.

For now, I'm using this dirty workaround to reload the application after login...: this.broadcastService.subscribe('msal:loginSuccess', () => { location.reload(); });

jasonnutter commented 3 years ago

@OPunktSchmidt Does this work?

export const protectedResourceMap: [string, string[]][] = [
  ['http://localhost:4200', null]
];
jasonnutter commented 3 years ago

Also, if you don't have any protected resources (i.e. your protected resource map is empty or null), you should not need to include the MSAL Interceptor at all in your app.

mtvw commented 3 years ago

@OPunktSchmidt Does this work?

export const protectedResourceMap: [string, string[]][] = [
  ['http://localhost:4200', null]
];

No, these calls still get intercepted (and a bearer token is added when logged in)

mtvw commented 3 years ago

Also, if you don't have any protected resources (i.e. your protected resource map is empty or null), you should not need to include the MSAL Interceptor at all in your app.

I have a list with 3 endpoints that should be intercepted. But msalIntercepter is processing each request that goes through the httpClient, also for url's that are not in the list.

jasonnutter commented 3 years ago

Sorry, something like this should work:

["http://localhost:4200/assets/i18n/*.json", null]

I have a list with 3 endpoints that should be intercepted. But msalIntercepter is processing each request that goes through the httpClient, also for url's that are not in the list.

Correct, the interceptor will be run for each request using the http client. It will attach tokens for urls that are listed in the protected resource maps, and by default, will attach ID tokens for requests against the app itself (e.g. the ngx-translate requests) unless an entry like the one above is added. The later is behavior we will be changing in MSAL Angular v2.

OPunktSchmidt commented 3 years ago
["http://localhost:4200/assets/i18n/*.json", null]

@jasonnutter This does not work either. The msal-library still tries to add a token to the request.

export const protectedResourceMap: [string, string[]][] = [['https://localhost:5001/Authentication/azuread', ['api://xyz/api.consume']],
    ["http://localhost:4200/assets/i18n/*.json", null]];    
      MsalModule.forRoot({
            auth: {
                clientId: 'xyz', // This is your client ID
                authority: 'https://login.microsoftonline.com/xyz', // This is your tenant info
                redirectUri: 'http://localhost:4200/login' // This is your redirect URI
            },
            cache: {
                cacheLocation: 'localStorage'
            }
        }, {
            popUp: true,
            consentScopes: [
                'user.read',
                'api://xyz/api.consume'
            ],
            unprotectedResources: null,
            protectedResourceMap: protectedResourceMap,
            extraQueryParameters: {}
        }),
mtvw commented 3 years ago

Couldn't get it to work either...

For now I went back to msal-angular 1.0.0, with these unprotectedResources: unprotectedResources: [ '/assets/' ],

This works as expected: calls to the translations files execute instantly without token. In the meantime, the popup appears to handle the login process. When the login is successful, the application works as expected with instant translations.

OPunktSchmidt commented 3 years ago

@mtvw I also went back to 1.0.0 and now it works as expected.

HOGENTJasperDeSmet commented 3 years ago

Any updates on this issue? Im having a similar issue but when accessing a reverse proxy on my webserver.

jasonnutter commented 3 years ago

Apologies for the delay. I was able to reproduce the behavior you describe in the ngx-translate sample app, and was able to mitigate it by providing the relative path in the protectedResourceMap, e.g.:

export const protectedResourceMap: [string, string[]][] = [
    ["/assets/i18n/*.json", null]
];

Apologies for the confusion, we'll make sure this is better documented. cc: @jo-arroyo

ishershunovich commented 3 years ago

I have the same issue. Adding ["/assets/i18n/*.json", null] into protectedResourceMap doesn't help, app sends requests with appended security token for these resources. Any workaround?


I'm sorry, I've found my mistake. It was in relative path, addition '/' at start was redundant. So, adding ["assets/i18n/*.json", null] works for me. Thank you very much!