IdentityServer4

ASP.NET Core 3: React Template with Auth

Preview 3 of ASP.NET Core was released on March 6th. This release added the option to include auth when creating an Angular or React application using the templates provided by Microsoft. I can’t convey how happy this feature makes me. As someone that hasn’t done a super deep dive on auth having a good starting point for a new application is very helpful.

Installation

To get the updated version of the templates install the latest version of the .NET Core 3 previews. You can find the installers here.

Project Creation

Using the .NET CLI from a command prompt in the directory you want the project created in run the following command.

dotnet new react --auth Individual

After the project is built you should be able to use the following command to run the application.

dotnet run

Issues with Preview 3

I did the above and it results in the following error.

Microsoft.AspNetCore.SpaServices: Information:

Failed to compile.

info: Microsoft.AspNetCore.SpaServices[0]
./src/components/api-authorization/ApiAuthorizationConstants.js
It seems that there are a few issues with the React template that shipped with Preview 3.  To fix the above error open the ApiAuthorizationConstants.js file found in the ClientApp/src/components/api-authorization directory and make the following change.
Before:
ApiAuthorizationPrefix = prefix,

After:
ApiAuthorizationPrefix: prefix,

After that fix, you will see the following error.

./src/components/api-authorization/ApiAuthorizationRoutes.js
Module not found: Can’t resolve ‘./components/api-authorization/Login’ in ‘\ClientApp\src\components\api-authorization’

This fix is a bit more involved. I found the workaround in the known issues page provided by Microsoft.

First, delete the ApiAuthorizationRoutes.js file which is in the same directory as the previous fix. Then replace the contents of App.js found in the ClientApp/src directory with the following.

import React, { Component } from 'react';
import { Route } from 'react-router';
import { Layout } from './components/Layout';
import { Home } from './components/Home';
import { FetchData } from './components/FetchData';
import { Counter } from './components/Counter';
import { Login } from './components/api-authorization/Login'
import { Logout } from './components/api-authorization/Logout'
import AuthorizeRoute from './components/api-authorization/AuthorizeRoute';
import { ApplicationPaths, LoginActions, LogoutActions } from './components/api-authorization/ApiAuthorizationConstants';

export default class App extends Component {
  static displayName = App.name;

  render () {
    return (
      <Layout>
        <Route exact path='/' component={Home} />
        <Route path='/counter' component={Counter} />
        <AuthorizeRoute path='/fetch-data' component={FetchData} />
        <Route path={ApplicationPaths.Login} render={() => loginAction(LoginActions.Login)} />
        <Route path={ApplicationPaths.LoginFailed} render={() => loginAction(LoginActions.LoginFailed)} />
        <Route path={ApplicationPaths.LoginCallback} render={() => loginAction(LoginActions.LoginCallback)} />
        <Route path={ApplicationPaths.Profile} render={() => loginAction(LoginActions.Profile)} />
        <Route path={ApplicationPaths.Register} render={() => loginAction(LoginActions.Register)} />
        <Route path={ApplicationPaths.LogOut} render={() => logoutAction(LogoutActions.Logout)} />
        <Route path={ApplicationPaths.LogOutCallback} render={() => logoutAction(LogoutActions.LogoutCallback)} />
        <Route path={ApplicationPaths.LoggedOut} render={() => logoutAction(LogoutActions.LoggedOut)} />
      </Layout>
    );
  }
}

function loginAction(name){
    return (<Login action={name}></Login>);
}

function logoutAction(name) {
    return (<Logout action={name}></Logout>);
}

With the above fixes, the site will load and you should see something like the following.

When you try to register you will get the following error.

