Configuration

ASP.NET Core 3.1 Web.config Transform for Production

I recently upgraded an application from ASP.NET Core 2.2 to 3.1 around the same time the application got moved to a new server. Side note, don’t make two large changes like that at the same time if you can help it as it always makes tracking down the cause of the issue much harder.  The application in question was created using the ASP.NET Core React template initially.

The Error

After the changes above the site started returning the following error.

HTTP Error 500.0 – ANCM In-Process Handler Load Failue

To diagnose the issue I enabled standard out logging by changing the stdoutLogEnabled to true. The following is the line from the application’s web config with the logging enabled.

<aspNetCore processPath="dotnet" arguments=".\YourApplication.dll" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout">

The Cause

The logging showed that the application was trying to start the React part of the application using the React Development Server, which uses npm. Here is the related code from the Configure function of the Startup class.

app.UseSpa(spa =>
           {
               spa.Options.SourcePath = "ClientApp";

               if (env.IsDevelopment())
               {
                   spa.UseReactDevelopmentServer(npmScript: "start");
               }
           });

As you can see from the highlighted area the Reac Development Server should only be used when the environment is set to development and sure enough, the web.config has environment variable for ASPNETCORE_ENVIRONMENT and the value is Development as you can see in the following example.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <handlers>
        <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath="dotnet" arguments="YourApplication.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="InProcess">
        <environmentVariables>
          <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" />
        </environmentVariables>
      </aspNetCore>
    </system.webServer>
  </location>
</configuration>

A Fix

My knee jerk reaction was to find a way to get the value of that variable changed when the application is published in release mode. This helped me find the Microsoft docs for Transform web.config.

For this fix, I used a build configuration base transform. To do this I added a new file named web.Release.config. With this new file present, the transforms in the file will be executed when a release build is run. The following is the transform I used to get ASPNETCORE_ENVIRONMENT set to Production.

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <location>
    <system.webServer>
      <aspNetCore>
        <environmentVariables>
          <environmentVariable xdt:Transform="Replace" xdt:Locator="Match(name)" name="ASPNETCORE_ENVIRONMENT" value="Production" />
        </environmentVariables>
      </aspNetCore>
    </system.webServer>
  </location>
</configuration>

It is important that the structure of your transforms match what is in your actual web.config or the transform won’t be able to locate the element that needs to be transformed which in this case we are looking for an element under configuration/location/system.webServer/aspNetCore/enviromentVariables.

In our example, we are telling the transform we want to replace the element  (xdt:Transform=”Replace”) that matches the name (xdt:Locator=”Match(name)”) ASPNETCORE_ENVIRONMENT with the value Production.

Wrapping Up

This is just one small example of what can be done with web.config transforms. The official docs gave me a general high-level idea of what transforms can do, but wasn’t super helpful for what I needed to do. If the docs don’t cover your use case get ready to do a fair amount of searching.

Side note in the application I was referencing above it turns out that the web.config in the published for .NET Core 2.1 didn’t contain a value for ASPNETCORE_ENVIRONMENT which was why it wasn’t an issue before.

ASP.NET Core 3.1 Web.config Transform for Production Read More »

Visual Studio Missing Scaffolding for ASP.NET Core

This morning I set out to try the Scaffold Identity option that was added as part of the ASP.NET 2.1 release. The docs for this feature is really good so I didn’t think I would have any issue, but I was wrong.

Sample Application

To start I used the .NET CLI to create a new application using the following command.

dotnet new razor

I then opened the project in Visual Studio. Then following the docs I right-clicked on the project and expanded the Add option, but the New Scaffolded item is missing.

Sample Application 2

Since the project created using the CLI didn’t have the option I thought I would try creating a new project in Visual Studio. From in Visual Studio using File > New > Project.

Under Visual C# > Web select ASP.NET Core Web Application and click OK.

Select Web Application and then click OK.

After the creation process finishes I right-clicked on the project and expanded the Add option and the New Scaffolded item is there.

What’s the difference?

After trying a lot of different things I finally got to the point of looking at the csproj files for both projects. Here is the csproj from the Visual Studio create project.

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.1.2" PrivateAssets="All" />
  </ItemGroup>
</Project>

