ASP.NET Core

Auth0: Introduction and Initial Project Setup

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

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

Sign-up

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

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

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

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

Auth0 Setup

Client

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

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

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

API

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

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

Sample Solution Structure and Setup

The sample solution for this post has two projects.

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

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

To start, add a directory to contain the solution.

API Application

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

dotnet new webapi
Client Application

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

dotnet new angular

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

npm install
Solution

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

dotnet new sln --name AspNetCoreAngularAuth0

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

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

Securing the API Application

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

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

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

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

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

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

app.UseAuthentication();

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

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

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

Client Application

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

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

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

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

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

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

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

    options.ClaimsIssuer = "Auth0";

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

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

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

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

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

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

app.UseAuthentication();
Testing Setup

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

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

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

        return View();
    }

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

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

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

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

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

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

<p>
    @ViewBag.ApiResults
</p>

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

Wrapping Up

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

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

 

Auth0: Introduction and Initial Project Setup Read More »

ASP.NET Core Basics: Razor Pages

As part of the ASP.NET Core 2.0 release, a new feature called Razor Pages was added to better handle page-focused scenarios. This post is going to cover adding usage of Razor Pages to an existing ASP.NET Core MVC application. This is part of the ASP.NET Core Basics series and the repo before any changes for this post can be found here. All the changes will be in the Contacts project if you are using the sample repo.

Add the end of this post the Razor Pages example will have the same level of functionality as the existing Razor example in the project.

Setup

To follow the conventions of a new Razor Pages project add a Pages directory to the root of the project. As another level of grouping add a ContactsRazorPages directory under the Pages directory. The strange name of the directory is just to make it super clear this is different than the existing Contact List already in the application.

Scaffolding

Razor Pages supports the same level of scaffolding as the existing Razor/MVC setup we are all used to in Visual Studio. To start, right-click on the ContactsRazorPages directory and select Add > Razor Page.

This will open the Add Scaffold dialog. We are using entity framework and want to generate a full set of CRUD operations so select Razor Pages using Entity Framework (CRUD) and click Add.

Next, we have to select the Model and DB Context. We want to use the same model and context as our existing Razor/MVC setup so we are going to select the Contact class and the ContactsContext DB Context. Finally, since this is part of an existing application make sure and check the Use a layout page and use the existing _Layout.cshtml page used by the rest of the application. Click the Add button and hang out while the magic happens.

After a couple of minutes, the directory will be filled with a set of pages to do contact CRUD operations. Since this is a page-centric method of working all the files you need are in this one directory. No separate controller and views as in traditional MVC. By default, routing is handled via the folder structure.

View Imports

I had to a _ViewImports.cshtml file to the Pages to get things working. I tried adding the needed imports to the existing import, but it didn’t work for some reason. Here are the contents of the file.

@using RazorPages
@namespace RazorPages.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Add to the Navigation Menu

Our last step is going to be to add the new contact list to the site’s navigation menu. Open the _Layout.cshtml file in the Views/Shared directory and a link to the new contact list.

Existing MVC based contact list:
<li><a asp-area="" asp-controller="Contacts" asp-action="Index">Contact List</a></li>

New Razor Pages based contact list:
<li><a asp-page="/ContactsRazorPages/Index">Contact List (Razor Pages)</a></li>

Notice that the link uses the asp-page tag helper instead of asp-controller.

Wrapping Up

The above covers adding Razor Pages to an existing MVC application and rounds out the all the major UI options for the ASP.NET Basics series. Keep in mind that Razor Pages functionality is fully on par with tradition MVC and has been recommended by Microsoft as the way to do when page-centric work. If you haven’t check out the getting started docs to get a better feel for how to use pages.

The completed code can be found here.

ASP.NET Core Basics: Razor Pages 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 »

Vue: Contact Detail

Last week’s post covered adding a Vue project to the ASP.NET Core Basics example solution. This post is going to cover adding a read-only view of a contact’s detail to being the Vue sample a step closer to being in line with the Angular, Aurelia and React samples. The code before any changes can be found here.

Contact Class

The Contact interface in the contactlist.ts file should be deleted. Add a contact.ts file to the ClientApp/components/contacts/ directory. This will hold the replacement for the interface we just deleted. The full class definition follows.