MissingMethodException: Method not found: ‘Microsoft.EntityFrameworkCore.Metadata.Builders.IndexBuilder Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder`1.HasIndex(System.Linq.Expressions.Expression`1<System.Func`2<!0,System.Object>>)’.

IdentityServer4.EntityFramework.Extensions.ModelBuilderExtensions+<>c__DisplayClass2_0.<ConfigurePersistedGrantContext>b__0(EntityTypeBuilder<PersistedGrant> grant)

Again the known issue page to the rescue. Open your csproj file and replace the following package references.

Before:
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.0.0-preview3-19153-02" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.0.0-preview3.19153.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.0.0-preview3.19153.1" />

After:
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.0.0-preview-18579-0056" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.0.0-preview.19080.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.0.0-preview.19080.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.0.0-preview.19080.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.0.0-preview.19080.1" />

And add the following bit of XML.

<PropertyGroup>
  <NoWarn>$(NoWarn);NU1605</NoWarn>
</PropertyGroup>

Wrapping Up

After the above tweaks, everything just worked. I’m sure by preview 4 the experience will be even better. Even with the issues I hit getting a basic new project going I am very excited. Thank you, ASP.NET team, at Microsoft adding auth to these templates is going to be super helpful. Also, note that Angular template also got an auth option (and it seems to be a smoother experience at the moment).

ASP.NET Core 3: React Template with Auth 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 »

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 »

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: Migration to ASP.NET Core 2

The Identity App that is part of my IdentityServer sample project is the last application I have on GitHub (of the ones that will get upgraded) that needs an upgrade to ASP.NET Core. The starting point of the project before any changes can be found here. This post assumes that you have already followed my generic ASP.NET Core 2 migration post, which can be found here, on the project you are migrating. One final note of caution this post has been written using the RC1 version of the Identity Server NuGet packages and then moved to the final version so there will be two different related pull requests that will have to be looked at to get the full picture of all the changes.

Package Changes

The first change is to get a version of the Identity Server packages what will work from ASP.NET Core 2.

Before:
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="1.0.1" />
<PackageReference Include="IdentityServer4.EntityFramework" Version="1.0.1" />

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

Database Initialization

I wasted a lot of time on finding out this was an issue when I was trying to create Entity Framework migrations and kept getting Unable to create an object of type ‘ApplicationDbContext’. Add an implementation of ‘IDesignTimeDbContextFactory‘ errors. The gist is database initialization needs to be moved out of Startup and context constructors.

Let’s start with the ApplicationDbContext and remove the following code from the constructor as well as the associated property.

if (_migrated) return;
Database.Migrate();
_migrated = true;

Next, in the Configure function of the Startup class remove the following line.

IdentityServerDatabaseInitialization.InitializeDatabase(app);

We still need the database initialization code to run, but where should that be done? In the Main function of the Program class seems to be the new recommended location. The following is the new Main function.

public static void Main(string[] args)
{
    var host = BuildWebHost(args);

    using (var scope = host.Services.CreateScope())
    {
        var services = scope.ServiceProvider;

        try
        {
            IdentityServerDatabaseInitialization.InitializeDatabase(services);
        }
        catch (Exception ex)
        {
            var logger = services.GetRequiredService<ILogger<Program>>();
            logger.LogError(ex, "An error occurred Initializing the DB.");
        }
    }

    host.Run();
}

InitializeDatabase now needs to take an IServiceProvider instead of an IApplicationBuilder. This forced a lot of lines to change so the following is the full class.

public static class IdentityServerDatabaseInitialization
{
    public static void InitializeDatabase(IServiceProvider services)
    {
        PerformMigrations(services);
        SeedData(services);

    }

    private static void PerformMigrations(IServiceProvider services)
    {
        services
          .GetRequiredService<ApplicationDbContext>()
          .Database.Migrate();
        services
          .GetRequiredService<ConfigurationDbContext>()
          .Database.Migrate();
        services
          .GetRequiredService<PersistedGrantDbContext>()
          .Database.Migrate();
    }

    private static void SeedData(IServiceProvider services)
    {
        var context = services.GetRequiredService<ConfigurationDbContext>();

        if (!context.Clients.Any())
        {
            foreach (var client in Config.GetClients())
            {
                context.Clients.Add(client.ToEntity());
            }
            context.SaveChanges();
        }

        if (!context.IdentityResources.Any())
        {
            foreach (var resource in Config.GetIdentityResources())
            {
                context.IdentityResources.Add(resource.ToEntity());
            }
            context.SaveChanges();
        }

        if (!context.ApiResources.Any())
        {
            foreach (var resource in Config.GetApiResources())
            {
                context.ApiResources.Add(resource.ToEntity());
            }
            context.SaveChanges();
        }
    }
}

Startup Changes

Most of the changes to the Startup class are in the ConfigureServices function, but some cross with the Configure function as well. The existing AddIdentityServer extension has multiple changes especially if you are using Entity Framework for your configuration data. AddTemporarySigningCredential is now AddDeveloperSigningCredential. The following is the new version including configuration data.

services.AddIdentityServer()
    .AddDeveloperSigningCredential()
    .AddAspNetIdentity<ApplicationUser>()
    .AddConfigurationStore(options =>
    {
      options.ConfigureDbContext = builder =>                 
        builder.UseSqlite(Configuration
                          .GetConnectionString("DefaultConnection"),
                          db => db.MigrationsAssembly(migrationsAssembly));
    })
    .AddOperationalStore(options =>
    {
      options.ConfigureDbContext = builder =>
        builder.UseSqlite(Configuration
                          .GetConnectionString("DefaultConnection"),
                          db => db.MigrationsAssembly(migrationsAssembly));
    });

The way to handle registration of external authentication has changed as well. For example, this application uses Twitter. The UseTwitterAuthentication call in the Configure function needs to be removed. The following added to the bottom of the ConfigureServices is now the proper way to add external authentication providers.

services.AddAuthentication().AddTwitter(twitterOptions =>
{
    twitterOptions.ConsumerKey = 
         Configuration["Authentication:Twitter:ConsumerKey"];
    twitterOptions.ConsumerSecret = 
         Configuration["Authentication:Twitter:ConsumerSecret"];
});

Entity Framework

The new changes in Identity from the ASP.NET Core team included a new foreign key which is one of the things that Sqlite migrations can’t actually do. Since I don’t really have any data I care about I just deleted the database and the existing migrations and snapshots and regenerated everything. If you are using Sqlite and this isn’t an option for you check out this post for some options. If you aren’t using Sqlite then the migrations should work fine. The following are the commands to generate migrations for the 3 contexts that the Identity Application uses.

dotnet ef migrations add InitConfigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/Configuration

dotnet ef migrations add InitPersistedGrant -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrant

dotnet ef migrations add InitApplication -c ApplicationDbContext -o Data/Migrations

Quick Start UI Changes

As part of going from the RC1 version to the Final version, the Identity Server team updated the UI and related bits to be in line with the new features added in the ASP.NET Core 2.0 release. Turns out that resulted in a lot of changes. Since I haven’t done any custom work in this area of my Identity Application I deleted the related files in my local project and pulled from the ASP.NET and Entity Framework Combined sample. I am going to give a good idea of all the files I replace, but in case I miss something GitHub will have the full story.

In the Controllers folder replace AccountController.cs and ManageController.cs. Add or replace the following folders:  Extensions, Models, Quickstart, Services, and Views.

Application Insights Error

I ran into the following error.

System.InvalidOperationException: No service for type ‘Microsoft.ApplicationInsights.AspNetCore.JavaScriptSnippet’ has been registered.

You may or may not see it, but if you do open the _Layout.cshtml and remove the following two lines.

@inject Microsoft.ApplicationInsights.AspNetCore.JavaScriptSnippet JavaScriptSnippet


@Html.Raw(JavaScriptSnippet.FullScript)

Wrapping up

If you hit any issues not covered above make sure and check out the breaking changes issue. The completed code can be found here for part 1 and here for part 2.

Identity Server: Migration to ASP.NET Core 2 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 »

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 »

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 »