And the csproj from the CLI created project.

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.2</TargetFramework>
  </PropertyGroup>


  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0-preview3-35497" PrivateAssets="All" />
  </ItemGroup>
</Project>

Notice that the CLI created project is targeting .NET Core 2.2 which is in preview at the time of this writing. Not surprisingly Visual Studio has some limits on what can be done using preview bits.

Wrapping Up

I can’t believe that I let having preview bits installed cause me issues again. I’m guessing that most people aren’t installing the previews so hopefully, this isn’t an issue most of you will have to deal with.

If you do want to use the CLI to create an application targeting a specific version of .NET Core it can be done using a global.json file. Check out Controlling .NET Core’s SDK Version for more information.

Visual Studio Missing Scaffolding for ASP.NET Core Read More »

Controlling .NET Core’s SDK Version

Recently I was building a sample application and noticed a build warning that I was using a preview version of the .NET Core SDK.

You can see from the screen show that the build shows zero warning and zero errors, but if you read the full build text you will notice the following message.

NETSDK1057: You are working with a preview version of the .NET Core SDK. You can define the SDK version via a global.json file in the current project. More at https://go.microsoft.com/fwlink/?linkid=869452 [C:\sdkTest\sdkTest.csproj]

That will teach me not to just look at the ending results of a build. I didn’t explicitly install the preview version I have been accidentally using, but I’m pretty sure it got installed with the Visual Studio Preview I have installed.

Setting the SDK version for a project

Following the link in the message from above will take you to the docs page for global.json which will allow you to specify what version of the SDK you want to use. Using the following command will give you a list of the SDK versions you have installed.

dotnet --list-sdks

On my machine, I have the following 5 SDKs installed.

2.1.201 [C:\Program Files\dotnet\sdk]
2.1.202 [C:\Program Files\dotnet\sdk]
2.1.302 [C:\Program Files\dotnet\sdk]
2.1.400-preview-009063 [C:\Program Files\dotnet\sdk]
2.1.400-preview-009171 [C:\Program Files\dotnet\sdk]

For this project, I really want to use version 2.1.302 which is the newest non-preview version. Using the following .NET CLI command will create a global.json file targeting the version we want.

dotnet new globaljson --sdk-version 2.1.302

If you open the new global.json file you will see the following which has the SDK version set to the value we specified. If you use the above command without specifying a version it will use the latest version installed on your machine, including preview versions.

{
  "sdk": {
    "version": "2.1.302"
  }
}

Now if you run the build command you will see that the warning is gone.

global.json Location

So far we have been using global.json from within a project directory, but that isn’t the only option for its location. For example, if I moved the global.json from the C:\sdkTest directory to C:\ then any .NET Core base application on the C drive would use the version specified in that top-level global.json unless they specified their own version (or had another global.json up the projects folder structure before it made it to the root of the C drive).

For the full details see the matching rules in the official docs.

Wrapping Up

I’m not sure if anyone one else is in this boat, but until I saw the build warning from above the need to control the SDK version never crossed my mind. Thankfully the teams have Microsoft have made the ability to lock to a version of the SDK simple. I could see this being especially useful if you are required to stick with a long-term support version of the SDK, which would currently require you to be on version 1.0 or 1.1.

Controlling .NET Core’s SDK Version Read More »

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

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

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

The Problem

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

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

Configuration API Endpoint

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

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

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

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

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

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

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

Angular Changes

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

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

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

@Injectable()
export class ConfigurationService {

  private configuration: IServerConfiguration;

  constructor(private http: HttpClient) { }

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

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

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

}

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

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

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

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

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

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

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

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

 Wrapping Up

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

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

ASP.NET Core 2 Fails to Publish With Angular 5

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

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

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

The Fix

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

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

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

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

Before:
new AotPlugin

After:
new AngularCompilerPlugin

Wrapping Up

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

 

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

Pass ASP.NET Core Appsettings Values to Angular

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

Settings

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

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

Providing Configuration Data to Angular

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

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

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

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

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

@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration

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

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

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

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

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

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

Angular Server-Side Boot

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

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

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

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

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

Angular Client-Side

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

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

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

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

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

Wrapping Up

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

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

Email with ASP.NET Core Using Mailgun