export class Contact {
    id: number;
    name: string;
    address: string;
    city: string;
    state: string;
    postalCode: string;
    phone: string;
    email: string;

    constructor(data?: any) {
        if (data == null) return;
        (<any>Object).assign(this, data);
    }

    getAddress(): string {
        return `${this.address} ${this.city}, ${this.state} ${this.postalCode}`;
    }

}

This class is very simple and will be used later to show how a function call can be used as part of a view.

Contact Service

Since the application will now have two places that need to access the ASP.NET Core API lets to refactor the API access into a service. This is the same style used by the Aurelia, Angular and React applications. Add a contactService.ts file to the contacts directory. The service will provide functions to get all contacts or a single contact an ID. The following is the full class.

import { Contact } from './contact';

export class ContactService {
    private baseUrl = 'http://localhost:13322/api/contactsApi/';

    getAll(): Promise<Contact[]> {
        return fetch(this.baseUrl)
            .then(response => response.json() as Promise<Contact[]>)
            .then(contacts => Array.from(contacts, c => new Contact(c)));
    }

    getById(id: string): Promise<Contact> {
        return fetch(`${this.baseUrl}${id}`)
            .then(response => response.json())
            .then(contact => new Contact(contact));
    }
}

Now the API access in the ContactListComponent can be refactored to use the service. First, add imports for contact and service.

import { Contact } from './contact';
import { ContactService } from './contactService';

Next, replace the fetch call with a service call.

Before:
fetch('http://localhost:13322/api/contactsApi/')
    .then(response => response.json() as Promise<Contact[]>)

After:
let contactService = new ContactService();
contactService.getAll()

Contact Detail Component

Add a contactDetail.ts which will be the backing component for the contact detail view. The following is the full class. Pretty much all it is doing is using get contact service to get contact detail by ID when it is mounted.

import Vue from 'vue';
import { Component, Prop } from 'vue-property-decorator';
import { Contact } from './contact';
import { ContactService } from './contactService';

@Component
export default class ContactDetailComponent extends Vue {
    contact: Contact = new Contact();
    @Prop() id: string;

    mounted() {
        let contactService = new ContactService();
        contactService.getById(this.id)
            .then(data => {
                this.contact = data;
            });
    }
}

Now add a contactDetail.vue.html file for the UI bits of the contact detail. This is basically just HTML with the same binding syntax as before. Notice that the binding of address is a function call. The other Vue things to note are that the details will only show if contact is truthy using the v-if.

<template>
    <div>
        <h1>Contact Details</h1>
        <hr />
        <div v-if="contact">
            <dl class="dl-horizontal">
                <dt>ID</dt>
                <dd>{{ contact.id }}</dd>
                <dt>Name</dt>
                <dd>{{ contact.name }}</dd>
                <dt>Address</dt>
                <dd>{{ contact.getAddress() }}</dd>
                <dt>Phone</dt>
                <dd>{{ contact.phone }}</dd>
                <dt>Email</dt>
                <dd>{{ contact.email }}</dd>
            </dl>
        </div>
        <router-link to="/contactlist">Back to List</router-link>
        <hr />
    </div>
</template>

<script src="./contactDetail.ts"></script>

Routing with a Parameter

Finding the proper way to route with at parameter in Vue was not easy. The way it is done has evolved over time making find the right way a little challenging. The first step is to add a route for the contact detail by adding a new item to the routes array in the boot.ts file.

{ path: '/contactdetail/:id', component: require('./components/contacts/contactDetail.vue.html'), props: true }

The :id in the path denotes a parameter. The props: true bit is also key to getting the ID when in the contact detail component. The contactlist.vue.html needs to be updated to link to the contact detail route.

Before:
<td>{{ contact.id }}</td>

After:
<td>
  <router-link :to="'/contactdetail/' +  contact.id">{{ contact.id }}
  </router-link>
</td>

Finally, in the ContactDetailComponent we need to use the ID. To do so we need an import to allow the use of the Prop decorator.

import { Component, Prop } from 'vue-property-decorator';

Now all that is needed is to apply the decorator to a property of the class that matches the name of the parameter in the route.

@Prop() id: string;

Now the ID can be used to get the proper contact from the contact service. The following is code that was already shown, but the full context of how the prop decorator is used is important so I am repeating it here.

@Component
export default class ContactDetailComponent extends Vue {
    contact: Contact = new Contact();
    @Prop() id: string;

    mounted() {
        let contactService = new ContactService();
        contactService.getById(this.id)
            .then(data => {
                this.contact = data;
            });
    }
}

Wrapping Up

This brings the projects one step closer to being on the same level feature-wise. Look for one more post to get the Vue project features lined up with the other samples in the solution.

Keep in mind that this is my first look at Vue and my examples may or may not be idiomatic.

The code in a finished state can be found here.

Vue: Contact Detail Read More »

ASP.NET Core Basics: Vue with an API

In the past, I have done some exploration on Aurelia, Angular and React via the ASP.NET Core Basics series. This post is going to take a similar approach using Vue. The code for the project will be in the same repo as the previous basics examples and will be utilizing the same API to pull data. The code before adding the Vue project can be found here.

Project Creation

There is not a template for Vue built into Visual Studio, but there is a set of templates that can be used via the .NET CLI with the dotnet new that includes Vue (as well as Aurelia and Knockout). A full list of available templates can be found here. The template we need is Microsoft.AspNetCore.SpaTemplates and can be installed using the following command from a command prompt.

dotnet new -i "Microsoft.AspNetCore.SpaTemplates::*"

Create a Vue folder at the same level as the other projects, for the samples this would be in the src folder. In the command prompt navigate to the src/Vue/ directory. Run the following command to create the Vue project.

dotnet new vue

After the project generation completes run the following to get the needed npm packages installed.

npm  install

The last step is to get the new project added to the existing solution. Navigate the command prompt to your solution file. For the sample project, this is the root of the repo. Then run the following command (this could also be done through Visual Studio instead of using the CLI).

dotnet sln add src/Vue/Vue.csproj

Adding the Contact List

In the ClientApp/components directory add a new contacts directory to house all the awesome contact related functionality we will be adding. Next, add a new contactlist.ts. To this new file add the interface which defines what a Contact looks like.

interface Contact {
    id: number;
    name: string;
    address: string;
    city: string;
    state: string;
    postalCode: string;
    phone: string;
    email: string;
}

Above the contact interface, add the following imports needed to create a Vue component.

import Vue from 'vue';
import { Component } from 'vue-property-decorator';

Finally, add the contact list component. This component maintains a list of contacts which gets filled from our existing API when the component is mounted (see the docs for more information on Vue’s lifecycle hooks).

@Component
export default class ContactListComponent extends Vue {
    contacts: Contact[] = [];

    mounted() {
        fetch('http://localhost:13322/api/contactsApi/')
            .then(response => response.json() as Promise<Contact[]>)
            .then(data => {
                this.contacts = data;
            });
    }
}

The following if the full component just to provide full context.

import Vue from 'vue';
import { Component } from 'vue-property-decorator';

@Component
export default class ContactListComponent extends Vue {
    contacts: Contact[] = [];

    mounted() {
        fetch('http://localhost:13322/api/contactsApi/')
            .then(response => response.json() as Promise<Contact[]>)
            .then(data => {
                this.contacts = data;
            });
    }
}

interface Contact {
    id: number;
    name: string;
    address: string;
    city: string;
    state: string;
    postalCode: string;
    phone: string;
    email: string;
}

Next up is the UI for this contact list component. In the same directory add contactlist.vue.html.  The following is the full file.

<template>
    <div>
        <h1>Contact List</h1>
        <table v-if="contacts.length" class="table">
            <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
            </tr>
            </thead>
            <tbody>
            <tr v-for="contact in contacts">
                <td>{{ contact.id }}</td>
                <td>{{ contact.name }}</td>
            </tr>
            </tbody>
        </table>

        <p v-else><em>Loading...</em></p>
    </div>
</template>

<script src="./contactlist.ts"></script>

All of the above is pretty straightforward. All the Vue specific items have a v- prefix.

Add the Contact List to Navigation

The last bit needed to have a working contact list is adding it to navigation so the user can get to the list. The routes are defined in the boot.ts file in the routes array. Add the following line to the array to handle our contact list.

{ path: '/contactlist', component: require('./components/contacts/contactlist.vue.html') }

Now that the router knows to handle the contact list it needs to be added to the navigation UI. Open the /navmenu/navmenu.vue.html file and find the unordered list that is the navigation menu. Add a new list item to provide a link to the contact list.

<li>
    <router-link to="/contactlist">
        <span class="glyphicon glyphicon-list-alt"></span> Contact List
    </router-link>
</li>

Wrapping Up

Vue reminds me a lot of Aurelia in its simplicity so far which is awesome. Look for the same progression of posts for Vue that happened with React over the last few weeks.

The code in its final state can be found here.

ASP.NET Core Basics: Vue with an API Read More »

React: Form for Contact Add

Last week’s post covered adding a read-only view of a contact’s details. As before the goal is to get the React project’s features in line with the Aurelia and Angular samples. This week we will be adding a form to all addition of a new contact. The code before any changes can be found here.

Contact List

On the contact list page, we need to add a link to create a contact. This will be added just after the header in the ContactList.tsx file.

<h1>Contact List</h1>
<Link to={'contactdetail'}>Create New Contact</Link>

Routing

In order to stay in line with the other sample, the ContactDetail component is going to be handling both the read-only view and the view to add a contact. This means the ID that is currently part of the contact detail needs to be optional. The following is the change to make ID optional by adding a question mark.

Before:
<Route path='/contactdetail/:id' component={ContactDetail} />

After:
<Route path='/contactdetail/:id?' component={ContactDetail} />

Contact Service

In the ContactService class, we need to add a save function. This new function will make a post request to the contacts API and return the new contact with the ID from the API to the caller.

save(contact: Contact): Promise<Contact> {
    return fetch(this.baseUrl,
            {
                method: 'post',
                headers: {
                    'Accept': 'application/json',
                    'Content-Type': 'application/json' 
                },
                body: JSON.stringify(contact)
            })
        .then(response => response.json())
        .then(contact => new Contact(contact));
}

Contact Detail

The ContactDetail class is where most of the changes are. I had some trouble getting React’s forms to work directly with the instance of the contact class and ended up just storing the parts of a contact directly in state instead of as an object. I expect this is a failure on my part and not an issue with React. I may revisit this in the future. Below is the new structure of the state used by contact details.

interface ContactDetailState {
    id: string;
    name: string;
    address: string;
    city: string;
    state: string;
    postalCode: string;
    phone: string;
    email: string;
    loading: boolean;
    redirect: boolean;
}

Next, the constructor needed to be changed to handle the new state structure and to handle being called without an ID.

constructor(props: any) {
    super();

    if (props.match.params.id == undefined) {
        this.state = {
            id: props.match.params.id,
            name: '', address: '', city: '',
            state: '', postalCode: '', phone: '',
            email: '',
            loading: false,
            redirect: false
        };
    }
    else {
        this.state = {
            id: props.match.params.id,
            name: '', address: '', city: '',
            state: '', postalCode: '', phone: '',
            email: '',
            loading: true,
            redirect: false
        };

        let contactService = new ContactService();
        contactService.getById(this.state.id)
            .then(data => {
                this.setState({
                    name: data.name, address: data.address, city: data.city,
                    state: data.state, postalCode: data.postalCode, phone: data.phone,
                    email: data.email,
                    loading: false
                });
            });
    }
}

Again, this is way more code than it would be the contact class were being used. Next, the render function needs to be adjusted to render the UI for the read-only view or the contact creation. The decision is based on the ID being set or undefined.

public render() {
    let contents = this.state.loading
        ? <p><em>Loading...</em></p>
        : this.state.id != undefined &&
          !this.state.redirect
            ? this.renderExistingContact()
            : this.renderNewContact();

    return <div>
        <h1>Contact Detail</h1>
        <hr />
        {contents}
        <NavLink to={'/contactlist'}>Back to List</NavLink>
        <hr />
    </div>;
}

The render of an existing contact needs to be changed to use state instead of an instance of a contact.

