Authentication

Blazor Authentication: Hide a Nav Menu Item

In last week’s post, Server-Side Blazor with Authentication, we covered creating a Server-Side Blazor application with Authentication and then used the attribute to not allow the user to view the Fetch data page if they weren’t logged in.

While the authorize attribute does keep the user from viewing the contents of the page it still allows the user access to the nav menu item for the page they aren’t authorized to access. This is going to be a quick post showing how the AuthorizedView component can be used to hide any content that a user should be logged in to see (or be in a specific role).

Hide a Nav Menu Item

In the Pages/Shared directory open the NavMenu.razor file which is the file where the nav menu is defined. The following code is the code that renders the Fetch data menu item which is the section we want to hide if the user isn’t logged in.

<li class="nav-item px-3">
    <NavLink class="nav-link" href="fetchdata">
        <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
    </NavLink>
</li>

To hide menu item we wrap the list item in the AuthorizeView component.

<AuthorizeView>
    <li class="nav-item px-3">
        <NavLink class="nav-link" href="fetchdata">
            <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
        </NavLink>
    </li>
</AuthorizeView>

Do note that you should still use the Authorize attribute on the page that should require authorization and not depend on the menu item being hidden keeping users from finding the page.

Wrapping Up

While the Authorize attribute is still very useful I’m sure that the AuthorizeView will be getting a lot of use in Blazor apps. AuthrozieView has the advantage of not being limited to page component.

Also, note that AuthorizeView also supports roles and policies. Make sure and check out the official AuthorizeView component docs for more details. If interested the code for the component is on GitHub.

Blazor Authentication: Hide a Nav Menu Item Read More »

ASP.NET Core Server-Side Blazor with Authentication

It has been close to a year since I did my first into post on Blazor, ASP.NET Core Basics: Blazor, and a lot has changed. The biggest thing is that it was announced that Server-Side Blazor is going to ship with .NET Core 3. This post is going to walk through creating a server-side Blazor application including authentication.

Sever-Side Blazor

What is server-side Blazor and how is it different from client-side Blazor? The quick answer is that client-side Blazor uses WebAssembly and Mono to run .NET code in the browser allowing for basically a SPA written in .NET. With Server-Side Blazor the app is executed on the server and update/processing are requested over a SignalR connection.

For a deeper dive into the differences check out the ASP.NET Core Blazor hosting models doc.

Pre-requisites

To start make sure to install at least preview 6 of .NET Core 3. Using a command prompt you can run the following command to see which versions of the .NET Core SDK are installed.

dotnet --list-sdks

The previews of .NET Core 3 can be downloaded from here. Also, make sure to use the latest preview of Visual Studio 2019.

Application Creation

I used the following command from the command prompt to create a new Blazor application using individual authentication.

dotnet new blazorserverside --auth Individual

Visual Studio also has the template available if you select the ASP.NET Core Web Application project type and about three screens in select the Blazor Server App option.

After the creation process is complete open the project in Visual Studio. At this point, if you run the application you will see the standard options to log in or register.

Requiring Auth for a Page

At this point, the application allows account creation and login, but all pages are available to all user, even if they aren’t logged in. There are multiple ways to deal with this. For this post, I’m going with the closest to what I am used to from MVC which is using an Authorize attribute. In the Pages directory open then FetchData.razor file and make the following changes to make the page require the user to be authorized. Note that this method should only be used on page components.

Before:

@page "/fetchdata"
@using BlazorAuth.Data
@inject WeatherForecastService ForecastService

After:

@page "/fetchdata"
@using BlazorAuth.Data
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@inject WeatherForecastService ForecastService

Now if you go to the Fetch data page without being logged in you will see a Not authorized message. This message can be customized using the AuthorizeView component. You can find more details in the docs.

Wrapping Up

It is neat seeing how far Blazor has come since I last played with it. The docs are great and the product has improved a ton. I may do a follow-up post to go more into how to use the AuthorizeView component.

Make sure to check out the official docs on Blazor authentication and authorization to get the full picture of the subject.

ASP.NET Core Server-Side Blazor with Authentication Read More »

Create React or Angular Application from Visual Studio with Authentication

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

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

Required Installs

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

Project Creation

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

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

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

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

Select Individual User Accounts and click OK.

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

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

Wrapping Up

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

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

Azure B2C: Social Logins

This post is going to cover enabling a social login for a site using Azure B2C for authentication. If you are new to this set of posts you can find the initial setup of the sample application in the  ASP.NET Core with Azure B2C Auth post. I would also recommend checking out the Azure B2C: Customize Layouts to learn how to change the provided UI to provide your users with a consistent look and feel that matches the rest of your application.

Social Login Provider Setup

Azure B2C supports most of the login provides you would expect such as Google, Facebook, Twitter, Microsoft, etc. as well as any provider that supports OpendID Connect. No matter which option you pick you will have to register/signup your application with the provider. Unfortunately, Azure B2C doesn’t provide links to the registration pages of the services it supports so it is up to you to find those yourself.

For this example, I’m going to be walking through the process using Google. You can get all the details of Google’s OpenID Connect offering in their docs. To get started we need to set up our application in the developer console. The link will take you to the dashboard where you will see a message about selecting or creating a new project. Click the Create link. In the next page enter the Project name and click Create.

After the creation process finishes click Credentials from the navigation menu on the left.

On the top of the screen select OAuth consent screen. On this page, you will need to at least enter an Application name and an Authorized domain of b2clogin.com (not shown in the screenshot, but still required) and click the Save button at the bottom of the page.

Next, select the Credentials tab and click the Create credentials button and select the OAuth client ID option.

On the next page, select Web application as the application type. Enter a Name. For the next two fields, you will need your tenant ID from Azure B2C. In the screenshot, you can see my where I did this with my tenant ID of testingorg3. Also, make sure and enter the URLs in all lower case, I had redirect issues using mixed casing. For Authorized JavaScript origins use the URL https://yourtenantid.b2clogin.com and for Authorized redirect URIs use https://yourtenantid.b2clogin.com/testingorg3.onmicrosoft.com/oauth2/authresp

After clicking create you will see a dialog with your client ID and client secret make note of these as they will be needed when we add the login provider in Azure B2C.

Azure B2C Changes

Now that we have the Google side setup head over to Azure and find your Azure B2C resource. Select Identity provides from the navigation menu and click the Add button.

Enter a Name, I’m just using the name of the provider. Then, click on Identity provider type which will trigger the Select social identity provider selection to show. Click Google and then click OK.

Next, click Setup this identity provider which will show a fly out where you will need to enter your Client ID and Client secret provided by Google. After entering your values click OK.

Next, click the Create button at the bottom of the Add identity provider screen. When this process is done we will have two identity providers for this B2C resource email and Google. Next, we need to enable our new Google provider for our sign up/sign in user flow. From the menu select User flows and then click the flow you have set up for Sign up and sign in.

Next, select Identity providers, this will show a list of providers available for the selected flow. Check any additional providers the flow should use, Google, in our case. Finally, click Save.

Try it out

With all the above change attempt a login with your application and you will see Google as a sign in option.

Wrapping Up

Hopefully the above will give you a jump start on adding support for social logins to your applications. Adding other providers will really close to what we did for Google from the Azure B2C prospective, of course the sign up process will vary by provider.

Azure B2C: Social Logins Read More »

Azure B2C: Customize Layouts

In the post ASP.NET Core with Azure B2C Auth we did a walkthrough of setting up the basics of Azure B2C and creating a new application that used our new B2C setup for auth. This post is going to be using that same setup to show how to replace the Microsoft provided pages for sign up/sign in with your own custom pages.

Custom Page Hosting

Our custom page needs to be hosted somewhere public with CORS enabled. If the test application was hosted somewhere public we could just us it, but since it is running on localhost that isn’t currently an option. We are going to use Azure Blob storage for hosting in this example.

Create A Storage Account

From the Azure Portal select Storage accounts.

Click the Add button.

Next, on the Create storage account page, I used a new resource group and tried storage accounts names until I found an unused one. For the rest of the fields, I took the defaults and then clicked Review + create.

On the review + create page it takes a few seconds for the account to be validated. After validation click the Create button.

After the storage deployment is complete click the Go to resource button.

Setup Blob storage

The above will land you on the Overview page for the new storage account. Select CORS from the menu.

Since this is just a test I’m allowing everything under the Blob service, for a real deploy I would recommend only allowing the values you expect requests from. After setting your values click the Save button.

Back on the storage menu on the right side of the screen select Blobs.

Click the + Container button to create a new blob storage container.

In the new container, page enter a name and select your public access level. I’m going with the most permissive access level, for a production system you will need to evaluate the appropriate access level for your use case. Click OK when done.

When done you will be returned to your list of containers. Click on the container that was just created to view the details.

Create a custom page

Now that we have our blob storage we need to create the HTML page that we want to to use instead of the default. The following is the code for the page I’m going to use. It is going to be super ugly as I’m not going to use any styling.

<!DOCTYPE html>
<html>
  <head>
    <title>Custom Page!</title>
  </head>
  <body>
    <h1>Custom Page!</h1>
    <div id="api"></div> 
  </body>