Sending emails from ASP.NET Core using Mailgun is a topic I covered in this post almost a year ago. The previous post was before ASP.NET Core hit 1.0 and I didn’t save or upload the code to GitHub. Based on the comments on the post I decided to redo the post using the current version of ASP.NET.

Starting point

For this post, I created a new ASP.NET Core Web Application targeting .NET Core using Individual User Accounts for authentication. The project before any changes for email can be found here.

UI and Controller change to support email

The project template comes with all the UI and controller functions need to support email, but they are commented out. The following is going to walk through uncommenting the proper code.

Account controller

Starting with the Register function the code to send a confirmation email needs to be uncommented and the existing call to _signInManager.SignInAsync should be commented out to keep a user from being signed in before their email address has been confirmed. The following is after the changes.

var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Action("ConfirmEmail", "Account", 
                             new { userId = user.Id, code = code }, 
                             protocol: HttpContext.Request.Scheme);
await _emailSender.SendEmailAsync(model.Email, "Confirm your account",
   $"Please confirm your account by clicking this link: 
     <a href='{callbackUrl}'>link</a>");
//await _signInManager.SignInAsync(user, isPersistent: false);

Next, in the Login function add a check to verify a user’s account has been confirmed before allowing them to sign in. The new code starts with var user = await _userManager.FindByNameAsync(model.Email); the code above it is just to provide context.

 public async Task<IActionResult> Login(LoginViewModel model, 
                                        string returnUrl = null)
 {
      ViewData["ReturnUrl"] = returnUrl;
      if (ModelState.IsValid)
      {
          var user = await _userManager.FindByNameAsync(model.Email);
          if (user != null)
          {
              if (!await _userManager.IsEmailConfirmedAsync(user))
              {
                  ModelState.AddModelError(string.Empty, 
                              "You must have a confirmed email to log in.");
                  return View(model);
              }
          }

Finally, in the ForgotPassword function uncomment the following to enable sending the user a password reset link.

var code = await _userManager.GeneratePasswordResetTokenAsync(user);
var callbackUrl = Url.Action("ResetPassword", "Account", 
                             new { userId = user.Id, code = code }, 
                             protocol: HttpContext.Request.Scheme);
await _emailSender.SendEmailAsync(model.Email, "Reset Password",
   $"Please reset your password by clicking here: <a          
     href='{callbackUrl}'>link</a>");
return View("ForgotPasswordConfirmation");
Forgot password view

To enabled the UI related to sending an email for a forgotten password open ForgotPassword.cshtml found in the Views/Account/ directory and uncomment the following.

<form asp-controller="Account" asp-action="ForgotPassword" 
      method="post" class="form-horizontal">
    <h4>Enter your email.</h4>
    <hr />
    <div asp-validation-summary="All" class="text-danger"></div>
    <div class="form-group">
        <label asp-for="Email" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <input asp-for="Email" class="form-control" />
            <span asp-validation-for="Email" class="text-danger"></span>
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
             <button type="submit" 
                     class="btn btn-default">Submit</button>
        </div>
    </div>
</form>

Warning for sites with existing users

The changes above will cause issues for any existing users since they will not have completed the email confirmation step keeping them from being able to log in or reset passwords. Manually marking existing users as confirmed can be done by updating the EmailConfirmed bit field to true in the AspNetUsers table.

Mailgun

Mailgun is an email service run by Rackspace that provides a simple API for sending emails. The free level of the service allows up to 10k emails to be sent a month.

You can sign up for an account here. Once logged in go to the Domains section.

Next, select your domain should only be one if you are on a new account. This will take you to a screen that looks like the following some of which will be needed to connect with the Mailgun API. I took the time to replace my information with a fake version so this screen shot could be referenced using the values from the screenshot for the rest of the post.

Configuration

Settings class

In order to hold and load Mailgun email related settings add a new EmailSettings class. In the sample project, this class can be found in the Configuration directory. The following is the full contents of the file.

public class EmailSettings
{
    public string ApiKey { get; set; }
    public string ApiBaseUri { get; set; }
    public string RequestUri { get; set; }
    public string From { get; set; }
}
User secrets introduction

User secrets is a concept in ASP.NET Core used to set configuration items and have them stored outside of the project so they will be excluded from version control. They are a great way to store private API key and related items which is why they will be used to store our Mailgun configuration items. I will be coving the basics here, but for a more detail explanation check out the official docs on the topic of app secrets.

Setting user secrets

In the Solution Explorer right-click on the project and select Manage User Secrets.

This will open the secrets.json file which will be used to store secrets related to the select project. Keep in mind this file is stored in your user directory in an unencrypted way so don’t view it as a secured store.

Based on the screenshot above from Mailgun’s domain detail page the json file would look like the following. The RequestUri is the only setting not pulled from the domain settings above and would just need fakesandbox replaced with the sandbox ID for your domain.

{
  "EmailSettings": {
    "ApiKey": "api:key-fakeapikey",
    "ApiBaseUri": "https://api.mailgun.net/v3/",
    "RequestUri": "fakesandbox.mailgun.org/messages",
    "From": "[email protected]"
  }
}
Loading user secrets in Startup

In the ConfigureServices function of the Startup class the EmailSettings section of our user secrets can be loaded and made available via the dependency injection system using the following line of code.

services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));

Not that user secrets are only meant to be used for development and for a production build of the applications the settings would need to be moved to a different location such as environment variables or Azure Key Vault.

Using Mailgun to send email

Not that the application has the email sending portion of the UI enabled the SendEmailAsync function of the AuthMessageSender class needs to be implemented. The class can be found in the MessageServices.cs file of the Services directory.

Injection of email settings

The first change needed is to add a class level variable to store email settings and to add a constructor that will allow the email setting to be injected.

private readonly EmailSettings _emailSettings;

public AuthMessageSender(IOptions<EmailSettings> emailOptions)
{
    _emailSettings = emailOptions.Value;
}
Sending an email

The body of the SendEmailAsync function is where the call to Mailgun’s API will be made using the email setting injected via the class’s constructor. The following is the full body of the function.

using (var client = new HttpClient { BaseAddress = 
                                     new Uri(_emailSettings.ApiBaseUri) })
{
    client.DefaultRequestHeaders.Authorization = 
      new AuthenticationHeaderValue("Basic",
    Convert.ToBase64String(Encoding.ASCII.GetBytes(_emailSettings.ApiKey)));

    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("from", _emailSettings.From),
        new KeyValuePair<string, string>("to", email),
        new KeyValuePair<string, string>("subject", subject),
        new KeyValuePair<string, string>("html", message)
    });

    await client.PostAsync(_emailSettings.RequestUri, 
                           content).ConfigureAwait(false);
}

