.NET Core

Pass ASP.NET Core Appsettings Values to Angular

As part of getting my set of Identity Server 4 sample applications to run in Azure, I needed a way in the Client Application to pass some configuration values from appsettings.json to the Angular front end that could be used both during server-side rendering and client-side rendering. This application is using JavaScriptServices. This solution may need tweaking if your application isn’t using JavaScriptServices. The code for the client application can be found here.

Settings

In this example, we need to pass the address of our Identity Server and API from appsettings.json to Angular. The following is the settings file for this example.

{
  "Logging": {
    "IncludeScopes": false,
    "Debug": {
      "LogLevel": {
        "Default": "Warning"
      }
    },
    "Console": {
      "LogLevel": {
        "Default": "Warning"
      }
    }
  },
  "IdentityServerAddress": "http://localhost:5000",
  "ApiAddress": "http://localhost:5001/"
}

Providing Configuration Data to Angular

In this application, Angular is loaded from the index action of the home controller. This view can be found in the Views/Home folder in the Index.cshtml file. The following is the file before any changes.

@{
    ViewData["Title"] = "Home Page";
}

<app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>

<script src="~/dist/vendor.js" asp-append-version="true"></script>
@section scripts {
    <script src="~/dist/main-client.js" asp-append-version="true"></script>
}

The first change needed is to inject the configuration data using ASP.NET Core’s DI system. Add the following two lines at the top of the file.

@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration

Now the configuration data from the application is available to this view. Next, we need to pull a couple of values out of the configuration data and pass it to the Angular application. To do this we are going to use the asp-prerender-data tag helper. You can read more about it in the official docs. The idea is you construct an object which is then serialized and stored in params.data. In our example, we are passing the URLs for the Identity and API Applications.

<app asp-prerender-module="ClientApp/dist/main-server"
     asp-prerender-data='new {
    apiUrl = Configuration["ApiAddress"],
    identityUrl = Configuration["IdentityServerAddress"]
}'>Loading...</app>

The above is creating a new object with an apiUrl property and an identityUrl property. The following is the full completed view for reference.

@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration
@{
    ViewData["Title"] = "Home Page";
}

<app asp-prerender-module="ClientApp/dist/main-server"
     asp-prerender-data='new {
    apiUrl = Configuration["ApiAddress"],
    identityUrl = Configuration["IdentityServerAddress"]
}'>Loading...</app>

<script src="~/dist/vendor.js" asp-append-version="true"></script>
@section scripts {
    <script src="~/dist/main-client.js" asp-append-version="true"></script>
}

Angular Server-Side Boot

When Angular gets prerendered on the server-side it runs the code in the boot.server.ts file. This is where we will set up the providers needed on for the server side prerender. This is the bit that I missed for the longest time when trying to get this example going. I kept trying to find a way to add the providers in the app.module.server.ts file. Add any providers you need to the providers constant. For example, the following is passing URLs for an API and Identity Server in addition to the defaults provided by JavaScriptServices.

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: 'API_URL', useValue: params.data.apiUrl },
    { provide: 'IDENTITY_URL', useValue: params.data.identityUrl }
];

Lower in the same file we can pass through the configuration values to the client side render as globals on the window object. To do this add a globals property to the object being passed to the resolve call.

return new Promise<RenderResult>((resolve, reject) => {
    zone.onError.subscribe((errorInfo: any) => reject(errorInfo));
    appRef.isStable.first(isStable => isStable).subscribe(() => {
        // Because 'onStable' fires before 'onError', we have to delay slightly before
        // completing the request in case there's an error to report
        setImmediate(() => {
            resolve({
                html: state.renderToString(),
                globals: {url_Config: params.data}
            });
            moduleRef.destroy();
        });
    });
});

The above will have the URLs as part of a single object, but you could have each URL as its own property if you prefer.

Angular Client-Side

Now that the server-side has providers for API URL and Identity URL we need to provide the client-side with the same capabilities. These changes will be in the app.module.browser.ts file. The first step is to add providers for each.

providers: [
    { provide: 'ORIGIN_URL', useFactory: getBaseUrl },
    { provide: 'API_URL', useFactory: apiUrlFactory },
    { provide: 'IDENTITY_URL', useFactory: identityUrlFactory },
    AppModuleShared
]

Next, we need functions to return the URLs from the url_Config property of the window object which the following two functions do.

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

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

Wrapping Up

With the above, you can now use your configuration values from ASP.NET Core and pass them through to your Angular application. In hindsight, the process is pretty simple, but getting to that point took me much longer to figure out than I would like to admit. I hope this post saves you some time!

Pass ASP.NET Core Appsettings Values to Angular Read More »

Identity Server: API Migration to ASP.NET Core 2

After writing the basic migration guide from ASP.NET Core 1.1.x to 2.0 I embarked on the task of upgrading the rest of the projects I have on GitHub. For the most part, it has been a pretty smooth transition. This post is going cover the differences that I hit while converting an API that is part of my IdentityServer sample project. This assumes that you have already followed my other migration post which can be found here.

Package Changes

The source of this conversion being different is that the IdentityServer4.AccessTokenValidation NuGet package is not currently supported on ASP.NET Core 2. Token validation can be done using bits provided by the framework. This is the recommended path suggested by the IdentityServer team as posted on this issue. Longer term you may want to switch back if you have a need for more features not provided by the Microsoft implementation as pointed out in this issue.