</html>

You can make this page look however you want, but it will always need the div with the ID of API as this is where Azure will inject the elements that actually handle the signup/sign in. Save your page.

Upload custom page to blob storage

Back in Azure click the Upload button and then select your file and click the Upload button.

After upload, you will be returned to the list of items in your current container. Click the item you just created. In the details copy the URL as we are going to need it to give B2C the location of our custom page.

B2C use custom page

In your portal head back to your Azure AD B2C page and select User flows.

Select the flow you want to use the custom page for. In our case, we are going to be using the flow for Sign up and sign in.

In the Customize section select Page layouts.

In the bottom of the page select Yes for  Use custom page content and past the link to your blob from above into the Custome page URI field and click Save.

Try it out

With all of the above setup you can now go back to the application using B2C and hit your sign in link and you will see your custom page. Here is what the one in the sample looks like.

Obviously, this example is really ugly and isn’t something you would do to your users, but it gives you the basic idea of how to use a custom page.

Wrapping Up

Hopefully the above will help you get started with customizing you B2C related pages to give your users a more consistent look and feel. The above only uploaded an HTML page to blob storage, but you could also upload a CSS file or any other assets you need. Also, don’t forget if your site is publicly accessible the assets can be stored with the rest of your application in that is appropriate, just remember to configure CORS to allow requests from Azure.

If you want more information on this topic check out the official docs from Microsoft on the subject.

Azure B2C: Customize Layouts Read More »

ASP.NET Core with Azure B2C Auth

I ran into a previous co-work a while back and they were talking about using Azure’s B2C for authentication on their apps. It sounded like a good solution. This blog post is going to cover getting the Azure B2C setup and will cover creating a sample application that will use B2C for authorization.

Create Azure Active Directory B2C

This all assumes you already have an Azure account. If you don’t you can sign up for a free trial (not an affiliate link). After you are signed up head to the Azure Portal.

Note: you can skip this section if you want to use the default Active Directory that is on your Azure account.

In the upper left click the Create a resource link.

In the search box look for Azure Active Directory B2C.

After selecting Azure Active Directory B2C more information will load to the right in a new panel. Click the Create button to continue.

Next, select if you want to create a new B2C tenant or use an existing one. I don’t have an existing one so the following flow will be for creating a new tenant.

On the next panel, you will need to enter an organization name and initial domain name. After entering valid values click the create button.

Switch Active Directory

Now that the new directory has been created we need to switch to the new directory in the Azure Portal. In the left panel click Azure Active Directory.

Click the Switch directory link.

A new panel will show on the right side of the screen with a list of directories you have available. Select the one you created in the steps above or an existing one you would like to use.

Using the search box in the top middle of the portal find Azure AD B2C.

Sample Application

Before moving forward on the Azure side we are going to create our sample client application. This little detour will make it easier for me to point out what values in Azure need to go where in the application configuration.

To create a new web application already set up to use Azure B2C use the following .NET CLI command from a command prompt. There is also a Visual Studio template if you prefer that route.

dotnet new webapp --auth IndividualB2C

In the resulting application, your appsettings.json will have the following section for AzureAdB2C.

"AzureAdB2C": {
  "Instance": "https://login.microsoftonline.com/tfp/",
  "ClientId": "11111111-1111-1111-11111111111111111",
  "CallbackPath": "/signin-oidc",
  "Domain": "qualified.domain.name",
  "SignUpSignInPolicyId": "",
  "ResetPasswordPolicyId": "",
  "EditProfilePolicyId": ""
}

Azure AD B2C Setup

Back to the Azure portal and the Azure AD B2C service page. In the Overview page, the first thing we need to make note of and use to set some configuration values in our application for is the Domain name.

In your appsettings.json file use this value for your Domain value.

"Domain": "TestingOrg3.onmicrosoft.com"

The subdomain is also used to build the Instance like the following.

"Instance": "https://TestingOrg3.b2clogin.com/tfp/"

On the panel that loads hit the Add button. On the new Application panel, we need to give the application a Name, select the type of clients which is Web App / Web API in our case. Next, is the Reply URL which with default setup is your base url/sigin-oidc. I messed this value up in the beginning and got some really strange errors. Finally hit the Create button.

After the creation process is complete copy the value in the Application ID field and use it as ClientId in your appsettings.json file.

Back in Azure select the User flows (policies) option.

At the top of the panel that loads click the New user flow button. The next panel shows a selection of flows that can be added. The application we are making will use both the Sign up and sign in flow and the Password rest flow. I’m only going to walk through the first one, but the second one is very smiliar. Click on the Sign up and sign in like.