private renderExistingContact() {
    return <dl className="dl-horizontal">
        <dt>ID</dt>
        <dd>{this.state.id}</dd>
        <dt>Name</dt>
        <dd>{this.state.name}</dd>
        <dt>Address</dt>
        <dd>{this.state.address} {this.state.city}, {this.state.state} {this.state.postalCode}</dd>
        <dt>Phone</dt>
        <dd>{this.state.phone}</dd>
        <dt>Email</dt>
        <dd>{this.state.email}</dd>
    </dl>;
}

The following is the render of the add contact UI. I will call out a couple of parts after. The bulk of the code is just rending of the form.

    private renderNewContact() {
        return (
            <div>
                {this.state.redirect && <Redirect to={`/contactdetail/${this.state.id}`}/>}
                <form role="form" className="form-horizontal" onSubmit={(e: any) => this.handleSubmit(e)}>
                    <div className="form-group">
                        <label className="col-sm-2 control-label">Name</label>
                        <div className="col-sm-10">
                            <input type="text" placeholder="name" className="form-control" name="name" value={this.state.name} onChange={(e: any) => this.handleChange(e)} />
                        </div>
                    </div>
                    <div className="form-group">
                        <label className="col-sm-2 control-label">Address</label>
                        <div className="col-sm-10">
                            <input type="text" placeholder="address" className="form-control" name="address" value={this.state.address} onChange={(e: any) => this.handleChange(e)} />
                        </div>
                    </div>
                    <div className="form-group">
                        <label className="col-sm-2 control-label">City</label>
                        <div className="col-sm-10">
                            <input type="text" placeholder="city" className="form-control" name="city" value={this.state.city} onChange={(e: any) => this.handleChange(e)} />
                        </div>
                    </div>
                    <div className="form-group">
                        <label className="col-sm-2 control-label">State</label>
                        <div className="col-sm-10">
                            <input type="text" placeholder="state" className="form-control" name="state" value={this.state.state} onChange={(e: any) => this.handleChange(e)} />
                        </div>
                    </div>
                    <div className="form-group">
                        <label className="col-sm-2 control-label">Zip</label>
                        <div className="col-sm-10">
                            <input type="text" placeholder="zip" className="form-control" name="postalCode" value={this.state.postalCode} onChange={(e: any) => this.handleChange(e)} />
                        </div>
                    </div>
                    <div className="form-group">
                        <label className="col-sm-2 control-label">Phone</label>
                        <div className="col-sm-10">
                            <input type="text" placeholder="phone" className="form-control" name="phone" value={this.state.phone} onChange={(e: any) => this.handleChange(e)} />
                        </div>
                    </div>
                    <div className="form-group">
                        <label className="col-sm-2 control-label">Email</label>
                        <div className="col-sm-10">
                            <input type="email" placeholder="email" className="form-control" name="email" value={this.state.email} onChange={(e: any) => this.handleChange(e)} />
                        </div>
                    </div>
                    <div className="text-center">
                        <button className="btn btn-success btn-lg" type="submit">Save</button>
                        <button className="btn btn-danger btn-lg" onClick={() => this.reset()}>Reset</button>
                    </div >
                </form>
            </div>
        );
    }

The following is the input for the contact’s name.

<input type="text" placeholder="name" className="form-control" name="name" value={this.state.name} onChange={(e: any) => this.handleChange(e)} />

Since I decided to go with the controlled component route React will be responsible for being the source of truth, not the form its self. To accomplish this it is important that the input has a name and an onChange event handler set up. The following is the handleChange function which uses the name from the on change event to update the proper property in the component’s state.

private handleChange(event: any): void {
    const target = event.target;
    const value = target.value;
    const name = target.name;

    this.setState({ [name]: value });
}

The following line is the reset button which will reset the state to blank out all the fields in the form.

<button className=”btn btn-danger btn-lg” onClick={() => this.reset()}>Reset</button>

The reset function just uses setState to blank out all the fields.

private reset() {
    this.setState({
        name: '', address: '', city: '',
        state: '', postalCode: '', phone: '',
        email: ''
    });
}

Not surprisingly the submit button triggers a submit of the form. What happens on submit is defined in the opening form tag.

