AzureAD / microsoft-authentication-library-for-js

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

Msal Angular Interceptor is not working properly in Anuglar Universal SSR #4835

Open dboschm opened 2 years ago

dboschm commented 2 years ago

Core Library

MSAL.js v2 (@azure/msal-browser)

Core Library Version

2.24.0

Wrapper Library

MSAL Angular (@azure/msal-angular)

Wrapper Library Version

2.3.0

Description

MSAL Angular Interceptor is using window APIs and is therefore not applicable in Angular Universal / SSR Projects.

From what I see in the error message and in the code this probably comes from this line. image

Reproduction steps:

Error Message

image

Msal Logs

No response

MSAL Configuration

{
auth: {
            clientId: environment.clientB2CSettings.clientId,
            authority: environment.b2cPolicies.authority,
            redirectUri: environment.clientB2CSettings.redirectUri,
            postLogoutRedirectUri: environment.clientB2CSettings.postLogoutRedirectUri,
            knownAuthorities: [
                environment.b2cPolicies.authority
            ],
        },
        cache: {
            cacheLocation: BrowserCacheLocation.LocalStorage,
            storeAuthStateInCookie: isPlatformBrowser(platform) && isIE, // set to true for IE 11
        },
}

### Relevant Code Snippets

```javascript
export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
    const protectedResourceMap = new Map<string, Array<string>>();
    protectedResourceMap.set(environment.apiConfig.uri + '/settings', environment.apiConfig.scopes);
    return {
        interactionType: InteractionType.Redirect,
        protectedResourceMap
    };
}


### Reproduction Steps

1. Add MSAL Interceptor to your project. 
2. Add angular universal to your project using schematics `ng add @nguniversal/express-engine` 
3. RUN `npm run dev:ssr` 
4. Open your browser at a protected route (e.g. `localhost:4200/protected-route`) which makes an protected api call to the backend without beeing signed in. 

This should result in a redirect (or popup as configured in the Interceptor config) but it actually ends up in an error message: 
`ERROR ReferenceError: window is not defined
    at MsalInterceptor.acquireTokenInteractively`

### Expected Behavior

I expect either a direct redirect to the configured b2c login page 
or a partially rendered html without the results from the protected api call (as it returned an http 401 response). 

### Identity Provider

Azure B2C Basic Policy

### Browsers Affected (Select all that apply)

Chrome, Firefox, Edge

### Regression

_No response_

### Source

External (Customer)
dboschm commented 2 years ago

To be more precise. I don't expect the msal interceptor make any backendrequests that it knows are protected (are in the protected sourcemap) in the case of SSR.

sameerag commented 2 years ago

@dboschm Thanks for raising this. Unfortunately we do not support server side token fetch for MSAL JS, the library is always meant to work only on client side. If you need better error handling, we can provide the support by adding a more meaningful error message.

We recommend msal-node for any server side use cases.

dboschm commented 2 years ago

Thanks a lot for the response. At least I found this msal docs file, which states that msal-angular has minimal ssr capabilities: https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-angular/docs/v2-docs/angular-universal.md And indeed e.g. guards are working fine. I have a partially public and partially protected api, and I configured the msal interceptor with the protected api endpoints. I think msal-node would be an overkill in my case.

Either it would be great if msal Interceptor would allow to have an interaction type of none when running ssr. So that when a backend request fails with 401 because there is no token available on ssr the request just cancels without any interaction. With this we would just avoid to get into the code, that needs browser apis.

Or msal Interceptor could cancel protected backend requests upfront, when running in ssr mode. (Indeed this is what I programmed as a workaround for SSR, by putting a wrapper around msal Interceptor, that detects ssr, then matches the configured protected routes to the request, and either executes the request or cancels it. However I don't like this, solution as especially the Route matching is already programmed in the msal interceptor and I kind of copied it)

sameerag commented 2 years ago

@dboschm Thanks for the details. I am tagging this as an enhancement and we will try to improve this experience. However please note that the prioritization may be low, since we are currently heads down delivering committed features. cc @EmLauber

yksht commented 2 years ago

Im not familiar with Angular Universal SSR, anyway there is unsafe usage of global window object. I see there is properly injected document object in the interceptor constructor

@Inject(DOCUMENT) document?: any

And I guess const redirectStartPage = window.location.href; should be replaced with const redirectStartPage = this._document.location.href;.

As I remember angular provides its own abstraction over document/location object in case it is not browser platform.

jo-arroyo commented 2 years ago

@yksht Can you please open a new issue here with your requested fix? This will help with our feature tracking and prioritization. Thank you.

yksht commented 1 year ago

@jo-arroyo Is it realy necessary to create another issue? :) Replacing window object with properly injected document object should help with this issue in Angular Unversal SSR