As for the actual change, just remove the reference to IdentityServer4.AccessTokenValidation from your project using the NuGet UI, Package Manager Console, or by editing the csproj file.

Startup

All the rest of the changes are in the Startup class. First, in the Configure function app.UseIdentityServerAuthentication gets replaced with app.UseAuthentication.

Before:
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
    Authority = "http://localhost:5000",
    RequireHttpsMetadata = false,
    ApiName = "apiApp"
});

After:
app.UseAuthentication();

In the ConfigureServices function is now where JWT Bearer options are set up. First, we have to add the type of authentication the API is going to use and then the options for JWT Bearer are set, which will match the settings that were being used before with the IdentityServer package.

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = 
                               JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = 
                               JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
    o.Authority = "http://localhost:5000";
    o.Audience = "apiApp";
    o.RequireHttpsMetadata = false;
});

Wrapping up

With the above, your API can run on ASP.NET Core 2 and still verify authorization using IdentityServer4. My IdentityServer sample project is taking the longest to update so I would expect at least one or two more posts on the process as each of the projects gets upgraded.

Identity Server: API Migration to ASP.NET Core 2 Read More »

All Migrations are not Created Equal

While trying to deploy my sample Identity Server set of applications to Azure I got the following error when the Entity Framework migrations attempted to run.

System.Data.SqlClient.SqlException (0x80131904): Column 'Id' in table 'AspNetRoles' is of a type that is invalid for use as a key column in an index

This was not something I would get when attempting to run locally, but it failed every time when using SQL Azure. Long store short is that the migrations that were trying to be applied were created when I was using Sqlite as a backing store (UseSqlite).

I deleted all the migrations and recreated them with the app being aware that it would be running on SQL Server (UseSqlServer) and all worked as it should. It makes total sense that the migrations would vary based on the data provider being used, but not something I had really thought about. Not something I will forget again.

All Migrations are not Created Equal Read More »

Create a .NET Standard Library with Visual Studio 2017

It has been almost a year since I wrote this post on creating a .NET Standard Library which was before Visual Studio 2017 was released. This post is going to cover the same basic idea, but for the new tooling which has vastly simplified the process.

.NET Standard Library

The .NET Standard Library specifies what .NET APIs are available based on the version of the .NET Standard Library being implemented. The following is a comparison to portable class libraries that really helped me understand the difference. This was pulled from the .NET Standard Library link above.

.NET Standard Library can be thought of as the next generation of Portable Class Libraries (PCL). The .NET Standard Library improves on the experience of creating portable libraries by curating a standard BCL and establishing greater uniformity across .NET runtimes as a result. A library that targets the .NET Standard Library is a PCL or a “.NET Standard-based PCL”. Existing PCLs are “profile-based PCLs”.

The .NET Standard Library and PCL profiles were created for similar purposes but also differ in key ways.

Similarities:

  • Defines APIs that can be used for binary code sharing.

Differences:

  • The .NET Standard Library is a curated set of APIs, while PCL profiles are defined by intersections of existing platforms.
  • The .NET Standard Library linearly versions, while PCL profiles do not.
  • PCL profiles represents Microsoft platforms while the .NET Standard Library is agnostic to platform.

Create a .NET Standard Library

In Visual Studio click File > New > Project.

This will launch the New Project dialog. Find the .NET Standard templates and select Class Library.

Now that the library has been created right click on the project and go to properties. On the Application tab, there is an option to for Target framework which currently defaults to .NETStandard 1.4. Depending on your platform your library needs to support will decide which version of the .NET Standard you need to use.

See the chart on this page for help with picking a version of the standard. The lower the version of the standard you target the more platforms your library will run on, but keep in mind the lower the version the smaller the API surface that is available.

Wrapping up

This process is so much easier than it was in Visual Studio 2015. The tooling around .NET Core and .NET Standard has gotten so much better. This type of library is the future and I highly recommend using .NET Standard when you have the option over portable class libraries.

Create a .NET Standard Library with Visual Studio 2017 Read More »

Identity Server: External Authentication using Twitter

This post is going to cover adding authentication using Twitter to the same project that has been used in all of my IdentityServer examples. The same basic idea would apply to almost any third party authentication setup so this should give you a good starting point for any integration. The starting point of the code can be found here.

Create Twitter App

Before any code changes create a new application on Twitter via this page. Click Create New App to begin the process.

On the Create an application page enter all the requested information. Note that the website won’t allow a localhost address. If you don’t have a real address for your application just enter a random URL as I did here. When finished click Create your Twitter application.

Now that we have an application click on the Keys and Access Tokens tab. We will need both the Consumer Key and Consumer Secret when we get to the Identity Application.

Identity Application Changes

Now that we have a Twitter application ready to go let us dive into the changes needed to the Identity Application. The first step is to add a reference to Microsoft.AspNetCore.Authentication.Twitter via NuGet.

Next in the ConfigureServices function of the Startup class after app.UseIdentityServer() add the following.

app.UseTwitterAuthentication(new TwitterOptions
{
    AuthenticationScheme = "Twitter",
    DisplayName = "Twitter",
    SignInScheme = "Identity.External",
    ConsumerKey = Configuration["Authentication:Twitter:ConsumerKey"],
    ConsumerSecret = Configuration["Authentication:Twitter:ConsumerSecret"]
});

The first three options should a straight forward enough. The next two are the values from the Twitter application I mentioned above. In this example, I am storing the values using User Secrets which get pulled out of configuration. For more details on how to set up secrets, you can see this post.

The above are all the changes required. The Identity Application will now allow users to auth using Twitter.

Logging in using Twitter

As you can see below the login page now has a button for Twitter.

When the user chooses to log in using Twitter they are shown the following page where they must approve access to their Twitter account from your application.

If this is the first time a user has logged in with Twitter they will be prompted to enter an email address to finish registration.

Wrapping up

As you can see adding external authentication is super simple. Check out the Microsoft Docs on Twitter Auth (ASP.NET Core 2.0 so look out for differences if you are not on the preview bits) and IdentityServer Docs on External Auth for more information.

The finished code can be found here.

 

Identity Server: External Authentication using Twitter Read More »

Identity Server: Changing Angular OpenID Connect Clients

Thanks to Andrew Stegmaier opening this issue on the repo that goes with my IdentityServer exploration I was made aware of a certified OpendID Connect client specifically written for Angular (4+). The angular-auth-oidc-client was created by damienbod. This post is going to cover the transition to this new client. The starting point of the code can be found here. All the changes discussed in this post take place in the ClientApp project.

Package Changes

In package.json the following changes need to be made using your package manager of choice or manually changing the fill and doing a restore.

Remove:
"oidc-client": "1.3.0",
"babel-polyfill": "6.23.0"

Add:
"angular-auth-oidc-client": "^1.3.1"

App Module Changes

Both app.module.client.ts and app.module.server.ts got a little cleanup to remove the duplicate provider code. The following lines were deleted from both files.

import { AuthService } from './components/services/auth.service';		
import { GlobalEventsManager } from './components/services/global.events.manager';		
import { AuthGuardService } from './components/services/auth-guard.service';

The providers array moved to using providers imported from app.module.shared.ts.

Before:
providers: [
    AuthService, AuthGuardService, GlobalEventsManager
]

After:
providers: [
    ...sharedConfig.providers
]

Next, in app.module.shared.ts the following imports were removed.

import { CallbackComponent } from './components/callback/callback.component';
import { GlobalEventsManager } from './components/services/global.events.manager';

Then, the following import for the OpenId Connect client was added.

import { AuthModule } from 'angular-auth-oidc-client';

In the declarations array CallbackComponent was removed. In the imports array AuthModule.forRoot() was added. The route for CallbackComponent was removed and the canActivate condition was removed from the fetch-data route. Finally, the providers section is reduced to only the AuthService. That was a lot of changes, so I am including the full finished class below.

import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';

import { AppComponent } from './components/app/app.component'
import { NavMenuComponent } from './components/navmenu/navmenu.component';
import { HomeComponent } from './components/home/home.component';
import { FetchDataComponent } from './components/fetchdata/fetchdata.component';
import { CounterComponent } from './components/counter/counter.component';
import { UnauthorizedComponent } from './components/unauthorized/unauthorized.component';

import { AuthModule } from 'angular-auth-oidc-client';
import { AuthService } from './components/services/auth.service';

export const sharedConfig: NgModule = {
    bootstrap: [ AppComponent ],
    declarations: [
        AppComponent,
        NavMenuComponent,
        CounterComponent,
        FetchDataComponent,
        HomeComponent,
        UnauthorizedComponent
    ],
    imports: [
        AuthModule.forRoot(),
        RouterModule.forRoot([
            { path: '', redirectTo: 'home', pathMatch: 'full' },
            { path: 'home', component: HomeComponent },
            { path: 'unauthorized', component: UnauthorizedComponent },
            { path: 'counter', component: CounterComponent },
            { path: 'fetch-data', component: FetchDataComponent  },
            { path: '**', redirectTo: 'home' }
        ])
    ],
    providers: [ AuthService ]
};

File Deletions

The following files were completely removed. Some of them may come back in a different form, but for the moment the functions they were handling are being dealt with in a different way.

auth-guard.service.ts
callback.component.ts
global.events.manager.ts

Auth Service

The AuthService class was pretty much rewritten since it is at the core of the interaction with the OpenId Connect client. It still contains pretty much all the functionality as before just using the new client. The following is most of the class. I removed all of the HTTP calls except for get to save space.

