Angular Configuration using ASP.NET Core settings

This post shows how ASP.NET Core application settings can be used to configure an Angular application. ASP.NET Core provides excellent support for different configuration per environment, and so using this for an Angular application can be very useful. Using CI, one release build can be automatically created with different configurations, instead of different release builds per deployment target.

Code: https://github.com/damienbod/AspNet5IdentityServerAngularImplicitFlow/tree/master/src/AngularClient

History

2017-11-05 Updated to Angular 5 and Typescript 2.6.1

ASP.NET Core Hosting application

The ClientAppSettings class is used to load the strongly typed appsettings.json from the json file. The class contains the properties required for OIDC configuration in the SPA and the required API URLs. These properties have different values per deployment, so we do not want to add these in a typescript file, or change with each build.

namespace AngularClient.ViewModel
{
    public class ClientAppSettings
    {
        public string  stsServer { get; set; }
        public string redirect_url { get; set; }
        public string client_id { get; set; }
        public string response_type { get; set; }
        public string scope { get; set; }
        public string post_logout_redirect_uri { get; set; }
        public bool start_checksession { get; set; }
        public bool silent_renew { get; set; }
        public string startup_route { get; set; }
        public string forbidden_route { get; set; }
        public string unauthorized_route { get; set; }
        public bool log_console_warning_active { get; set; }
        public bool log_console_debug_active { get; set; }
        public string max_id_token_iat_offset_allowed_in_seconds { get; set; }
        public string apiServer { get; set; }
        public string apiFileServer { get; set; }
    }
}

The appsettings.json file contains the actual values which will be used for each different environment.

{
  "ClientAppSettings": {
    "stsServer": "https://localhost:44318",
    "redirect_url": "https://localhost:44311",
    "client_id": "angularclient",
    "response_type": "id_token token",
    "scope": "dataEventRecords securedFiles openid profile",
    "post_logout_redirect_uri": "https://localhost:44311",
    "start_checksession": false,
    "silent_renew": false,
    "startup_route": "/dataeventrecords",
    "forbidden_route": "/forbidden",
    "unauthorized_route": "/unauthorized",
    "log_console_warning_active": true,
    "log_console_debug_active": true,
    "max_id_token_iat_offset_allowed_in_seconds": 10,
    "apiServer": "https://localhost:44390/",
    "apiFileServer": "https://localhost:44378/"
  }
}

The ClientAppSettings class is then added to the IoC in the ASP.NET Core Startup class and the ClientAppSettings section is used to fill the instance with data.

public void ConfigureServices(IServiceCollection services)
{
  services.Configure<ClientAppSettings>(Configuration.GetSection("ClientAppSettings"));
  services.AddMvc();

A MVC Controller is used to make the settings public. This class gets the strongly typed settings from the IoC and returns it in a HTTP GET request. No application secrets should be included in this HTTP GET request!

using AngularClient.ViewModel;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;

namespace AngularClient.Controllers
{
    [Route("api/[controller]")]
    public class ClientAppSettingsController : Controller
    {
        private readonly ClientAppSettings _clientAppSettings;

        public ClientAppSettingsController(IOptions<ClientAppSettings> clientAppSettings)
        {
            _clientAppSettings = clientAppSettings.Value;
        }

        [HttpGet]
        public IActionResult Get()
        {
            return Ok(_clientAppSettings);
        }
    }
}

Configuring the Angular application

The Angular application needs to read the settings and use these in the client application. A configClient function is used to GET the data from the server. The APP_INITIALIZER could also be used, but as the settings are been used in the main AppModule, you still have to wait for the HTTP GET request to complete.

configClient() {

	// console.log('window.location', window.location);
	// console.log('window.location.href', window.location.href);
	// console.log('window.location.origin', window.location.origin);

	return this.http.get(window.location.origin + window.location.pathname + '/api/ClientAppSettings').map(res => {
		this.clientConfiguration = res.json();
	});
}

In the constructor of the AppModule, the module subscribes to the configClient function. Here the configuration values are read and the properties are set as required for the SPA application.

clientConfiguration: any;

constructor(public oidcSecurityService: OidcSecurityService, private http: Http, private configuration: Configuration) {

	console.log('APP STARTING');
	this.configClient().subscribe(config => {

		let openIDImplicitFlowConfiguration = new OpenIDImplicitFlowConfiguration();
		openIDImplicitFlowConfiguration.stsServer = this.clientConfiguration.stsServer;
		openIDImplicitFlowConfiguration.redirect_url = this.clientConfiguration.redirect_url;
		openIDImplicitFlowConfiguration.client_id = this.clientConfiguration.client_id;
		openIDImplicitFlowConfiguration.response_type = this.clientConfiguration.response_type;
		openIDImplicitFlowConfiguration.scope = this.clientConfiguration.scope;
		openIDImplicitFlowConfiguration.post_logout_redirect_uri = this.clientConfiguration.post_logout_redirect_uri;
		openIDImplicitFlowConfiguration.start_checksession = this.clientConfiguration.start_checksession;
		openIDImplicitFlowConfiguration.silent_renew = this.clientConfiguration.silent_renew;
		openIDImplicitFlowConfiguration.startup_route = this.clientConfiguration.startup_route;
		openIDImplicitFlowConfiguration.forbidden_route = this.clientConfiguration.forbidden_route;
		openIDImplicitFlowConfiguration.unauthorized_route = this.clientConfiguration.unauthorized_route;
		openIDImplicitFlowConfiguration.log_console_warning_active = this.clientConfiguration.log_console_warning_active;
		openIDImplicitFlowConfiguration.log_console_debug_active = this.clientConfiguration.log_console_debug_active;
		openIDImplicitFlowConfiguration.max_id_token_iat_offset_allowed_in_seconds = this.clientConfiguration.max_id_token_iat_offset_allowed_in_seconds;

		this.oidcSecurityService.setupModule(openIDImplicitFlowConfiguration);

		configuration.FileServer = this.clientConfiguration.apiFileServer;
		configuration.Server = this.clientConfiguration.apiServer;
	});
}

The Configuration class can then be used throughout the SPA application.

import { Injectable } from '@angular/core';

@Injectable()
export class Configuration {
    public Server = 'read from app settings';
    public FileServer = 'read from app settings';
}

I am certain, there is a better way to do the Angular configuration, but not much information exists for this. APP_INITIALIZER is not so well documentated. Angular CLI has it’s own solution, but the configuration file cannot be read per environment.

Links:

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments

https://www.intertech.com/Blog/deploying-angular-4-apps-with-environment-specific-info/

https://stackoverflow.com/questions/43193049/app-settings-the-angular-4-way

https://damienbod.com/2015/10/11/asp-net-5-multiple-configurations-without-using-environment-variables/

Advertisements

4 comments

  1. […] Angular Configuration using ASP.NET Core settings (Damien Bowden) […]

  2. Full of security holes. Effectively leaking server configuration right through, regardless of whether you include “secure” settings or not. A hackers dream.

    1. Hi I don’t see what security is at risk here. All the properties are public knowlegde, which ones are dangerous?

      Greetings Damien

  3. […] Google Chrome Developers The Ultimate Angular CLI Reference Guide – Jurgen Van de Moere Angular Configuration using ASP.NET Core settings – Damien Bowden TypeScript: JavaScript + Types = Awesome Developer Productivity – […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: