Angular

Using NSwag to Generate Angular Client for an ASP.NET Core 3 API

This week we are going to add an Angular project that will utilize the API we created a few weeks ago. This post is part of the revamp of my ASP.NET Core Basics repo that I kicked off when .NET Core 3.0 was released. For details on how we got to the current point in the application check out the following posts.

Swagger/OpenAPI with NSwag and ASP.NET Core 3
ASP.NET Core 3: Add Entity Framework Core to Existing Project
New Razor Pages Project Backed with an API
Using NSwag to Generate Angular Client for an ASP.NET Core 3 API
Using NSwag to Generate React Client for an ASP.NET Core 3 API
Using NSwag to Generate Blazor Server Client for an ASP.NET Core 3.1 API
Using NSwag to Generate a Vue Client for an ASP.NET Core 3.1 API

Do note that I realize that using an ASP.NET Core backed Angular project for this sample is overkill and a plain Angular application would have been all that is needed, but I wanted to use the ASP.NET Core template as a base for all the projects in this series. After the initial application creation, you can think of this example as setting up access to a secondary API in addition to the application’s main API if that helps.

The sample code before any of the changes in this post can be found here.

API Changes

Before we get to the actual Angular side of this post we are going to update the associated API to accept all cross-origin resource sharing (CORS) requests. The ASP.NET Core setup has a lot of options and I recommend being me explicit about what your API will accept if you can. Check out the official Microsoft CORS docs for more information.

All the changes needed will be in the Startup class of the API project. The CORS setup is policy-based and each policy needs a name that I stored in the following class level constant.

private const string AllowAllCors = "AllowAll";

In the ConfigureServices function add the following to register the CORS policy. Again be more restrictive with your policy if you can but for this example, we are opening up the API to allow any request.

services.AddCors(options =>
                 {
                     options.AddPolicy(AllowAllCors,
                                       builder =>
                                       {
                                           builder.AllowAnyHeader();
                                           builder.AllowAnyMethod();
                                           builder.AllowAnyOrigin();
                                       });
                 });

Finally, in the Configure function add the following to get CORS added to the HTTP pipeline processing. I’m not 100% sure if it matters where you added it in the pipeline, but I added it close to the front. I included a bit pipeline in the sample app for reference.

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.UseCors(AllowAllCors);

app.UseHttpsRedirection();

That is all the changes needed for the API, next we will create a new Angular project.

Create an Angular Project

Add a new directory for the application and then in a terminal navigate to that directory. Then the following command can be used to create the new Angular project. The target framework isn’t required, but I have a preview of .NET Core 3.1 installed and I wanted to make sure this project is targeting .NET Core 3.0.

dotnet new angular -f netcoreapp3.0

Next, use the following command to add the new project to the solution file which is in the root of the repo. Your filenames and paths could vary if you can’t using the same code of course.

dotnet sln ..\..\BasicsRefresh.sln add ContactsAngular.csproj

Use NSwagStudio to Generate Angular Client

NSwag provides multiple options for client generation including a CLI option, code, and a Windows application. This post is going to use the Windows application which is called NSwagStudio. Download and install NSwagStudio from here.

Next, make sure your API is running and get the URL of its OpenAPI/Swagger specification URL. For example, I am using a local instance of my API and the URL I need is https://localhost:5001/swagger/v1/swagger.json. If you are using the Swagger UI you can find a link to your swagger.json under the API title.

Now that we have the OpenAPI/Swager specification URL for the API switch over to NSwagStudio. The application will open with a new document ready to go. There are a few options we will need to set. First, select the OpenAPI/Swagger Specification tab and enter your API’s specification URL in the Specification URL box.

In the Outputs section check the TypeScript Client checkbox and then select the TypeScript Client tab. There are a lot of options to play with, but I highlighted the ones I changed to generate the client for this sample. First I entered a Module name so the generated code will all be in that module. For Template, it is very important to select Angular so that the resulting code will be set up for Angular’s dependency injection. Also, make sure and change the Injection token type to InjectionToken if you are on a newer version of Angular. The final option that needs to be set is the Output file path and this is the location you want the generated file to be. I output the Angular project directory under ClientApp\src\app\apis\contactApi.ts. After all the options are set click Generate Files.

For more information on generating Angular clients check the official docs on the subject.

Create UI and Use Generated Client

The sample API is for contact management so the UI we are going to build is to display a contact list. The new components are going to go in a new contacts directory under ClientApp\src\app. In this directory, we will need two new files one for the HTML, contact-list.component.html, and the other for the backing TypeScript class, contact-list.component.ts.

The following is the full content on the HTML file. I’m not going to go into the Angular specific bit in this post, but even if you don’t know Angular you will get the idea of what is going on. The rendered result will be a table of contacts.

<h1 id="tableLabel">Contacts</h1>

<p *ngIf="!contacts"><em>Loading...</em></p>

<table class='table table-striped' aria-labelledby="tableLabel" *ngIf="contacts">
  <thead>
    <tr>
      <th>Name</th>
      <th>Address</th>
      <th>City</th>
      <th>State</th>
      <th>Postal Code</th>
      <th>Phone</th>
      <th>Email</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let contact of contacts">
      <td>{{ contact.name }}</td>
      <td>{{ contact.address }}</td>
      <td>{{ contact.city }}</td>
      <td>{{ contact.state }}</td>
      <td>{{ contact.postalCode }}</td>
      <td>{{ contact.phone }}</td>
      <td>{{ contact.email }}</td>
    </tr>
  </tbody>
</table>

Next is the TypeScript class for the contact list. This is where you get to see the usage of the client that NSwag generated. The related lines are highlighted.

import { Component } from '@angular/core';
import { contacts as Contacts } from "../apis/contactApi";

@Component({
  selector: 'app-contact-list',
  templateUrl: './contact-list.component.html'
})
export class ContactListComponent {
  public contacts: Contacts.IContact[];

  constructor(contactsClient : Contacts.ContactsClient) {
    contactsClient.getContacts().subscribe(result => {
        this.contacts = result;
      },
      error => console.error(error));
  }
}

As you can see from the code above we are injecting the ContactClient and then calling its getContracts and assigning the results to the class’s local contacts variable.

Now that our components are built we need to register them with the application. These changes are in the app.module.ts file found in the ClientApp\src\app directory. The following is the full file excluding the imports that were existing with the changes for our contact related items highlighted.

import { ContactListComponent } from "./contacts/contact-list.component";
import { contacts } from "./apis/contactApi";

@NgModule({
  declarations: [
    AppComponent,
    NavMenuComponent,
    HomeComponent,
    CounterComponent,
    FetchDataComponent,
    ContactListComponent
  ],
  imports: [
    BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
    HttpClientModule,
    FormsModule, 
    RouterModule.forRoot([
      { path: '', component: HomeComponent, pathMatch: 'full' },
      { path: 'contact-list', component: ContactListComponent },
      { path: 'counter', component: CounterComponent },
      { path: 'fetch-data', component: FetchDataComponent },
    ])
  ],
  providers: [contacts.ContactsClient],
  bootstrap: [AppComponent]
})
export class AppModule { }

The first change was to import both the ContactListComponent and contacts which is the contact client. Next, the ContactListComponent was added to the declarations array. Then we added the ContactListComponent to the RouterModule so that Angular will know how to get us to the contact list page. Finally, we added the ContractsClient to the providers array which will allow Angular to inject the client into our contact list component.

To add our contact list to the nav menu we need to change the nav-menu.component.html file in the ClientApp\src\app\nav-menu directory. Add the following list item to add a Contact link in the navbar.

<li class="nav-item" [routerLinkActive]="['link-active']">
  <a class="nav-link text-dark" [routerLink]="['/contact-list']"
    >Contacts</a
  >
</li>

Wrapping Up

I’m repeating myself, but NSwag’s client generation makes it very simple to get starting consuming APIs, but even over time being able to regenerate a client for an API and have any changes in that API ready to go is really nice.

The sample projects after all the changes in this post can be found here.

Using NSwag to Generate Angular Client for an ASP.NET Core 3 API Read More »

Create React or Angular Application from Visual Studio with Authentication

Having templates that provide a good starting point for a new application is an important part of the value that is provided by Microsoft. Nothing kills progress faster than having to spend a week trying to discover the recommended way to set up a project. Thankfully template is an area that Microsoft continues to invest in.

A few weeks ago in my ASP.NET Core 3: React Template with Auth post I went over creating a React application that included auth out of the box using the .NET CLI. In this post, we are going to create an Angular application with Auth from Visual Studio.

Required Installs

As of this writing .NET Core 3 Preview 4 was just released so please make sure you have the latest preview installed. Also, make sure and install the latest preview of Visual Studio. The screenshots in this post will be from the Visual Studio 2019 16.1.0 Preview 1 release. Make sure you have at least the ASP.NET and web development workload installed.

Project Creation

Open Visual Studio and from the Get started area on the left click Create a new project.

On the next screen select the ASP.NET Core Web Application project type and click Next.

On the next screen at a minimum enter a Project name and click the Create button.

On the next screen select the template you want to use. I will be selecting Angular, but the same authentication setup will work for React or React and Redux templates. After selecting your template type on the right side of the screen under Authentication click the Change link.

Select Individual User Accounts and click OK.

After the Change Authentication dialog closes click the Create button on the template selection dialog.

After a couple of minutes, the application will be ready to go. The only other step you will need to do is to apply the Entity Framework migrations. This can be done from the .NET CLI or the first time you try to register an account in debug mode you will be prompted to apply the migrations.

Wrapping Up

I’m not sure if I am alone in this or not, but I get super excited seeing the time being invested by Microsoft in making the getting started experiences better with every release. Having authentication available out of the box for a SAP backed by an API make getting started on a project super simple.

Create React or Angular Application from Visual Studio with Authentication Read More »

Pass ASP.NET Core Appsettings Values to Angular via an API Call

There have been a few issues opened on the repo I have showing usage of Angular, Identity Server 4, and ASP.NET Core together that related to incompatibilities with the newer versions of Angular. In an effort to fix this issue the plan was to recreate the client application using the new Angular template from Microsoft which from what I read should address the issue.

The code before any changes can be found here, but in this case, the whole client application has been recreated so the starting point may not be super helpful.

The Problem

For the most part, this worked well, but the problem can when I needed to use some configuration values from ASP.NET Core in my new Angular application. The previous versions of the template used server-side rendering which I utilized to pass the configuration values. The new template doesn’t use server-side rendering by default and I wanted to find a way to solve the issue without requiring server-side rendering.

The other issue is that I want to be able to run this application in Azure and set the configuration values as environment variables. While Angular seems to have support for environment files finding a solution that used a systems environment variables turned out too not be simple.

Configuration API Endpoint

Since the configuration values I need to get to the client application are secret I decided to go the route of pulling them via an API call back to the same ASP.NET Core application that is hosting the Angular Application, which is the Client App project in the sample solution.

I added a ConfigurationController.cs class to the Controller directory with the following contents.

[Produces("application/json")]
[Route("api/Configuration")]
public class ConfigurationController : Controller
{
    private readonly IConfiguration _configuration;

    public ConfigurationController(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    [HttpGet("[action]")]
    public IActionResult ConfigurationData()
    {
        return Ok(new Dictionary<string, string>
        {
            { "IdentityServerAddress", _configuration["IdentityServerAddress"] },
            { "ApiAddress", _configuration["ApiAddress"] }
        });
    }
}

This controller gets constructed with a reference to the application’s configuration which is then used to populate a dictionary with the values my Angular application needs. For completeness, the following is the contents of the application’s appsettings.json file.

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "IdentityServerAddress": "http://localhost:5000",
  "ApiAddress": "http://localhost:5001/api/"
}

Angular Changes

This is the part that I really struggled to get right. I needed the configuration values from the API above to be available as soon as possible. Thankfully I came across this blog post by Juri Strumpflohner which covers using Angular’s APP_INITIALIZER.

The first thing I need was to create a class in Angular to get the configuration values from the API and serve to them the rest of the Angular application. To do this I added a configuration.service.ts into a new ClientApp/src/app/configuration directory. The full class follows.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class ConfigurationService {

  private configuration: IServerConfiguration;

  constructor(private http: HttpClient) { }

  loadConfig() {
    return this.http.get<IServerConfiguration>('/api/Configuration/ConfigurationData')
      .toPromise()
      .then(result => {
        this.configuration = <IServerConfiguration>(result);
      }, error => console.error(error));
  }

  get apiAddress() {
    return this.configuration.ApiAddress;
  }

  get identityServerAddress() {
    return this.configuration.IdentityServerAddress;
  }

}

export interface IServerConfiguration {
  ApiAddress: string;
  IdentityServerAddress: string;
}

This class hits the API to get the configuration values in the loadConfig function and maps it to a class level field. It also provides properties to get the individual configuration values.

As I mentioned above, getting the application to get these configuration values in a timely matter was something I really struggled to do. The first step to using Angular’s APP_INITIALIZER to solve this issue is to change the import from @angular/core to include APP_INITIALIZER and to import the ConfigurationService.  All these changes are being made in the app.module.ts file.

import { NgModule, APP_INITIALIZER } from '@angular/core';
import { ConfigurationService } from "./configuration/configuration.service";

Next, we need to define a function that will call the ConfigurationService.loadConfig function.

const appInitializerFn = (appConfig: ConfigurationService) => {
  return () => {
    return appConfig.loadConfig();
  };
};

Finally, in the providers array add an element for the APP_INITIALIZER and the ConfigurationService.

providers: [
  ConfigurationService,
  {
    provide: APP_INITIALIZER,
    useFactory: appInitializerFn,
    multi: true,
    deps: [ConfigurationService]
  }]

 Wrapping Up

This is one of those things that turned out to be way more complicated than I expected. Thankfully with the above changes, I was able to get it working. I hope this saves you all some time. The code with all the changes can be found here.

Pass ASP.NET Core Appsettings Values to Angular via an API Call Read More »

ASP.NET Core 2 Fails to Publish With Angular 5

I had an issue opened on my Identity Server GitHub repo saying that publishing the client application fails with something like the following error when using server-side rendering.

Can’t resolve ‘./../$$_gendir/ClientApp/app/app.module.browser.ngfactory’

This was only an issue after moving the project to target Angular 5. After some research, it turns out that Angular tools for Webpack has to use a different plugin for ahead of time compiling for Angular 5+. I ended up finding a fix in this issue.

The Fix

In order to resolve the issue, I made the following changes in the webpack.config.js file. The first change is in the require statements at the top of the file.

Before:
const AotPlugin = require('@ngtools/webpack').AotPlugin;

After:
const AngularCompilerPlugin = require('@ngtools/webpack').AngularCompilerPlugin;

Next, make the following replacement in the rest of the file, which should only be two places.

Before:
new AotPlugin

After:
new AngularCompilerPlugin

Wrapping Up

Most of the projects I reference on this blog are written to specifically target the topic I am trying to cover and as such I miss things like testing publish since I’m never going to production with these applications. I really appreciate those of you who find issues and take the time to open an issue on GitHub. Keep them coming, and if you find a solution before I do feel free to submit a pull request.

 

ASP.NET Core 2 Fails to Publish With Angular 5 Read More »

Trying the New ASP.NET Core Angular Template with CLI Support

I got hit by the flu that has been going around and now that the fever has passed I thought it would be a good time to try out the new version of the Angular template for ASP.NET Core that works well with the Angular CLI.

Template Installation

Note that at the time of this writing the templates are in the release candidate stage and a new version could be available by the time you are reading this so make sure and check this page for potential updates.

Running the following command from a command prompt will install the RC version of the templates.

dotnet new --install Microsoft.DotNet.Web.Spa.ProjectTemplates::2.0.0-rc1-final

Project Creation

Create a directory for the project and navigate to it in a command prompt. Run the following command to create the new Angular project.

dotnet new angular

Next, if you have a solution you want to add the new project to that can be done with the following command adjusting for the naming of your project and solution.

dotnet sln "ASP.NET Core Basics.sln" add src\AngularWithCli\AngularWithCli.csproj

Installation of Angular CLI

From the command prompt run the following command to install the Angular CLI globally.

npm install -g @angular/cli

After the above, I got the following error trying to run any Angular CLI commands.

You seem to not be depending on “@angular/core”. This is an error.

The problem ended up being that I had not installed all the packages for the project. The issue was cleared up by the following command.

npm install

Angular CLI Usage

Navigate to the ClientApp directory and you can then use all the Angular CLI commands as you would in a stand along Angular application. Some of which can be found here. If you are looking for a quick command to verify all is work the following command works well by running a linter on your project.

ng lint

Wrapping Up

Having templates that are compatible with the Angular and React CLI is a big step forward. The CLIs provide a lot of functionality and by having a setup that doesn’t restrict their usages is a great move. Make note that server-side rendering is no longer enabled by default, but can still be enabled for Angular projects, but not React based projects.

I recommend you check out the official documentation which can be found here.

I hope we see these functionality moves to the other templates that are outside of the templates in this package in order to support Aurelia and Vue.

Trying the New ASP.NET Core Angular Template with CLI Support Read More »

Identity Server: Migration to version 2.1 and Angular HTTP Changes

Version 2.1 of Identity Server 4 was released a few weeks and this post is going to cover updating my sample project to the latest version. The starting point of the code can be found here. We are going to tackle this in sections as there are updates needed for an ASP.NET Core Update, Identity Server Update, and some broken bits in Angular.

ASP.NET Core Update

The sample projects were all on ASP.NET Core version 2.0.0. For each project right-click and select Edit ProjectName.csproj. Make the following change.

Before:
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />

After:
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.3" />

Identity Server Update

Right-click the Identity App project and select Edit IdentityApp.csproj. Next, make the following changes.
Before:
<PackageReference Include="IdentityServer4.EntityFramework" Version="2.0.0" />

After:
<PackageReference Include="IdentityServer4.EntityFramework" Version="2.1.0" />

Next, need to add a couple of Entity Framework migrations to see if there were any data changes with the following commands from a command prompt in the Identity App project directory.

dotnet ef migrations add Configration21 -c ConfigurationDbContext -o Data/Migrations/IdentityServer/Configuration
dotnet ef migrations add PersistedGrant21 -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrant

Turns out that there were no data changes for this version so if you are on version 2.0.0 you can skip this step.

Angular Issues

I’m not sure how I didn’t hit this issue on the last update post, but the Client App needs to be changed to use the new Angular HttpClient. I got the following error when trying to run the client application.

An unhandled exception occurred while processing the request.

NodeInvocationException: No provider for PlatformRef!
Error: No provider for PlatformRef!
at injectionError
After some digging, I tracked the issue down to using HttpModule instead of HttpClientModule. To make this transition we need to make a few changes. In the app.module.shared.ts make the following changes to the imports section.
Before:
import { HttpModule } from '@angular/http';

After:
import { HttpClientModule } from '@angular/common/http';

Next, in the imports array make the following change.

Before:
HttpModule

After:
HttpClientModule

Next, in the webpack.config.vendor.js fille add the following to the vendor array.

'@angular/common/http'

The last changes are to the auth.service.ts and they are extensive so instead of going through them I’m just going to post the full class after all the changes.

import { Injectable, Component, OnInit, OnDestroy, Inject } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs/Rx';
import { Subscription } from 'rxjs/Subscription';

import { OidcSecurityService, OpenIDImplicitFlowConfiguration } from 'angular-auth-oidc-client';

@Injectable()
export class AuthService implements OnInit, OnDestroy {
    isAuthorizedSubscription: Subscription;
    isAuthorized: boolean;

    constructor(public oidcSecurityService: OidcSecurityService,
        private http: HttpClient,
        @Inject('ORIGIN_URL') originUrl: string,
        @Inject('IDENTITY_URL') identityUrl: string
    ) {
        const openIdImplicitFlowConfiguration = new OpenIDImplicitFlowConfiguration();
        openIdImplicitFlowConfiguration.stsServer = identityUrl;
        openIdImplicitFlowConfiguration.redirect_url = originUrl + 'callback';
        openIdImplicitFlowConfiguration.client_id = 'ng';
        openIdImplicitFlowConfiguration.response_type = 'id_token token';
        openIdImplicitFlowConfiguration.scope = 'openid profile apiApp';
        openIdImplicitFlowConfiguration.post_logout_redirect_uri = originUrl + 'home';
        openIdImplicitFlowConfiguration.forbidden_route = '/forbidden';
        openIdImplicitFlowConfiguration.unauthorized_route = '/unauthorized';
        openIdImplicitFlowConfiguration.auto_userinfo = true;
        openIdImplicitFlowConfiguration.log_console_warning_active = true;
        openIdImplicitFlowConfiguration.log_console_debug_active = false;
        openIdImplicitFlowConfiguration.max_id_token_iat_offset_allowed_in_seconds = 10;

        this.oidcSecurityService.setupModule(openIdImplicitFlowConfiguration);

        if (this.oidcSecurityService.moduleSetup) {
            this.doCallbackLogicIfRequired();
        } else {
            this.oidcSecurityService.onModuleSetup.subscribe(() => {
                this.doCallbackLogicIfRequired();
            });
        }
    }

    ngOnInit() {
        this.isAuthorizedSubscription = this.oidcSecurityService.getIsAuthorized().subscribe(
            (isAuthorized: boolean) => {
                this.isAuthorized = isAuthorized;
            });
    }

    ngOnDestroy(): void {
        this.isAuthorizedSubscription.unsubscribe();
        this.oidcSecurityService.onModuleSetup.unsubscribe();
    }

    getIsAuthorized(): Observable<boolean> {
        return this.oidcSecurityService.getIsAuthorized();
    }

    login() {
        console.log('start login');
        this.oidcSecurityService.authorize();
    }

    refreshSession() {
        console.log('start refreshSession');
        this.oidcSecurityService.authorize();
    }

    logout() {
        console.log('start logoff');
        this.oidcSecurityService.logoff();
    }

    private doCallbackLogicIfRequired() {
        if (typeof location !== "undefined" && window.location.hash) {
            this.oidcSecurityService.authorizedCallback();
        }
    }

    get(url: string): Observable<any> {
        return this.http.get<any>(url, { headers: this.getHeaders() });
    }

    put(url: string, data: any): Observable<any> {
        const body = JSON.stringify(data);
        return this.http.put<any>(url, body, { headers: this.getHeaders() });
    }

    delete(url: string): Observable<any> {
        return this.http.delete<any>(url, { headers: this.getHeaders() });
    }

    post(url: string, data: any): Observable<any> {
        const body = JSON.stringify(data);
        return this.http.post<any>(url, body, { headers: this.getHeaders() });
    }

    private getHeaders() {
        let headers = new HttpHeaders();
        headers = headers.set('Content-Type', 'application/json');
        return this.appendAuthHeader(headers);
    }

    private appendAuthHeader(headers: HttpHeaders) {
        const token = this.oidcSecurityService.getToken();

        if (token === '') return headers;

        const tokenValue = 'Bearer ' + token;
        return headers.set('Authorization', tokenValue);
    }
}

With all those changes made run the following two commands in a command prompt in the Client App project directory.

node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js
node node_modules/webpack/bin/webpack.js

Wrapping up

This post ended up being more about Angular than Identity Server, but it is nice to have everything upgraded to the latest and working.

The files in the completed can be found here.

Identity Server: Migration to version 2.1 and Angular HTTP Changes Read More »

Auth0: Usage from Angular

This post is a continuation of my exploration of using Auth0 with ASP.NET Core with an API and an Angular front end. I recommend you start with the first post if you are new to Auth0.

This post is going to add a login from Angular in the Client Application as well as accessing the API once logged in. The starting point of the code can be found here.

API Application

To give us an endpoint in the API to call let’s move the SimpleDataController class from the ClientApp/Controllers directory to ApiApp/Controllers directory. Also, remember to adjust the namespace to reflect this move.

To remove some complication we are going to add a CORS policy to the API Application to allow all CORS request. This wouldn’t necessarily something I would recommend for a production application. For more information on CORS check out this post.

To add the CORS policy open the Startup class and add the following to the ConfigureServices function which adds a CORS policy to DI that allows all calls through.

services.AddCors(options =>
{
    options.AddPolicy("default", policy =>
    {
        policy.AllowAnyOrigin()
            .AllowAnyHeader()
            .AllowAnyMethod();
    });
});

Next, in the Configure function, CORS needs to be added to the HTTP pipeline with the policy added above. The following is the full function body for reference, but only the CORS line is new.

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.UseCors("default");
app.UseAuthentication();
app.UseMvc();

Note that the API isn’t currently using Authorization at all and won’t be for this post.

Client Application

Instead of redoing a lot of the same work that I covered in some of my posts on using Identity Server and Angular together I decided to copy the ClientApp directory and use it as a starting point. The rest of the client changes are going to assume the same starting point which means we will be using angular-auth-oidc-client instead of the Auth0 client to do the Open ID Connect bits.

Configuration values from appsettings.json

We need some setting from the ASP.NET Core part of the client application. First, add the following new settings to the appsettings.json file.

"ApiAddress": "http://localhost:50467/api/",
"ClientId": "yourAngularClientId"

Next, Index.cshtml needs to be changed to provide some prerender data. The following shows the new data being passed into the Angular application (this post covers this in detail).

<app asp-prerender-module="ClientApp/dist/main-server"
     asp-prerender-data='new {
    apiUrl = Configuration["ApiAddress"],
    identityUrl = $"https://{Configuration["Auth0:Domain"]}",
    clientId = Configuration["ClientId"]
}'>Loading...</app>

Now that the prerender values are being passed in we need to handle them in the boot.server.ts file in the createServerRenderer export. The following is the full list of values.

export default createServerRenderer(params => {
    const providers = [
        { provide: INITIAL_CONFIG, useValue: { document: '<app></app>', url: params.url } },
        { provide: APP_BASE_HREF, useValue: params.baseUrl },
        { provide: 'BASE_URL', useValue: params.origin + params.baseUrl },
        { provide: 'ORIGIN_URL', useValue: params.origin + params.baseUrl },
        { provide: 'API_URL', useValue: params.data.apiUrl },
        { provide: 'IDENTITY_URL', useValue: params.data.identityUrl },
        { provide: 'CLIENT_ID', useValue: params.data.clientId },
        { provide: 'URL_CONFIG', useValue: params.data}
    ];

Down a bit in the file, the setImmediate call needs to be changed to the following.

setImmediate(() => {
    resolve({
        html: state.renderToString(),
        globals: {url_Config: params.data}
    });

Next, in the app.module.browser.ts file we need functions for getting the config values as well as the associated providers. The following is the full file without the imports.

@NgModule({
    bootstrap: [AppComponent],
    imports: [
        BrowserModule,
        AppModuleShared
    ],
    providers: [
        { provide: 'ORIGIN_URL', useFactory: getBaseUrl },
        { provide: 'API_URL', useFactory: apiUrlFactory },
        { provide: 'IDENTITY_URL', useFactory: identityUrlFactory },
        { provide: 'CLIENT_ID', useFactory: clientIdFactory },
        AppModuleShared
    ]
})
export class AppModule {
}

export function getBaseUrl() {
    return document.getElementsByTagName('base')[0].href;
}

export function apiUrlFactory() {
    return (window as any).url_Config.apiUrl;
}

export function identityUrlFactory() {
    return (window as any).url_Config.identityUrl;
}

export function clientIdFactory() {
    return (window as any).url_Config.clientId;
}

Finally, in the auth.service.ts file we need to inject the new configuration values. The following is the constructor that takes in the new values as well as uses them in the set up of the OpenIDImplicitFlowConfiguration.

constructor(public oidcSecurityService: OidcSecurityService,
    private http: HttpClient,
    @Inject('ORIGIN_URL') originUrl: string,
    @Inject('IDENTITY_URL') identityUrl: string,
    @Inject('CLIENT_ID') clientId: string
) {
    const openIdImplicitFlowConfiguration = new OpenIDImplicitFlowConfiguration();
    openIdImplicitFlowConfiguration.stsServer = identityUrl;
    openIdImplicitFlowConfiguration.redirect_url = originUrl + 'callback';
    openIdImplicitFlowConfiguration.client_id = clientId;
    openIdImplicitFlowConfiguration.response_type = 'id_token token';
    openIdImplicitFlowConfiguration.scope = 'openid profile apiApp';
    openIdImplicitFlowConfiguration.post_logout_redirect_uri = originUrl + 'home';
    openIdImplicitFlowConfiguration.forbidden_route = '/forbidden';
    openIdImplicitFlowConfiguration.unauthorized_route = '/unauthorized';
    openIdImplicitFlowConfiguration.auto_userinfo = true;
    openIdImplicitFlowConfiguration.log_console_warning_active = true;
    openIdImplicitFlowConfiguration.log_console_debug_active = false;
    openIdImplicitFlowConfiguration.max_id_token_iat_offset_allowed_in_seconds = 10;

    this.oidcSecurityService.setupModule(openIdImplicitFlowConfiguration);

    if (this.oidcSecurityService.moduleSetup) {
        this.doCallbackLogicIfRequired();
    } else {
        this.oidcSecurityService.onModuleSetup.subscribe(() => {
            this.doCallbackLogicIfRequired();
        });
    }
}

Make sure in the Auth0 client setup you allow the callback listed above or you will run into issues.

Wrapping Up

Working on this post further convinced me that Open ID Connect is the way to go. I basically took an implementation that was meant for Identity Server and used it with Auth0.

Make note that the reason the API is unsecured at this point is that I had an issue getting angular-auth-oidc-client to play nicely with Auth0 for some reason. I doubt it is an issue with either product just some sort of missing configuration on my part. This is the part of the post that I sunk so much time on. In the end, I decided to just skip it for now. If anyone does get that setup work I would love to see the sample so I know what I was doing wrong.

The completed code can be found here.

Auth0: Usage from Angular Read More »

Auth0: Introduction and Initial Project Setup

As I started my exploration of Identity Server I listed a few alternatives that provided the same type of functionality, but as a Software as a Service. This series of posts will be covering one of the options I mentioned, Auth0.

The big selling points for Auth0, and other services like it, are that it removes you from having to worry about Auth/User Management and get to the part of your applications that bring value to your customers. As with Identity Server, Auth0 can use OpenID Connect (as well as a lot of other protocols), single sign-on and API Access Control.

Sign-up

The first step in getting started is to sign up for a new account. Here is a link to the sign-up page (not an affiliate link). You can use a username and password or a social login. I’m going the social route using GitHub.

After user creation then there are a couple of setup steps. The first is to choose a tenant domain and region.

Click next and on the second step, there are questions about what the account is going to be used for. In my case, it is a personal, developer, who is just playing around.

Click Create Account to finish the creation of your account which will then land you on the account dashboard page.

Auth0 Setup

Client

From the Auth0 Dashboard click the New Client button. Give the client a name, TestMvc in my case, and select Regular Web Applications as the type.

In a follow-up post I will be covering the Single Page Web Application, but for this post, we are going to be using MVC since it tends to be simpler. The next page defaults to a framework selection which seems to be a guide to getting going for the framework you select. We are going to skip that and click on the Settings tab.

On the settings page, we need to fill in a value for Allowed Callback URLs. The sample client should use http://localhost:50774/signin-auth0. Click the Save Changes button.

API

While we are doing some setup on the Auth0 site we are going to go ahead and set up our API as well. Click the APIs menu option on the left menu.

Then click the Create API button. In the dialog enter a Name and Identifier and click the Create button.

Sample Solution Structure and Setup

The sample solution for this post has two projects.

  • ApiApp – Backend application and is a resource that is will require authorization to access. The API is an ASP.NET Core Web API.
  • ClientApp – Frontend application that will be requesting authorization. This is an ASP.NET Core application that is hosting an Angular (4) application. Note for this post we will be using MVC and not Angular. A future post will deal with the Angular side.

The sample solution with the two projects already added can be found here. If you are using the sample solution feel free to skip the next two sub-sections as they are going over how the projects were created.

To start, add a directory to contain the solution.

API Application

Inside the solution directory, create an ApiApp directory. From the command line in the ApiApp directory run the following command to create a new ASP.NET Core application using the Web API template.

dotnet new webapi
Client Application

Inside the solution directory, create a ClientApp directory. From the command line in the ClientApp directory run the following command to create a new ASP.NET Core application using the Angular template which as of this writing outputs an Angular 4 application.

dotnet new angular

After generation is done run the following to make the all the NPM packages that are required get installed.

npm install
Solution

Inside the solution directory, let’s create a solution file for use with Visual Studio. Run the following command to create a solution file named AspNetCoreAngularAuth0.sln.

dotnet new sln --name AspNetCoreAngularAuth0

Next, run the following two commands to add the API and Client projects to the solution.

dotnet sln add ApiApp/ApiApp.csproj
dotnet sln add ClientApp/ClientApp.csproj

Securing the API Application

Open the appsettings.json file and add a section for Auth0. We are going to need to store the Auth0 domain (tenant domain from sign up) and API Identifier (from the creation of the API at Auth0).  The following is the full file from the API project with the new Auth0 section.

{
  "Logging": {
    "IncludeScopes": false,
    "Debug": {
      "LogLevel": {
        "Default": "Warning"
      }
    },
    "Console": {
      "LogLevel": {
        "Default": "Warning"
      }
    }
  },
  "Auth0": {
    "Domain": "yourTenantDomain.auth0.com",
    "ApiIdentifier": "http://localhost:50467/"
  }
}

Next, in the ConfigureServices function of the Startup class add the following to add authentication using the JWT Bearer scheme to the DI system.

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

}).AddJwtBearer(options =>
{
    options.Authority = $"https://{Configuration["Auth0:Domain"]}/";
    options.Audience = Configuration["Auth0:ApiIdentifier"];
});

In the Configure function add the following line before app.UseMvc() to add authentication to the HTTP pipeline for the API application.

app.UseAuthentication();

The last step in the API for this post is to add a controller that will require authentication. The following is the full code for the AuthTestController that was added to the Controllers directory.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace ApiApp.Controllers
{
    [Route("api/[controller]")]
    [Authorize]
    public class AuthTestController : Controller
    {
        [HttpGet]
        public string Get()
        {
            return "Congratulations you are authenticated";
        }
    }
}

Client Application

In the Client Application open the appsettings.json and add the following setting related to Auth0. This is the full file so the logging section was existing.

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "Auth0": {
    "Domain": "yourTenantDomain.auth0.com",
    "ClientId": "yourClientId",
    "ClientSecret": "yourClientSecret",
    "CallbackUrl": "http://localhost:50774/signin-auth0",
    "ApiIdentifier": "yourApiIdentifier"
  }
}

If you are going to be checking in your code into a publically accessible source control I recommend you use user secrets instead of appsettings.json. You can read more about user secrets here.

Next, in the ConfigureServices function of the Startup class add the following. I’m not going to go over this code line by line the gist is it is setting up the application to using cookies and Open ID Connect for authentication.

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect("Auth0", options =>
{
    options.Authority = $"https://{Configuration["Auth0:Domain"]}";
    options.ClientId = Configuration["Auth0:ClientId"];
    options.ClientSecret = Configuration["Auth0:ClientSecret"];

    options.ResponseType = "code";
    options.Scope.Clear();
    options.Scope.Add("openid");

    options.CallbackPath = new PathString("/signin-auth0");

    options.ClaimsIssuer = "Auth0";

    options.Events = new OpenIdConnectEvents
    {
        OnRedirectToIdentityProviderForSignOut = (context) =>
        {
            var logoutUri = $"https://{Configuration["Auth0:Domain"]}/v2/logout?client_id={Configuration["Auth0:ClientId"]}";

            var postLogoutUri = context.Properties.RedirectUri;
            if (!string.IsNullOrEmpty(postLogoutUri))
            {
                if (postLogoutUri.StartsWith("/"))
                {
                        // transform to absolute
                        var request = context.Request;
                    postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase + postLogoutUri;
                }
                logoutUri += $"&returnTo={ Uri.EscapeDataString(postLogoutUri)}";
            }

            context.Response.Redirect(logoutUri);
            context.HandleResponse();

            return Task.CompletedTask;
        },
        OnRedirectToIdentityProvider = context =>
        {
            context.ProtocolMessage.SetParameter("audience", Configuration["Auth0:ApiIdentifier"]);
            return Task.FromResult(0);
        }
    };
});

Note that the OnRedirectToIdentityProvider bit is related to getting access to the API.

In the Configure function add the following line before app.UseMvc to add authentication to the HTTP pipeline.

app.UseAuthentication();
Testing Setup

In order to provide a way to test login, logout, and API access without using the Angular portion of the client app, remember that will be a future post, I add an AuthTestController to the Controllers directory with the following.

public class AuthTestController : Controller
{
    public async Task<IActionResult> Index()
    {
        ViewBag.ApiResults = "Not Called";

        var client = new HttpClient();
        client.DefaultRequestHeaders.Authorization = 
		     new AuthenticationHeaderValue("Bearer", await HttpContext.GetTokenAsync("access_token"));
        ViewBag.ApiResults = await (await client.GetAsync("http://localhost:50467/api/authtest"))		                     .Content.ReadAsStringAsync();

        return View();
    }

    public async Task Login(string returnUrl = "/")
    {
        await HttpContext.ChallengeAsync("Auth0", new AuthenticationProperties() { RedirectUri = returnUrl });
    }

    [Authorize]
    public async Task Logout()
    {
        await HttpContext.SignOutAsync("Auth0", new AuthenticationProperties
        {
            RedirectUri = Url.Action("Index", "Home")
        });
        await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    }
}

Sorry for the formatting, but that sample has long of verbose statements. Nothing crazy going on in this file. There is an Index action that attempts to call the API and then returns a view. The Login and Logout functions do what they say and were pull right from the official docs.

The associated Index.cshtml file was added to the Views/AuthTest directory with the following.

@{
    ViewBag.Title = "Auth Test";
}

@if (User.Identity.IsAuthenticated)
{
    <a asp-controller="AuthTest" asp-action="Logout">Logout</a>
}
else
{
    <a asp-controller="AuthTest" asp-action="Login">Login</a>
}

<p>
    @ViewBag.ApiResults
</p>

This view just shows a link to login or logout and shows the results of the API call. It is ugly but is enough to prove the setup is working.

Wrapping Up

Getting up and running was much fast with Auth0 and would be true of any SASS option I’m sure. It also helped that I have more of an idea of what is going on after all the posts I did on Identity Server. Another positive is Auth0 has some great docs. I used the ASP.NET Core and Web API ones a lot to get this sample application up and running.

Next steps are to get this setup running in the Angular client which should be my next post. The finished code for this post can be found here.

 

Auth0: Introduction and Initial Project Setup Read More »

Identity Server: Upgrade Client to Angular 5

I have been working a lot on my basics sample project to explore some new client-side frameworks (React and Vue if you are interested). Since I have been away from the Identity Server sample for a while I thought it would be good to see what updates the project might need. It turns out that Angular was the big thing that was out of date. This post is going to cover the changes to get the project updated to Angular 5.

Package.json

In the Client App project open the package.json file and update the version of the @angular packages to at least the following version, and of course feel free to pin the exact version. I don’t just because of the nature of this sample.

"@angular/animations": "^5.0.0",
"@angular/common": "^5.0.0",
"@angular/compiler": "^5.0.0",
"@angular/compiler-cli": "^5.0.0",
"@angular/core": "^5.0.0",
"@angular/forms": "^5.0.0",
"@angular/http": "^5.0.0",
"@angular/platform-browser": "^5.0.0",
"@angular/platform-browser-dynamic": "^5.0.0",
"@angular/platform-server": "^5.0.0",
"@angular/router": "^5.0.0"

At this point, I tried to run and got an error about the version of rxjs being used. Instead of just blindly going package by package and seeing which versions were required I installed the Angular CLI and created a new Angular 5 application and used it as an example of what version I needed. Use the following commands if you would like to follow the same process since the current versions have changed by the time you are reading this post.

npm install -g @angular/cli
ng new sample-app

The above led me to the following version changes.

"rxjs": "^5.5.2",
"zone.js": "^0.8.14"

Attempt 2

At this point, I tried running the application again and received the following error.

Error: Version of @angular/compiler-cli needs to be 2.3.1 or greater. Current version is “5.0.1”.

As you can imagine I was surprised that 5.0.1 < 2.3.1. Turns out this is related to the version of @ngtools/webpack. This package deals with ahead-of-time compiling which my sample application uses, but the application I generated using the Angular CLI doesn’t. Updating to the following version cleared up the issue.

"@ngtools/webpack": "^1.8.0"

Open ID Connect Client

There was a much new version of the Open ID Connect Client that the Angular application is using so I upgrade it as well to the following version.

"angular-auth-oidc-client": "3.0.4"

This version dropped the startup_route so the following line had to be removed from the AuthService class.

openIdImplicitFlowConfiguration.startup_route = '/home';

Final Steps

Now that the package versions are sorted run the following commands from a command prompt to make sure that all the new version are installed and in the proper places.

npm install
node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js
node node_modules/webpack/bin/webpack.js

Wrapping Up

I am glad to have this upgrade done. It seems that every time I do one of these upgrades I end up down some rabbit hole. On the plus side, I seem to be getting faster at resolving the rabbit hole issues, or the frameworks have made a lot of progress on making sure the upgrade processes are simpler than they used to be. Either way, I get to expand my knowledge. I just need to schedule a bit more time before attempting upgrades.

The code in its finished state can be found here.

Identity Server: Upgrade Client to Angular 5 Read More »

Identity Server: Deploy to Azure

This post is going to cover taking the existing set of applications we have been using to learn about Identity Server and deploying them to Azure. The starting point of the code can be found here.

Prep Work

The applications as they stand from the link above are not ready to be pushed to Azure most due to some configuration changes that are needed. We will go through each of the applications and take the hard-coded values and move them to appsettings.json.

API Application Configuration

The API application needs two configuration values for the address of the Identity Application and the address of the Client Application. The following two lines need to be added to the application’s appsettings.json file.

"IdentityServerAddress": "http://localhost:5000",
"ClientAddress": "http://localhost:5002"

Then in the Startup class, the values need to be used. The Identity Server address is used in the JWT Bearer setup.

Before:
o.Authority = "http://localhost:5000";

After:
o.Authority = Configuration["IdentityServerAddress"];

Then the Client address is used in the CORS setup.

Before:
policy.WithOrigins("http://localhost:5002")

After:
policy.WithOrigins(Configuration["ClientAddress"])
Identity Application Configuration

The Identity application needs a configuration value for the address of the address of the Client Application. The following line needs to be added to the application’s appsettings.json file.

"ClientAddress": "http://localhost:5002"

Next, the Config class needs a reference to configuration passed into the GetClients function.

public static IEnumerable<Client> GetClients(IConfiguration configuration)

Next, the references to http://localhost:5002 need to be replaced with the value from the configuration. The following is one example.

Before:
RedirectUris = { "http://localhost:5002/signin-oidc" },

After:
RedirectUris = { $"{configuration["ClientAddress"]}/signin-oidc" },
Identity Application Entity Framework

As part of publishing this set of applications, this example is going to use Azure SQL and right now the application is set up to use SQLite. In the Startup class replace UseSqlite with UseSqlServer. The following is an example of one of the needed replacements.

Before:
options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));