The Mailgun API key is sent as an authentication header value with the rest of the parameters being sent via form URL encoded content. Finally, the Request URI from the email settings is used to send the post request to Mailgun.

If you just need to send a plain text instead of HTML the “html” key can be replaced with “text”.

Authorized recipients required for test domain

When using Mailgun with the default test domain note that only emails addressed to Authorized Recipients will be delivered. To add a recipient click Authorized Recipients button on Mailgun’s Domains page.

This will take you to the Authorized Recipients page where you can use the Invite New Recipient button to add a recipient.

Enter the email address you want to add and click the Send Invite button. After the email address is confirmed mail to that address will be delivered. Keep in mind this is only for test accounts and doesn’t have to be done when being used with a real domain.

Using logs to verify state of emails

During my testing, I wasn’t seeing emails come through and I thought something was wrong with my code, but it turned out that Mailgun was getting the request to send the mail they just hadn’t be processed yet. The Logs section of your Mailgun account is helpful for determining if they are getting your request to send an email or not.

As you can see in the screenshot the email request was accepted, but not yet delivered. It was 10 minutes later before the email was actually delivered. I am not sure if this delay is just for test domains or if would apply to live ones as well.

Other Email Options

Mailgun is obviously not the only option for sending emails. This post from Mashape lists 12 API providers. In addition SMTP is also an option which this post by Steve Gordon covers.

Wrapping up

The code finished code that goes with this post can be found here. Thank you to all the commenters on the original post for stepping in when I didn’t have all the answer or code available.

Email with ASP.NET Core Using Mailgun Read More »

ASP.NET Core User Options and Custom Validators

Last week’s post looked at ASP.NET Core’s password options and custom validators and this week I am going to cover the built-in user options as well as writing custom user validators.