<form role="form" className="form-horizontal" onSubmit={(e: any) => this.handleSubmit(e)}>

The handleSubmit function takes the contact information in state and uses it to create a new instance of a Contact which is then passed to the ContactService to be saved. The service returns a new contact object from the server and the ID is stored to state.

handleSubmit(event: any): void {
    event.preventDefault();

    let contact = new Contact();
    contact.name = this.state.name;
    contact.address = this.state.address;
    contact.city = this.state.city;
    contact.state = this.state.state;
    contact.postalCode = this.state.postalCode;
    contact.phone = this.state.phone;
    contact.email = this.state.email;

    let contactService = new ContactService();
    contactService.save(contact)
        .then(c => this.setState({ id: String(c.id) }));

    if (this.state.id) {
        this.setState({ redirect: true });
    }
}

If the server does return an ID for the new contact then the redirect is set to true. Then will case the following code to run in renderNewContact which will redirect the user back to the read-only view of the new contact.

{this.state.redirect && <Redirect to={`/contactdetail/${this.state.id}`}/>}

Wrapping Up

This pretty much gets the React application in line with the Aurelia and Angular sample applications. It has been fun getting a handle on the very, very basics of React. While I am back in the basics sample projects I may go ahead and tackle a Vue sample next.

The finished code can be found here.

React: Form for Contact Add Read More »

React: Contact Detail

Last week’s post covered adding a React project to the ASP.NET Core Basics solution. As I stated last week the goal is to get the React project’s features in line with the Aurelia and Angular samples. This week we will be adding a read-only view of a contact’s details. The code before any changes can be found here.

Contact Class

The Contact interface in the ContactList.tsx should be deleted and in its please we will add a Contact class. As part of this change, I also moved the contact related items to a contact directory. Add a contact.ts file to the ClientApp/components/contacts/ directory with the following contents.

export class Contact {
    id: number;
    name: string;
    address: string;
    city: string;
    state: string;
    postalCode: string;
    phone: string;
    email: string;

    constructor(data?: any) {
        if (data == null) return;
        (<any>Object).assign(this, data);
    }

    getAddress(): string {
        return `${this.address} ${this.city}, ${this.state} ${this.postalCode}`;
    }

}

This class is very simple and will be used later to show how a function call can be used as part of rendering.

Contact Service

Since the application will now have two places that need to access the ASP.NET Core API I decided to refactor the API access behind a service. This is the same style used by the Aurelia and Angular applications. Add a contactService.ts file to the Contacts directory. The service will provide functions to get all contacts or a single contact using its ID. The following is the full class.

import 'isomorphic-fetch';
import { Contact } from './contact';

export class ContactService {

    private baseUrl = 'http://localhost:13322/api/contactsApi/';

    getAll(): Promise<Contact[]> {
        return fetch(this.baseUrl)
            .then(response => response.json() as Promise<Contact[]>)
            .then(contacts => Array.from(contacts, c => new Contact(c)));
    }

    getById(id: string): Promise<Contact> {
        return fetch(`${this.baseUrl}${id}`)
            .then(response => response.json())
            .then(contact => new Contact(contact));
    }

}

The final step in this refactor is to use the new service in the ContactList class. First, add imports for the service and Contact class.

import { Contact } from './contact';
import { ContactService } from './contactService';

Then, replace the fetch call with the service.

Before:
fetch('http://localhost:13322/api/contactsApi/')
    .then(response => response.json() as Promise<Contact[]>)

After:
let contactService = new ContactService();
contactService.getAll()

Contact Detail Component

Add a ContactDetail.tsx file which will be used to show the details of a contact including using the getAddress function of the Contact class. The following is the full contents of the file.

import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import { Link, NavLink } from 'react-router-dom';
import 'isomorphic-fetch';
import { Contact } from './contact';
import { ContactService } from './contactService';

interface ContactDetailState {
    id: string;
    contact: Contact | undefined;
    loading: boolean;
}

export class ContactDetail extends React.Component<RouteComponentProps<{}>, ContactDetailState> {

    constructor(props: any) {
        super();
        this.state = { id: props.match.params.id, contact: undefined, loading: true };

        let contactService = new ContactService();
        contactService.getById(this.state.id)
            .then(data => {
                this.setState({ contact: data, loading: false });
            });
    }

    public render() {
        let contents = this.state.loading
            ? <p><em>Loading...</em></p>
            : this.state.contact
                ? ContactDetail.renderContactsTable(this.state.contact)
                : <p>No contacts</p>;

        return <div>
            <h1>Contact Detail</h1>
            <hr />
            {contents}
            <NavLink to={'/contactlist'}>Back to List</NavLink>
            <hr />
        </div>;
    }

    private static renderContactsTable(contact: Contact) {
        return <dl className="dl-horizontal">
                   <dt>ID</dt>
                   <dd>{contact.id}</dd>
                   <dt>Name</dt>
                   <dd>{contact.name}</dd>
                   <dt>Address</dt>
                   <dd>{contact.getAddress()}</dd>
                   <dt>Phone</dt>
                   <dd>{contact.phone}</dd>
                   <dt>Email</dt>
                   <dd>{contact.email}</dd>
               </dl>;
    }
}

This is all very similar to the things I have covered before. Now that we have a contact detail component we need a way to display it.

Routing with a Parameter

Import the contact detail in the route.tsx file.

import { ContactDetail } from './components/contacts/ContactDetail';

Next, add a route path for contact detail that expects an ID.

<Route path='/contactdetail/:id' component={ContactDetail} />

Back in the ContactList component change the ID to be a link to the new contact detail route using the ID of the contact.

Before:
<td>{contact.id}</td>

After:
<td><Link to={`contactdetail/${contact.id}`}>{contact.id}</Link></td>

The code for pulling the route parameter was in the ContactDetail component above, but I am going to show it again just so all the route with parameter information is together. The route parameters can be accessed using props.match.params.{parameter name} which in this case ends up being props.match.params.id. The following is the constructor of the ContactDetail component which is using a route parameter.

constructor(props: any) {
    super();
    this.state = { id: props.match.params.id, contact: undefined, loading: true };

    let contactService = new ContactService();
    contactService.getById(this.state.id)
        .then(data => {
            this.setState({ contact: data, loading: false });
        });
}

Wrapping Up

This brings the projects one step closer to being on the same level feature-wise. I expect at least one more post to get the project features lined up so make sure and keep a lookout for the next post.

Keep in mind that this is my first look at React and my examples may or may not be idiomatic. So far I am really enjoying working with React.

The code in a finished state can be found here.

React: Contact Detail Read More »

ASP.NET Core Basics: React with an API

In the past, I have done some exploration on Aurelia and Angular via the ASP.NET Core Basics series. This post is going to take a similar approach as I start doing some exploration with React. The code for the project will be in the same repo as the previous basics examples and will be utilizing the same API to pull data. The code before adding the React project can be found here.

This post is going to cover adding a React project to the existing using the React template that is now built into Visual Studio. The same thing can be accomplished using the .NET CLI so don’t feel like Visual Studio is required. The goal for the React project in this initial post will be to connect to the contacts API and download a list of contacts and render that to the screen. In future posts, I hope to expand this functionality to match that of the Aurelia and Angular projects.

Project Creation

Right-click and select Add > New Project.

In the Add New Project dialog select the ASP.NET Core Web Application. In the case of the sample, the project will be named React. Click OK to continue.

On the next screen make sure and select ASP.NET Core 2.0 and the React.js template. Then click OK.

The following is the resulting React project in the context of the full solution.

Next, make sure and run npm install from a command prompt in the React project’s directory to ensure all the npm packages get restored.

Adding the Contact List

Inside the ClientApp/components/ directory add a file name ContactList.tsx. TSX is the TypeScript version of the React JSX file type. The official docs on JSX can be found here. Since this is my first time working with React I took the FetchData.tsx file and copied the contents and used that as the starting point for my contact list. To lead with there is an interface for what should define a contact.

interface Contact {
    id: number;
    name: string;
    address: string;
    city: string;
    state: string;
    postalCode: string;
    phone: string;
    email: string;
}

Next, we have an interface for the state of this component with contains a loading flag and an array of contacts.

interface ContactListState {
    contacts: Contact[];
    loading: boolean;
}

In the constructor for the component is where the data is pulled from the API using fetch. The data from the API is then saved to the state of the component using the setState function.

constructor() {
    super();
    this.state = { contacts: [], loading: true };

    fetch('http://localhost:13322/api/contactsApi/')
        .then(response => response.json() as Promise<Contact[]>)
        .then(data => {
            this.setState({ contacts: data, loading: false });
        });
}

Next, the component has a function named renderContactsTable which takes an array of contacts and returns how they should be rendered. In this case, the contacts are rendered to a table that displays the contact ID and Name.

private static renderContactsTable(contacts: Contact[]) {
    return <table className='table'>
        <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
            </tr>
        </thead>
        <tbody>
            {contacts.map(contact =>
                <tr key={contact.id}>
                    <td>{contact.id}</td>
                    <td>{contact.name}</td>
                </tr>
            )}
        </tbody>
    </table>;
}

Finally, there is the render function. As you can guess this is what gets called to render the component. In this case, either “Loading” or the contact list gets displayed depending on if the contact list data has been loaded or not.

public render() {
    let contents = this.state.loading
        ? <p><em>Loading...</em></p>
        : ContactList.renderContactsTable(this.state.contacts);

    return <div>
        <h1>Contact List</h1>
        {contents}
    </div>;
}

The following is the full file for reference.

import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import 'isomorphic-fetch';

interface ContactListState {
    contacts: Contact[];
    loading: boolean;
}

export class ContactList extends React.Component<RouteComponentProps<{}>, ContactListState> {
    constructor() {
        super();
        this.state = { contacts: [], loading: true };

        fetch('http://localhost:13322/api/contactsApi/')
            .then(response => response.json() as Promise<Contact[]>)
            .then(data => {
                this.setState({ contacts: data, loading: false });
            });
    }

    public render() {
        let contents = this.state.loading
            ? <p><em>Loading...</em></p>
            : ContactList.renderContactsTable(this.state.contacts);

        return <div>
            <h1>Contact List</h1>
            {contents}
        </div>;
    }

    private static renderContactsTable(contacts: Contact[]) {
        return <table className='table'>
            <thead>
                <tr>
                    <th>ID</th>
                    <th>Name</th>
                </tr>
            </thead>
            <tbody>
                {contacts.map(contact =>
                    <tr key={contact.id}>
                        <td>{contact.id}</td>
                        <td>{contact.name}</td>
                    </tr>
                )}
            </tbody>
        </table>;
    }
}

interface Contact {
    id: number;
    name: string;
    address: string;
    city: string;
    state: string;
    postalCode: string;
    phone: string;
    email: string;
}

Add Contact List to Navigation

Now that we have the contact list component it needs to be added to the navigation menu. The first step is to add it to the application’s router. This can be found in the routes.tsx file. The file is short so I am going to include the full content. Lines 7 and 13 are the ones added to handle our contact list.

import * as React from 'react';
import { Route } from 'react-router-dom';
import { Layout } from './components/Layout';
import { Home } from './components/Home';
import { FetchData } from './components/FetchData';
import { Counter } from './components/Counter';
import { ContactList } from './components/ContactList';

export const routes = <Layout>
    <Route exact path='/' component={Home} />
    <Route path='/counter' component={Counter} />
    <Route path='/fetchdata' component={FetchData} />
    <Route path='/contactlist' component={ContactList} />
</Layout>;

The last change is to add a navigation link to the NavMenu found in the NavMenu.tsx file. As I am sure most of us are used to adding an item to the nav menu is just adding a new li, but with the React specific NavLink bit.

<li>
  <NavLink to={'/contactlist'} activeClassName='active'>
      <span className='glyphicon glyphicon-th-list-alt'></span> Contact List
  </NavLink>
</li>

Wrapping Up

React is different than both Aurelia and Angular. Don’t take that as a good or bad thing. I don’t plan to pick on a side on the Angular vs React debate I just want to get a good feel for the different frameworks. So far the React experience has been pretty nice and I look forward to doing more exploration.

You can find the finished code for this post here.

ASP.NET Core Basics: React with an API 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 »