After:
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

When switching database providers make sure to delete and recreate your database migrations. I didn’t to begin with and it cost me a lot of time in changing down a strange error which this post covers.

Client Application Configuration

The Client application needs two configuration values for the address of the Identity Application and the address of the API Application. The following two lines need to be added to the application’s appsettings.json file.

"IdentityServerAddress": "http://localhost:5000",
"ApiAddress": "http://localhost:5001/"

Then in the Startup class, the Identity Server Address needs to be used in the AddOpenIdConnect call.

Before:
options.Authority = "http://localhost:5000";

After:
options.Authority = Configuration["IdentityServerAddress"];

Next, the configuration values need to be passed to the Angular application. This process ended up being harder to figure out that I had anticipated and turned into a full blog post on its own. See this post for the details. The code for all the changes will also be on GitHub in case you need to the the diff for the client application.

Publish to Azure

Right-click on the Identity Application and select Publish.

This will show the Publish screen which provides the option to publish to Azure. We don’t have an existing App Service so we are going to create a new one. This page in the official docs explains all the options available on the publish screen. Click the publish button to continue.

The next screen that shows is the Create App Service Screen. I used all the default values and created a new Resource Group and App Service Plan. Keep in mind that the resource group and plan will be reused for the remaining two applications we are looking deploy. The only thing that will change between the applications on this screen will be the App Name.

