Search code examples
angulartypescriptrequirejswindows-authenticationsystemjs

SystemJs not working with windows authentication and typescript in angular4


I am currently attempting to get an Angular 4 app running with windows authentication. I have it successfully running with anonymous authentication, but not windows auth. As soon as I switch it over to windows authentication, the page fails. The first error I got was:

(index):38 Error: Fetch error: 401 Unauthorized
  Instantiating http://localhost:1264/app/main.js
  Loading app
    at http://localhost:1264/node_modules/systemjs/dist/system.src.js:1500:13 [<root>]
    at Zone.run (http://localhost:1264/node_modules/zone.js/dist/zone.js:125:43) [<root> => <root>]
    at http://localhost:1264/node_modules/zone.js/dist/zone.js:760:57 [<root>]
    at Zone.runTask (http://localhost:1264/node_modules/zone.js/dist/zone.js:165:47) [<root> => <root>]
    at drainMicroTaskQueue (http://localhost:1264/node_modules/zone.js/dist/zone.js:593:35) [<root>]
    at <anonymous> [<root>]

Nasty. But I kind of understand it. main.js was being loaded by SystemJs in the packages section in the Systemjs.config.js file:

    packages: {
  app: {
    main: './main.js',
    defaultExtension: 'js'
  },

so the issue has something to do with that. I read somewhere that to get around this, I have to add a couple more attributes, as follows:

packages: {
      app: {
        main: './main.js',
        defaultExtension: 'js',
        format: 'register',
        scriptLoad: true
      },

The format and scriptLoad attributes seem to get me past the unauthorized tags, but now I have another issue. It is now giving me the following error:

Uncaught Error: Module name "@angular/platform-browser-dynamic" has not been loaded yet for context: _. Use require([])
http://requirejs.org/docs/errors.html#notloaded
    at F (require.js:7) [<root>]
    at Object.m [as require] (require.js:26) [<root>]
    at requirejs (require.js:32) [<root>]
    at :1264/app/main.js:2:34 [<root>]

Again, I kind of figured out what was happening. It is attempting to execute the main.js file, but that file contains a "require" statement. At this point, the SystemJs context doesn't appear to recognise that requirejs is loaded. There is a script statement in my index file.

main.ts looks like this:

import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';

// ***4***
platformBrowserDynamic().bootstrapModule(AppModule);

which transpiles to this:

"use strict";
var platform_browser_dynamic_1 = require("@angular/platform-browser-dynamic");
var app_module_1 = require("./app.module");
// ***4***
platform_browser_dynamic_1.platformBrowserDynamic().bootstrapModule(app_module_1.AppModule);
//# sourceMappingURL=main.js.map

and it is bombing out on the require statement.

Ok, so why does it use require in the first place? That's because of the module statement in the typescript json file. In that file, the module attribute is set to commonjs. I have tried other module types, but I get 401 unauthorised with anything other than commonjs, and that includes using system.

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false,
    "suppressImplicitAnyIndexErrors": true,
    "typeRoots": [
      "./node_modules/@types/"
    ]
  },
  "compileOnSave": true,
  "exclude": [
    "node_modules/*",
    "**/*-aot.ts"
  ]
}

I have put my app up on bitbucket, in case anyone is interested. The app, which was build in Visual Studio 2017 Enterprise, can be found here: Angular2VS2015 (its a work in progress, hence the bad naming)

So the question is, how do I get the app to run under the context of windows authentication?


Solution

  • Ok, I have solved this problem. The issue is with SystemJs. When SystemJs makes a call to the server to retrieve the included files, it makes XHR calls. It's no different than making a call to a webapi service from javascript - if you're not authenticated, it will fail with a 401 Unauthorized.

    To get around this, you need to ensure that the authorization headers are included in the SystemJs calls. This is done by adding a meta tag in System.Config, and setting the authorization attribute to true. Here is my complete systemjs.config.js file, with the meta tag at the top that enables the authorization to work.

    /**
     * System configuration for Angular samples
     * Adjust as necessary for your application needs.
     */
    (function (global) {
        System.config({
            meta: {
                '*': { authorization: true }
            },
            paths: {
                // paths serve as alias
                'npm:': 'node_modules/'
            },
            // map tells the System loader where to look for things
            map: {
                // our app is within the app folder
                app: 'app',
    
                // angular bundles
                '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
                '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
                '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
                '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
                '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
                '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
                '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
                '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
                '@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js',
    
                // other libraries
                'rxjs': 'npm:rxjs',
                'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js',
                'ej-angular2': 'npm:ej-angular2'
            },
            // packages tells the System loader how to load when no filename and/or no extension
            packages: {
                app: {
                    main: './main.js',
                    defaultExtension: 'js'
                },
                rxjs: {
                    defaultExtension: 'js'
                },
                'ej-angular2': {
                    main: './src/index.js'
                }
            }
        });
    })(this);
    

    I hope this helps someone else, cause this was a biatch to figure out.