import { Injectable, Component, OnInit, OnDestroy } from '@angular/core';
import { Http, Headers, RequestOptions, Response } from '@angular/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: Http) {

        const openIDImplicitFlowConfiguration = new OpenIDImplicitFlowConfiguration();
        openIDImplicitFlowConfiguration.stsServer = 'http://localhost:5000';

        openIDImplicitFlowConfiguration.redirect_url = 'http://localhost:5002/callback';
        // The Client MUST validate that the aud (audience) Claim contains its client_id value registered at the Issuer identified by the iss (issuer) Claim as an audience.
        // The ID Token MUST be rejected if the ID Token does not list the Client as a valid audience, or if it contains additional audiences not trusted by the Client.
        openIDImplicitFlowConfiguration.client_id = 'ng';
        openIDImplicitFlowConfiguration.response_type = 'id_token token';
        openIDImplicitFlowConfiguration.scope = 'openid profile apiApp';
        openIDImplicitFlowConfiguration.post_logout_redirect_uri = 'http://localhost:5002/home';
        openIDImplicitFlowConfiguration.start_checksession = true;
        openIDImplicitFlowConfiguration.silent_renew = true;
        openIDImplicitFlowConfiguration.startup_route = '/home';
        // HTTP 403
        openIDImplicitFlowConfiguration.forbidden_route = '/forbidden';
        // HTTP 401
        openIDImplicitFlowConfiguration.unauthorized_route = '/unauthorized';
        openIDImplicitFlowConfiguration.log_console_warning_active = true;
        openIDImplicitFlowConfiguration.log_console_debug_active = false;
        // id_token C8: The iat Claim can be used to reject tokens that were issued too far away from the current time,
        // limiting the amount of time that nonces need to be stored to prevent attacks.The acceptable range is Client specific.
        openIDImplicitFlowConfiguration.max_id_token_iat_offset_allowed_in_seconds = 10;

        this.oidcSecurityService.setupModule(openIDImplicitFlowConfiguration);
    }

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

        if (window.location.hash) {
            this.oidcSecurityService.authorizedCallback();
        }
    }

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

    authorizedCallback() {
        this.oidcSecurityService.authorizedCallback();
    }

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

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

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

    get(url: string, options?: RequestOptions): Observable<Response> {
        if (options) {
            options = this.setRequestOptions(options);
        }
        else {
            options = this.setRequestOptions();
        }
        return this.http.get(url, options);
    }

    private setRequestOptions(options?: RequestOptions) {
        if (options) {
            this.appendAuthHeader(options.headers);
        }
        else {
            options = new RequestOptions({ headers: this.getHeaders(), body: "" });
        }
        return options;
    }

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

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

        if (token == '') return;

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

It doesn’t show the best in the world here so be sure and check it out on GitHub. All the IdentityServer configuration is done in the constructor using the OpenIDImplicitFlowConfiguration class.

Navigation Component

The NavMenuComponent class now needs some changes to match the new AuthService. First, the following change to the imports.

Before:
import { Component } from '@angular/core';
import { AuthService } from '../services/auth.service'
import { GlobalEventsManager } from '../services/global.events.manager'

After:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';
import { AuthService } from '../services/auth.service';

The AuthService class now provides the ability to subscribe to changes in the user’s authorization. To handle the subscription and unsubscription the class will implement both OnInit and OnDestroy. Here is the new class declaration.

export class NavMenuComponent implements OnInit, OnDestroy

Next, here is the implementation of ngOnInit which handles the subscription to the change in isAuthorized.

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

    if (window.location.hash) {
        this.authService.authorizedCallback();
    }
}

Then, ngOnDestroy handles the unsubscription.

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

The class level variable for _loggedIn is replaced with the following two variables.

isAuthorizedSubscription: Subscription;
isAuthorized: boolean;

The constructor has been greatly simplified and now only takes an instance of the AuthService.

constructor(public authService: AuthService) {
}

Finally, the login and logout functions have changed to match the new function names in the AuthService class.

public login() {
    this.authService.login();
}

public logout() {
    this.authService.logout();
}

Navigation Component UI

In the navmenu.component.html file, a couple of tweaks are required based on the new variable names used above. The first set is related to showing either Login or Logout.

Before:
<li *ngIf="!_loggedIn" [routerLinkActive]="['link-active']">
    <a (click)="login()" [routerLink]="['/login']">
        <span class="glyphicon glyphicon-user"></span> Login
    </a>
</li>

<li *ngIf="_loggedIn" [routerLinkActive]="['link-active']">
    <a (click)="logout()" [routerLink]="['/logout']">
        <span class='glyphicon glyphicon-log-out'></span> Logout
    </a>
</li>

After:
<li [routerLinkActive]="['link-active']">
    <a *ngIf="!isAuthorized" (click)="login()" [routerLink]="['/login']">
        <span class="glyphicon glyphicon-user"></span> Login
    </a>
</li>

<li [routerLinkActive]="['link-active']">
    <a *ngIf="isAuthorized" (click)="logout()">
        <span class='glyphicon glyphicon-log-out'></span> Logout</a>
</li>

The final change in this file was to make the link to fetch-data only show if the user is logging instead of sending the user to an unauthorized view.

Before:
<a [routerLink]="['/fetch-data']">
    <span class='glyphicon glyphicon-th-list'></span> Fetch data
</a>

After:
<a *ngIf="isAuthorized" [routerLink]="['/fetch-data']">
    <span class='glyphicon glyphicon-th-list'></span> Fetch data
</a>

Fetch Data Component

The final changes for the conversion to the new client are in the fetchdata.component.ts and they are only needed because of a rename of the HTTP Get helper in the AuthService.