The services tab looks like the following.

Next in the additional resources box lets hit the plus button next to SQL Database since our group of applications is going to need somewhere to store data. This will take us to the Configure SQL Database screen.

Since I don’t already have a SQL Server setup I am going to hit the New button to add one. That results in the next screen where you enter a name for the server as well as a username and password. After entering the required information click OK.

This will put you back on the configure database screen with most of it filled out. Make sure to set the database name you want to use.

Finally back on the Create App Service screen, you will see all the resources that you selected and configured. When you are satisfied with what you see click the Create button and let Azure do its magic.

When it is done you will see the profile now listed on the Publish page.

The above needs to be repeated for both the API and Client Applications, but using the Resource Group and App Service plan created above. Each profile should use a unique application name.

Identity Application Azure Configuration

The Identity Application needs access to the database that we created above. This means we need to set the DefaultConnection. The first step is to determine what the connection string should be. On the Azure Portal in your list of resources select the SQL database that we created above.

On the next page copy the provided connection string. Now navigate to the Identity App Service and under the Settings section select Application settings. Scroll down and find the Connection strings section and enter the copied value as the DefaultConnection.

Just above the Connection strings section we also need to enter a few values in the App settings section. For the Identity Application, we need the Twitter key and secret as well as the address of the client application. The following is a screenshot minus the actual values.

For the ClientAddress use the URL found in the Overview of the Client App’s App Service page.

API Application Azure Configuration

From the list of resources select the API App’s App Service page and in the Settings section select Application settings. In the App settings section add values for IdentityServerAddress and ClientAddress. As with the ClientAddress above the URLs for each application can be found on their respective App Service pages.

Client Application Azure Configuration

From the list of resources select the Client App’s App Service page and in the Settings section select Application settings. In the App settings section add values for IdentityServerAddress and ApiAddress.

Wrapping Up

At this point, you should be able to load up the application at the client address provided by Azure and have a working application. Overall the deployment to Azure was pretty easy. Getting the applications prepared to be deployed was a bit more challenging and sent me down a couple of rabbit holes. The code in its final state can be found here.

Identity Server: Deploy to Azure Read More »