User Options

Just as last week I am going to start off showing the default registration of identity with ASP.NET Core’s built in dependency injection which is found in the ConfigureServices function of the Startup class.

services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

Using the same overload of  AddIdentity  as last week which accepts an IdentityOptions there are two built-in user options for AllowedUserNameCharacters and RequireUniqueEmail. The following example shows both options.

services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
     options.User.AllowedUserNameCharacters = "abcom@.";
     options.User.RequireUniqueEmail = true;
})
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

When using AllowedUserNameCharacters make sure to include all the characters needed to make up a valid email address. My first test I left out the @ symbol and couldn’t create an account.

Custom User Validtors

The above built-in user validation options are pretty limited, but this just reflects that validation on users has standardized on email addresses for the most part. In the case that you require more specialized user validation the AddUserValidator extension to IdentityBuilder can be used in the AddIdentity chain to add custom validation based on the IUserValidator interface.

The only function on this interface is ValidateAsync. The following is an example user validator that limits users to a specific set of domains. For this example the domains are part of the validator class, but if this is something actually needed I would recommend storing the domains in a different way such as in a database or configuration.

public class UserDomainValidator<TUser> : IUserValidator<TUser> 
       where TUser : IdentityUser
{
    private readonly List<string> _allowedDomains = new List<string>
    {
        "elanderson.net",
        "test.com"
    };

    public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, 
                                              TUser user)
    {
        if (_allowedDomains.Any(allowed => 
               user.Email.EndsWith(allowed, StringComparison.CurrentCultureIgnoreCase)))
        {
            return Task.FromResult(IdentityResult.Success);
        }

        return Task.FromResult(
                 IdentityResult.Failed(new IdentityError
                 {
                     Code = "InvalidDomain",
                     Description = "Domain is invalid."
                 }));
    }
}

If the user’s email address doesn’t end with one of the domains in _allowedDomains the user will fail validation and the reason will be added to the list of identity validation errors which end up being show to the user. This validation is on the server side.

The following is what identity registration looks like with the user options and custom user validation.

services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
    options.User.AllowedUserNameCharacters = "abccom.";
    options.User.RequireUniqueEmail = true;
})
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders()
    .AddUserValidator<UserDomainValidator<ApplicationUser>>();

Wrapping Up

Following the above you can see how easy it to add any custom user validation that might be needed. It is also nice that adding custom validation on user and passwords are very similar.

ASP.NET Core User Options and Custom Validators Read More »

ASP.NET Core Password Options and Custom Validators

ASP.NET Core provides a lot of identity feature out of the box when individual user accounts is selected during project creation. Using the default settings a user’s password is required to be at least 6 characters and contain a number, a lower case letter, an uppercase letter and a special character. This post is going to cover changing the the above options as well as creating custom validators.

Password Options

The following is the default registration of identity in the ConfigureServices  function of the Startup  class with the default settings mentioned above.

services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

AddIdentity  can accept options part of which allows control over the basic characteristics of what is required for user passwords. Here is the same AddIdentity but with all the options for passwords listed.

services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
    options.Password.RequireDigit = true;
    options.Password.RequireLowercase = true;
    options.Password.RequireNonLetterOrDigit = true;
    options.Password.RequireUppercase = true;
    options.Password.RequiredLength = 6;
})
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

All the options do what you would expect. One thing to note is if you change the required length by setting options.Password.RequiredLength then the new setting will only be validated on post back to the server, which is the case of most password validation anyway, but for pre-post validation on length then the string length data annotation needs to be updated on RegisterViewModel.Password, ResetPasswordViewModel.Password, ChangePasswordViewModel.NewPassword and SetPasswordViewModel.NewPassword.

Custom Password Validators

The above is great for changing simple aspects of password validation, but we all know password rules for organizations are not always simple enough to be covered by the above. Thankfully Microsoft has provided the AddPasswordValidator  extension method to the IdentityBuilder class which is what is returned by AddIdentity.

AddPasswordValidator takes a type that implements IPasswordValidator. The custom validator only has to implement the  ValidateAsync defined by IPasswordValidator. The following validator checks to make sure that all the characters of the password are not the same and returns an IdentityResult based on the conditions passing. Forgive the contrived example, but I wanted to keep the class as simple as possible.