In the creation process you will need to set a Name for the flow and select the Identity providers that are valid for the flow. You also have the choice of fields to collect with this flow and which ones should be returned with the claim. After those options are set click the Create button.

The Name form this screen will need to be entered in your appsettings.json file for the SignUpSignInPolicyId value. Here is what I ended up with in my settings file with the Sign Up and Reset Password policies.

"SignUpSignInPolicyId": "B2C_1_SignInOut",
"ResetPasswordPolicyId": "B2C_1_PasswordReset"

Run the sample

At this point, you can run your sample application and click the Sign in link and you will see a page similar to the following which is coming from Azure B2C.

There are ways to customize the pages users will see under the associated flow in Azure.

Wrapping Up

I hit quite a few issues getting Azure B2C setup. I hope this post will help you all bypass some of the issues I hit.

ASP.NET Core with Azure B2C Auth Read More »

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 »

Two-factor Authentication in ASP.NET Core 2

This post was going to be an update of the SMS using Twilio Rest API in ASP.NET Core post a made a year or so ago, but once I got the example project created I noticed that the default template has removed SMS as an option for two-factor authentication in favor of authenticator auth support.

This post is going to explore the setup of this new two-factor authentication style. In a follow-up post, I may look at adding back support for SMS as the second factor as that seems like a more common scenario albeit a less secure one.

Project Setup

To create a new Razor Pages application using individual authentication I used the following command from a command prompt. Make sure you are in the directory you want the files to end up. The only reason I mention this is I just spend the last 5 minutes cleaning up my user directory because I forgot to change directories.

dotnet new razor --auth Individual

Next, I move up a directory and used the following command to add a new solution file.

dotnet new sln -n TwoFactorAuth

Finally, I used the following command to add the new project to the solution file. If you aren’t using Visual Studio then you don’t have to have the solution file.

dotnet sln add TwoFactorAuth\TwoFactorAuth.csproj

The project after the above can be found here.

Add a QR Code

Strictly speaking, a QR code isn’t required, but it is much better for users so they don’t have to enter a 32 character key into their authenticator application manually. I followed the official docs to enable QR code generation.

The first step is to download a Javascript QR Code library. I just used the one recommended in the docs, but any would work. This link will take you to a zip download. Open the download and copy qrcode.js and qrcode.min.js into a new qrcode directory inside of your project’s wwwroot/lib/ directory.

Next, find your project’s EnableAuthenticator.cshtml file and update the scripts section at the bottom of the page to the following.

@section Scripts {
    @await Html.PartialAsync("_ValidationScriptsPartial")
    
    <script type="text/javascript" src="~/lib/qrcode/qrcode.js"></script>
    <script type="text/javascript">
        new QRCode(document.getElementById("qrCode"),
            {
                text: "@Html.Raw(Model.AuthenticatorUri)",
                width: 150,
                height: 150
            });
    </script>

Enabling Two-Factor Authentication

The above changes are all that is required, and now we are going to walk through what it looks like for the user. Once logged in click on your user in in the upper right after of the site to open user management.

Next, click the Two-factor authentication link.

Then, click Configure authenticator app link.

This will land you on the page with the QR code.

Now using the authenticator application of your choice you can scan the QR code. Once scanned your authenticator application will display a code. Enter the code in the Verification Code box and click Verify. If all goes well you will be taken to a page that lists a set of recovery code for use if you can’t use your authenticator application for some reason.

Wrapping Up

I think it is awesome how easy the ASP.NET Core team has made the use two-factor authentication. With this being built into the templates it pushes all in the right direction. I do wish they would have left SMS as an option, but hopefully, it wouldn’t be hard to put back in.

The code in the final state can be found here.

Two-factor Authentication in ASP.NET Core 2 Read More »

Identity Server: Migration to version 2.1 and Angular HTTP Changes

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

ASP.NET Core Update

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

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

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

Identity Server Update

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

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

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

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

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

Angular Issues

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

An unhandled exception occurred while processing the request.

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

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

Next, in the imports array make the following change.

Before:
HttpModule

After:
HttpClientModule

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

'@angular/common/http'

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

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

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

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

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

        this.oidcSecurityService.setupModule(openIdImplicitFlowConfiguration);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Wrapping up

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

The files in the completed can be found here.

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

Auth0: Usage from Angular

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

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

API Application

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

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

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

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

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

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

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

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

Client Application

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

Configuration values from appsettings.json

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    this.oidcSecurityService.setupModule(openIdImplicitFlowConfiguration);

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

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

Wrapping Up

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

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

The completed code can be found here.

Auth0: Usage from Angular Read More »