Before:
authService.AuthGet(apiUrl + 'SampleData/WeatherForecasts').subscribe(result => {

After:
authService.get(apiUrl + 'SampleData/WeatherForecasts').subscribe(result => {

Wrapping Up

This change took a lot of changes, but in the long run, it is going to be a better choice since the new client is focused on Angular. Another great thing about this client is they are looking into ways to handle the first load not remembering the user is logged in due to server side rendering (issue #36).

The finished code for this post can be found here.

Identity Server: Changing Angular OpenID Connect Clients Read More »

Migration from ASP.NET Core 1.1.x to 2.0

On August 14th .NET Core 2.0 was released including corresponding versions of ASP.NET Core 2.0 and Entity Framework Core 2.0 which got with the finalization of .NET Standard 2.0. The links take you to the release notes for each item.

In this post, I will be covering taking the project used for the ASP.NET Basics series from 1.1.x to the 2.0 release. The starting point of the code can be found here. This post is only going to cover conversion of the Contacts project.

Installation

If you are a Visual Studio user make sure you have the latest version of Visual Studio 2017, which can be found here and at a minimum should be version 15.3.

Next, install the SDK for your operating system. The list of installs can be found here. For development, it is key that you install the SDK, not just the runtime. The following is a preview of what to expect on the download page.

Csproj

The csproj file of the project being upgraded is the best place to start the conversion. The TargetFramework needs to be changed to 2.0.

Before:
<TargetFramework>netcoreapp1.1</TargetFramework>

After:
<TargetFramework>netcoreapp2.0</TargetFramework>

Next, PackageTargetFallback changed to AssetTargetFallback.

Before:
<PackageTargetFallback>$(PackageTargetFallback);dotnet5.6;portable-net45+win8</PackageTargetFallback>

After:
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81</AssetTargetFallback>

There is a new Microsoft.AspNetCore.All package that bundles up what used to be a huge list of individual packages. Those individual packages still exist, but this new one wraps them and makes it much easier to get started. The following is the package list before and after.

Before:
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="1.1.1" />		
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="1.1.1" />		
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="1.1.1" />		
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />		
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.1" />		
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.1" />		
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.1.1" />		
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.1" />		
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.1" />		
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.0" />		
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.1" />		
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.1" />		
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="1.1.1" />		
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.1" />		
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.1" />		
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" />		
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.1" />		
<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="1.1.0" />		
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="1.1.0" />		

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

Last change in this file is to change the DotNetCliToolReference versions to 2.0.0.

Before:
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />
<DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="1.0.0" />
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.0" />

After:
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
<DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="2.0.0" />
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />

Program.cs

Program.cs is another area that has been simplified by creating a default builder that does all the same things that were happening before but hide the details. Keep in mind the old version still works and is valid to use if you use case needs it.

Before:
public static void Main(string[] args)
{
    var host = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .UseStartup<Startup>()
        .Build();

    host.Run();
}

After:
public static void Main(string[] args)
{
    BuildWebHost(args).Run();
}

public static IWebHost BuildWebHost(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
           .UseStartup<Startup>()
           .Build();

Identity

The remaining changes I had to make were all related to Identity. In the Startup class’s Configure function the following change was needed.

Before:
app.UseIdentity();

After:
app.UseAuthentication();

Next, in the ManageLoginsViewModel class, the type of the OtherLogins property changed.

Before:
public IList<AuthenticationDescription> OtherLogins { get; set; }

After:
public IList<AuthenticationScheme> OtherLogins { get; set; }

The SignInManager dropped the GetExternalAuthenticationSchemes function in favor of GetExternalAuthenticationSchemesAsync. This caused changes in a couple of files. First, in the ManageController the following change was made.

Before:
var otherLogins = _signInManager
                  .GetExternalAuthenticationSchemes()
                  .Where(auth => userLogins
                                 .All(ul => auth.AuthenticationScheme != ul.LoginProvider))
                  .ToList();

After:
var otherLogins = (await _signInManager
                   .GetExternalAuthenticationSchemesAsync())
                  .Where(auth => userLogins
                                 .All(ul => auth.Name != ul.LoginProvider))
                  .ToList();

The second set of changes were in the Login.cshtml file. First the function change.

Before:
var loginProviders = SignInManager.GetExternalAuthenticationSchemes().ToList();

After:
var loginProviders = (await SignInManager.GetExternalAuthenticationSchemesAsync()).ToList();

Then the change to deal with the changed property names.

Before:
<button type="submit" class="btn btn-default" 
        name="provider" value="@provider.AuthenticationScheme" 
        title="Log in using your @provider.DisplayName account">
    @provider.AuthenticationScheme
</button>

After:
<button type="submit" class="btn btn-default" 
        name="provider" value="@provider.Name" 
        title="Log in using your @provider.DisplayName account">
    @provider.Name
</button>

Wrapping up

With the changes in the Contacts project now works on ASP.NET Core 2.0!  Make sure to check out Microsoft’s regular migration guide. as well as their identity migration guide. A full list of breaking changes for this release can be found here.

There is a lot more to explore with this new release and I have a lot of projects to update. Don’t worry I won’t be doing a blog post on all of them, but if I do hit any issues I will create a new post of update this one with the fixes. The finished code can be found here.

Migration from ASP.NET Core 1.1.x to 2.0 Read More »

Identity Server: Redirect When Route Requires Logged in User

This post is going to continue where the series on IdentityServer4 left off, but I am not officially making it part of the series. There may be a few posts like this where I improve on the example applications from the series. The starting code for this post can be found here.

All the changes in the post are in the Client Application from the sample linked above. I did some cleanup on a couple of files so if you are looking for the differences keep in mind most of the changes are a result of the cleanup.

Unauthorized Component

The first step is to add a new component that will be shown to the user when they navigate to a page that requires them to be logged in but they are not. Add a unauthorized.component.html file to the ClientApp/app/components/unauthorized/ directory with the following contents.

<h2>
    Login is required to access this area
</h2>
<div>
    <button type="button" 
            class="btn btn-primary" 
            (click)="login()">Login</button>
    <button type="button" 
            class="btn btn-default" 
            (click)="goback()">Back</button>
</div>

This will tell the user they need to log in and provide a login button and a button to go back to the previous page. Next, add a unauthorized.component.ts file to the same directory. This class will handle the clicks from the view.

import { Component, OnInit } from '@angular/core';
import { Location } from '@angular/common';
import { AuthService } from '../services/auth.service';

@Component({
    selector: 'app-unauthorized',
    templateUrl: 'unauthorized.component.html'
})
export class UnauthorizedComponent implements OnInit {

    constructor(private location: Location, private service: AuthService) {

    }

    ngOnInit() {
    }

    login() {
        this.service.startSigninMainWindow();
    }

    goback() {
        this.location.back();
    }
}

This class is using the AuthService for login and Angular’s Location class to move back to the previous page.

New Component Usage

Now that this new component exists it needs to set up in app.module.shared.ts. First, add an import.

import { UnauthorizedComponent } from './components/unauthorized/unauthorized.component';

Next, add to the declarations array.

declarations: [
    AppComponent,
    NavMenuComponent,
    CounterComponent,
    FetchDataComponent,
    HomeComponent,
    CallbackComponent,
    UnauthorizedComponent
]

Finally, add unauthorized to the routes array.

RouterModule.forRoot([
    { path: '', redirectTo: 'home', pathMatch: 'full' },
    { path: 'home', component: HomeComponent },
    { path: 'callback', component: CallbackComponent },
    { path: 'unauthorized', component: UnauthorizedComponent },
    { path: 'counter', component: CounterComponent },
    { path: 'fetch-data', component: FetchDataComponent, 
                          canActivate:[AuthGuardService]  },
    { path: '**', redirectTo: 'home' }
])

Now that this new component is in place how does it get used? Well, any route that has canActivate:[AuthGuardService] will require the user to be logged in to activate. For example, the fetch-data route above won’t activate unless the user is logged in.

Auth Guard Service

AuthGuardService is an existing class in the project. The following is the full file.

import { Injectable, Component } from '@angular/core';
import { CanActivate, Router } from '@angular/router';

import { AuthService } from './auth.service';

@Injectable()
export class AuthGuardService implements CanActivate {

    constructor(private authService: AuthService, private router: Router) {
    }

    canActivate() {
        if (this.authService.loggedIn) {
            return true;
        }
        else {
            this.router.navigate(['unauthorized']);
        }
    }
}

As you can see in the canActivate function if the user is logged in then the function returns true otherwise, the user is routed to the unauthorized component. Before the changes in this post, this dropped the user back on the home page since the unauthorized component didn’t exist.

Wrapping up

With the changes above the user gets a slightly better experience. Just being dropped on the home page wasn’t very helpful as to why that was happening. This at least lets the user know they need to log in. Another option could be to hide the navigation for the routes they don’t have access to until they log it.

The finished version of the code can be found here.

Identity Server: Redirect When Route Requires Logged in User Read More »

Identity Server: Calling Secured API from Angular

This post is a continuation of a series of posts that follow my initial looking into using IdentityServer4 in ASP.NET Core with an API and an Angular front end. The following are the related posts.

Identity Server: Introduction
Identity Server: Sample Exploration and Initial Project Setup
Identity Server: Interactive Login using MVC
Identity Server: From Implicit to Hybrid Flow
Identity Server: Using ASP.NET Core Identity
Identity Server: Using Entity Framework Core for Configuration Data
Identity Server: Usage from Angular

This post is going to take the solution from last week, the code can be found here, and add an example of the Client Application (Angular) calling an endpoint on the API Application that requires a user with permissions.

API Application

To provide an endpoint to call with minimal changes this example just moves the SampleDataController from the Client Application to the API Application. The following is the full class.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace WebApplicationBasic.Controllers
{
    [Route("api/[controller]")]
    [Authorize]
    public class SampleDataController : Controller
    {
        private static string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", 
            "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

        [HttpGet("[action]")]
        public IEnumerable<WeatherForecast> WeatherForecasts()
        {
            var rng = new Random();
            return Enumerable.Range(1, 5)
                             .Select(index => new WeatherForecast
            {
                DateFormatted = DateTime.Now.AddDays(index).ToString("d"),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            });
        }

        public class WeatherForecast
        {
            public string DateFormatted { get; set; }
            public int TemperatureC { get; set; }
            public string Summary { get; set; }

            public int TemperatureF
            {
                get
                {
                    return 32 + (int)(TemperatureC / 0.5556);
                }
            }
        }
    }
}

Make special note that this class now has the Authorize attribute applied which is the only change that was made when moving the file from the Client Application. This attribute is what will require an authorized user for all the routes this controller services.

Client Application

In the Client Application, the first step is to remove the SampleDataController since it is now in the API Application.

Next, in the app.module.client.ts file, add a new provider which can be used to supply the URL of the API to the rest of the Client Application. Don’t take this as best practices for injecting configuration data it is just an easy way to handle it in this application. The following is the full class without the imports (which haven’t changed) the new item is the API_URL.

@NgModule({
    bootstrap: sharedConfig.bootstrap,
    declarations: sharedConfig.declarations,
    imports: [
        BrowserModule,
        FormsModule,
        HttpModule,
        ...sharedConfig.imports
    ],
    providers: [
        { provide: 'ORIGIN_URL', useValue: location.origin },
        { provide: 'API_URL', useValue: "http://localhost:5001/api/" },
        AuthService, AuthGuardService, GlobalEventsManager
    ]
})
export class AppModule {
}

Now for the changes that need to be made to the FetchDataComponent which is the class that will call the new API endpoint. First, add an import for the AuthService.

import { AuthService } from '../services/auth.service';

Next, there are a couple of changes to the signature of the constructor. The first is to use ‘API_URL’ instead of ‘ORIGIN_URL’. The second is to provide for injection of the AuthService. The following is a comparison between the version of the constructor signature.

Before:
constructor(http: Http, @Inject('ORIGIN_URL') originUrl: string)

After:
constructor(http: Http, @Inject('API_URL') apiUrl: string, authService: AuthService)

The final change is to use authService.AuthGet with the new URL instead of http.get.

Before:
http.get(originUrl + 
         '/api/SampleData/WeatherForecasts').subscribe(result => {
    this.forecasts = result.json() as WeatherForecast[];
});

After:
authService.AuthGet(apiUrl + 
                    '/SampleData/WeatherForecasts').subscribe(result => {
    this.forecasts = result.json() as WeatherForecast[];
});

With the above changes, the user has to be logged in or the API will respond with not authorized for the weather forecasts end point. The Client Application doesn’t have anything to provide the user with the fact they aren’t authorized at the moment, but that is outside the scope of this entry.

So far we haven’t look at the code in the AuthService class, but I do want to explain what the AuthGet function is doing and the related functions for put, delete, and post. These calls are wrappers around the standard Angular HTTP library calls that add authorization headers based on the logged in user. The following is the code of the AuthGet as well as two helper functions the class uses to add the headers.

AuthGet(url: string, options?: RequestOptions): Observable<Response> {
    if (options) {
      options = this._setRequestOptions(options);
    }
    else {
      options = this._setRequestOptions();
    }
    return this.http.get(url, options);
}

private _setAuthHeaders(user: User) {
    this._authHeaders = new Headers();
    this._authHeaders.append('Authorization', 
                             user.token_type + " " + user.access_token);
    this._authHeaders.append('Content-Type', 'application/json');
}

private _setRequestOptions(options?: RequestOptions) {
    if (options) {
      options.headers.append(this._authHeaders.keys[0],
                             this._authHeaders.values[0]);
    }
    else {
      //setting default authentication headers
      this._setAuthHeaders(this._currentUser);
      options = new RequestOptions({ headers: this._authHeaders, 
                                     body: "" });
    }
    return options;
}

Wrapping up

It feels like this application is finally getting to the point where other development could happen if it were more than a demo, which is exciting. My thought on how this could be used for real applications is the Identity Application would stand on its own and be used by many clients. The Client Application with a few more tweaks could be used as a template for Angular applications. The completed code can be found here.

This post finishes up the core of what I set out to learn about IdentityServer, but there could be more related posts as I continue to add some polish to the current implementation of the sample solution.

Identity Server: Calling Secured API from Angular Read More »

Identity Server: Usage from Angular

This post is a continuation of a series of posts that follow my initial looking into using IdentityServer4 in ASP.NET Core with an API and an Angular front end. The following are the related posts.

Identity Server: Introduction
Identity Server: Sample Exploration and Initial Project Setup
Identity Server: Interactive Login using MVC
Identity Server: From Implicit to Hybrid Flow
Identity Server: Using ASP.NET Core Identity
Identity Server: Using Entity Framework Core for Configuration Data
Identity Server: Usage from Angular (this post)

This post is finally going to add login from Angular in the Client Application. It has been a long time coming and will be a starting point, based on a few examples I found which I will list at the end. The starting point of the code can be found here.

API Application

In order for the Client Application to be able to call the API Application, there are some changes needed to allow cross-origin resource sharing. For more details check out this post only the basics will be covered here. First, add the following NuGet package.

  • Microsoft.AspNetCore.Cors

Next, in the ConfigureServices function of the Startup class add AddCors before AddMvc. The following is the full function. This allows calls to the API Application from the Client Application which is running on localhost on port 5002.

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy("default", policy =>
        {
            policy.WithOrigins("http://localhost:5002")
                .AllowAnyHeader()
                .AllowAnyMethod();
        });
    });

    services.AddMvc();
}

Then, in the Configure function add app.UseCors(“default”); to make sure the default policy defined above is enforced. The following is the full function.

public void Configure(IApplicationBuilder app, 
                      IHostingEnvironment env, 
                      ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    app.UseCors("default");

    app
      .UseIdentityServerAuthentication(
      new IdentityServerAuthenticationOptions
    {
        Authority = "http://localhost:5000",
        RequireHttpsMetadata = false,
        ApiName = "apiApp"
    });

    app.UseMvc();
}

Identity Application

The Identity Application doesn’t have a lot of changes, but some of the configuration between it and the Client Application is what took me the bulk of time getting the code for this post setup and going.

If you are keeping up with the series then last you will know last week all the configuration data was moved to a database using Entity Framework Core. This is a bit of a problem now that I need to add a new client and the configuration data doesn’t any associated UI. To work around this I just added the new client to the Config class in the GetClients function and then deleted the existing database and let Entity Framework recreate it based on the new seed data. Not optimal, but I didn’t want to complicate things by adding a UI for the client setup. The following is the new client.

new Client
{
    ClientId = "ng",
    ClientName = "Angular Client",
    AllowedGrantTypes = GrantTypes.Implicit,
    AllowAccessTokensViaBrowser = true,
    RequireConsent = true,

    RedirectUris = { "http://localhost:5002/callback" },
    PostLogoutRedirectUris = { "http://localhost:5002/home" },
    AllowedCorsOrigins = { "http://localhost:5002" },

    AllowedScopes =
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        "apiApp"
    },

}

There are a few things in this configuration that took me some time to get right. First of all best, I have been able to tell with this style of application implicit flow is the way to go which is handled by using a GrantTypes.Implicit for the AllowedGrantTypes.

The next issues I ran was a cross-origin resource sharing issue. Thankfully IdentityServer makes it easy to specify what origins should be allowed using the AllowedCorsOrigins property. In this example, we want to allow requests from the URL of our Client Application which is http://localhost:5002.

The last issue I had on was with the URIs I had set. The configuration in IdentityServer needs to exactly match the setup in the Client Application or you will have issues. I also had trouble trying to use the raw base address (http://localhost:5002) as the PostLogoutRedirectUris so look out for that as well.

Client Application

In the client application open the package.json file and add the following the dependencies section.

"oidc-client": "1.3.0",
"babel-polyfill": "6.23.0"

I also updated the typescript version to 2.3.4. Be cautious when changing the version of typescript as there is an issue with Angular and typescript 2.4.x at the moment.

At this point in the process, I had to find some resources on how to continue. The following are the ones I leaned on most.

ANGULAR OPENID CONNECT IMPLICIT FLOW WITH IDENTITYSERVER4
ASP.NET Core & Angular2 + OpenID Connect using Visual Studio Code
Repo for the previous link
Repo for with example Angular OidcClient

Getting this part of the application working involved a lot of changes and instead of going in depth on everything I am going to recommend just copying in the following files for the finished example code and dig more into them after you get an example working. Here is the list of files.

  • ClientApp/ClientApp/app/components/callback/callback.component.ts
  • ClientApp/ClientApp/app/components/services/ – whole directory
  • ClientApp/ClientApp/boot-server.ts – related to a typescript error only if needed

With the above files in place, we will now focus on using the functionality they provide to log in and protect routes. To begin  app.module.client.tsapp.module.server.ts and app.module.shared.ts all need the next set of changes. I haven’t tried it yet, but I bet this change could just be made in the shared file and used in the other two. Add the following imports.

import { AuthService } from './components/services/auth.service';
import { GlobalEventsManager } from './components/services/global.events.manager';
import { AuthGuardService } from './components/services/auth-guard.service';

Next, add the same three items to the array of providers (or add one if it doesn’t exist). The following is an example from the shared file.

 @NgModule({
     bootstrap: sharedConfig.bootstrap,		     
     declarations: sharedConfig.declarations,		     
     imports: [
         ServerModule,
         ...sharedConfig.imports
     ],
     providers: [
        AuthService, AuthGuardService, GlobalEventsManager 
     ]
 })

Finally, in the shared file change any routes that you would like to require the user to be logged in to be like the following which utilizes the canActivate of the route.

{ path: 'fetch-data', 
  component: FetchDataComponent, 
  canActivate:[AuthGuardService]  }

In the navmenu.component.html which is the UI for the navigation menu add the following two options to the unordered list.

<li *ngIf="!_loggedIn" [routerLinkActive]="['link-active']">
    <a (click)="login()" [routerLink]="['/login']">
       <span class="glyphicon glyphicon-user"></span> Login
    </a>
</li>
<li *ngIf="_loggedIn" [routerLinkActive]="['link-active']">
    <a (click)="logout()" [routerLink]="['/logout']">
       <span class='glyphicon glyphicon-log-out'></span> Logout
    </a>
</li>

The user will only ever see one of the above options based on being logged in or not which is what the *ngIf is doing.

The navigation view model (navmenu.component.ts) changed a bit more. The following is the complete file.

import { Component } from '@angular/core';
import { AuthService } from '../services/auth.service'
import { GlobalEventsManager } from '../services/global.events.manager'

@Component({
    selector: 'nav-menu',
    templateUrl: './navmenu.component.html',
    styleUrls: ['./navmenu.component.css']
})
export class NavMenuComponent {
    public _loggedIn: boolean = false;

    constructor (
      private _authService: AuthService,
      private _globalEventsManager: GlobalEventsManager) {
          _globalEventsManager.showNavBarEmitter.subscribe((mode)=>{
            // mode will be null the first time it is created, so you need to igonore it when null
            if (mode !== null) {
                console.log("Global Event, sent: " + mode);
                this._loggedIn = mode;
            }
        });
  }

  public login(){
      this._authService.startSigninMainWindow();
  }

  public logout(){
      this._authService.startSignoutMainWindow();
  }
}

New imports were added for the AuthService and GlobalEventsManager which get injected into the constructor of the class. The class also contains a _loggedIn property to track if the user is logged in or not. Finally, functions were added for login and logout to go with the two new links shown in the navigation.

Wrapping up

With the above, the Client Application can now log a user in and out utilizing IdentityServer from Angular. There are a lot of details in the files we just copied, but with a working sample, it is much easier to examine/customize how the process is being handled. Check back next week to see how to call the API Application from the Angular part of the Client Application.

The completed code can be found here.

Identity Server: Usage from Angular Read More »