public class SameCharacterPasswordValidator<TUser>: IPasswordValidator<TUser> 
       where TUser : class
{
    public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, 
                                              TUser user, 
                                              string password)
    {
        return Task.FromResult(password.Distinct().Count() == 1 ? 
            IdentityResult.Failed(new IdentityError
            {
                Code = "SameChar",
                Description = "Passwords cannot be all the same character."
            }) : 
            IdentityResult.Success);
    }
}

If validation failed is the result then is added to the list of validation messages the user sees just like with the built in password validations.

Here is registration of identity with the custom password validation which is on the last line.

services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
    options.Password.RequireDigit = true;
    options.Password.RequireLowercase = true;
    options.Password.RequireNonLetterOrDigit = true;
    options.Password.RequireUppercase = true;
    options.Password.RequiredLength = 6;
})
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders()
    .AddPasswordValidator<SameCharacterPasswordValidator<ApplicationUser>>();

Potential Use

Imagine you have a requirement to make sure a user doesn’t reuse the same password for a period of time. This would be a great place for a custom password validator. You could use dependency injection to get reference to a history of password hashes and use that to verify the user is not repeating the same password. Of course would have to first write the password hash history.

ASP.NET Core Password Options and Custom Validators Read More »

Dependency Injection Conditional Registration in ASP.NET Core

Now that I know emailing and SMS are working I wanted a way to get the information from the out going messages without actually hitting a third party service or having to use break points. To do this I wrote a new class that implemented IEmailSender and ISmsSender that wrote the messages to a file instead of hitting Mailgun and Twilio.

Dependency Injection (DI)

ASP.NET Core comes with dependency injection built-in the details of which are described in the docs and the code can be found here. The docs article does a great job of explaining DI so I am going to keep my description limited to the example at hand.

In ASP.NET Core services are registered in the ConfigureServices of the StartUp class. When an application first starts running the StartUp constructor runs followed by ConfigureServices and finally Configure.

Configuration Based Services

One potential way to handle decisions on which services to register is via configuration settings.  Using the following configuration as an example if WriteToFile is true then the file writer version of the email service should be used instead of the version that actually emails users.

{
  "EmailSettings": {
    "WriteToFile": true,
    "Path": "C:\Email\"
  }
}

With this configuration in place ConfigureServices uses the following to load the proper service based on the config value.

if (Configuration.Get<bool>("EmailSettings:WriteToFile"))
{
    services.AddTransient<IEmailSender, AuthMessageSenderFileWriter>();
}
else
{
    services.AddTransient<IEmailSender, AuthMessageSender>();
}

Environment Based Services

ASP.NET Core’s IHostingEnvironment defines an EnvironmentName which automatically gets loaded by the host from an environment variable. To quote Visual Studio’s Object Browser on EnvironmentName:

Gets or sets the name of the environment. This property is automatically set by the host to the value of the “Hosting:Environment” (on Windows) or “Hosting__Environment” (on Linux & OS X) environment variable.

I found this post by Armen Shimoon which clued me in on the fact that ConfigureServices is only called ASP.NET Core if no Configure{EnvironmentName}Services is found. This means that if EnviromentName is set to Development then ConfigureDevelopmentServices will be called instead of ConfigureServices. Extensions are provided out of the box to help support Development, Staging and Production values.

For development runs of the application to write to a file instead of emailing I added a ConfigureDevelopmentServices.

public void ConfigureDevelopmentServices(IServiceCollection services)
{
    ConfigureServices(services);
    services.AddTransient<IEmailSender, AuthMessageSenderFileWriter>();
}

The first thing this function does is to call ConfigureServices since that is where the majority of service registrations is done. ConfigureServices registers an IEmailSender with AuthMessageSender, but that registration is overwritten with AuthMessageSenderFileWriter in ConfigureDevelopmentServices after the ConfigureServices function returns.

Final Thoughts

I have presented a couple of ways to handled varying registrations with ASP.NET Core’s built in dependency injection. I see using the ability to run different version of ConfigureServices to be the one I will get the most use out of, but I am sure basing the decision off of configuration could come in handy as well.

If you know of other options please leave a comment.

Dependency Injection Conditional Registration in ASP.NET Core Read More »