Over a million developers have joined DZone.

Upgrading to ASP.NET Core RC2, OpenIdConnect, JWT, Swagger, AutoRest, and Angular 2 SPA (Part 2)

Continuing on from a previous post, this article details my journey in upgrading a Service Fabric multi-tenant application from .NET Core RC1 to RC2. Read on and learn from my experience.

· Web Dev Zone

Start coding today to experience the powerful engine that drives data application’s development, brought to you in partnership with Qlik.

Continuing on from a previous post, this article details my journey in upgrading a Service Fabric multi-tenant application from .NET Core RC1 to RC2, which turned out to be a breaking—albeit worthwhile—change, specifically for the Startup.cs class and related boot strapping code for Swagger, CookieAuthentication, OpenIdConnectAuthentication, and JwtBearerAuthentication. In subsequent articles, we’ll explore how .NET Core RC2 hosts web applications but for now, let’s look at the first challenge encountered during the upgrade, which was to chase down all required libraries that are also .NET Core RC2 compatible.

As of the time of writing, I could only get Swashbuckle version 6.0.0-beta9 to work with .NET Core RC2.

The below code supports multi-tenant Azure AD authentication and is meant for development scenarios as ValidateIssuer and RequireHttpsMetadata are both set to false for simplicity.

The full dependencies section of your project.json should look something like this:

"dependencies": {
    "Microsoft.AspNetCore.Hosting": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Authentication": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Authentication.Cookies": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Authentication.JwtBearer": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Diagnostics": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.SpaServices": "1.0.0-beta-000004",
    "Microsoft.AspNetCore.StaticFiles": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Mvc.Core": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Mvc.Formatters.Json": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Server.Kestrel": "1.0.0-rc2-final",
    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-rc2-final",
    "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0-rc2-final",
    "Microsoft.Extensions.Configuration.Json": "1.0.0-rc2-final",
    "Microsoft.IdentityModel.Clients.ActiveDirectory": "3.9.302261508-alpha",
    "Microsoft.Extensions.Configuration.Binder": "1.0.0-rc2-final",
    "Swashbuckle": "6.0.0-beta9",
    "Swashbuckle.SwaggerUi": "6.0.0-beta9",
    "Swashbuckle.SwaggerGen": "6.0.0-beta9"
}

Your Startup.cs usings should look something like the below:

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json.Serialization;
using System;
using System.Net;

Having sourced the relevant libraries and compatible versions, it’s now time to turn our attention to the ConfigureServices method wherein we’ll set up Swagger, tweak JSON formatting for JavaScript clients such as our Angular 2 SPA, and finally, also tweak how AutoRest generates client code. I want AutoRest to generate separate files per server side controller which is achieved through a custom SwaggerOperationNameFilter.

public IServiceProvider ConfigureServices(IServiceCollection services)
{
	// Add MVC service
	services.AddMvc().AddJsonOptions(options =>
	{
		// Support for JavaScript clients which assume CamelCase - starting with lower case
		options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
	});

	// Add Swagger API service
	services.AddSwaggerGen();
	services.ConfigureSwaggerGen(options =>
	{
		options.SingleApiVersion(new Swashbuckle.SwaggerGen.Generator.Info
		{
			Version = "v1",
			Title = "Acme API",
			Description = "Acme API Home",
			TermsOfService = "Legal"
		});

		// Controls how tools like AutoRest generate client code (separate files per server side controller)
		options.OperationFilter<SwaggerOperationNameFilter>();
		options.DescribeStringEnumsInCamelCase();
		options.DescribeAllEnumsAsStrings();
	});
    
	var acmeOptions = new AcmeOptions();
	Configuration.Bind(acmeOptions);
	services.AddSingleton(acmeOptions);
    
	// Configure IoC service
	var builder = new ContainerBuilder();
	builder.Populate(services);
	var container = builder.Build();
	return container.Resolve<IServiceProvider>();
}

Code for the custom SwaggerOperationNameFilter:

internal class SwaggerOperationNameFilter : IOperationFilter
{
	public void Apply(Operation operation, OperationFilterContext context)
	{
		operation.OperationId = context.ApiDescription.GroupName + "_" + operation.OperationId;
	}
}

Concluding the changes required for the .NET Core RC2 upgrade, we dive into the Configure method. Canny readers will notice that UseCookieAuthentication, UseOpenIdConnectAuthentication, and UseJwtBearerAuthentication have been refactored to handle options in a more consistent manner with the rest of the .NET Core APIs.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{	
	if (env.IsDevelopment())
	{
		app.UseDeveloperExceptionPage();
	}

	app.UseStaticFiles();

	app.UseCookieAuthentication(new CookieAuthenticationOptions
	{
		AuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme,
		AutomaticAuthenticate = true,
		AutomaticChallenge = true,
		CookieSecure = CookieSecureOption.Never,
		// The default setting for cookie expiration is 14 days. SlidingExpiration is set to true by default
		ExpireTimeSpan = TimeSpan.FromHours(1),
		SlidingExpiration = true
	});

	var acmeOptions = app.ApplicationServices.GetService<AcmeOptions>();

	app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
	{
		AutomaticAuthenticate = true,
		AutomaticChallenge = true,
		ClientId = acmeOptions.ClientId,
		Authority = AcmeConstants.AuthEndpointPrefix + "common/",
		PostLogoutRedirectUri = acmeOptions.PostLogoutRedirectUri,
		CallbackPath = AcmeRouteConstants.LoginCallbackRoute,
		SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme,
		AuthenticationScheme = OpenIdConnectDefaults.AuthenticationScheme,
		TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = false },
		RequireHttpsMetadata = false,
		Events = new OpenIdConnectAuthenticationEvents(acmeOptions)
		{
			OnAuthenticationFailed = context => OpenIdConnectAuthenticationEvents.GetFailedResponse(context)
		}
	});

	// Add JwtBearerAuthentication middleware 
	app.UseJwtBearerAuthentication(new JwtBearerOptions
	{
		AuthenticationScheme = JwtBearerDefaults.AuthenticationScheme,
		Audience = acmeOptions.JwtAudience,
		AutomaticAuthenticate = true,
		AutomaticChallenge = true,
		Authority = AcmeConstants.AuthEndpointPrefix + "common/",
		TokenValidationParameters = new TokenValidationParameters
		{
			ValidateIssuer = false,
		},
		RequireHttpsMetadata = false,
		Events = new JwtBearerAuthenticationEvents(acmeOptions)
		{
			OnAuthenticationFailed = context => JwtBearerAuthenticationEvents.GetFailedResponse(context)
		}
	});

	app.UseMvc(routes =>
	{
		routes.MapRoute(
			name: "webapi",
			template: "api/{controller}/{action}/{id?}");

		routes.MapSpaFallbackRoute("spa-fallback", new { controller = "Home", action = "Index" });
	});

	// Enable Use of Swagger
	app.UseSwaggerGen();
	app.UseSwaggerUi();
}

If you’re wondering why I left the Microsoft.IdentityModel.Clients.ActiveDirectory library at “3.9.302261508-alpha”, in upcoming articles we’ll detail a strategy for automated integration testing of your .NET Core APIs using xUnit and optionally a BDD approach (SpecFlow).

That's all for now... stay tuned for future posts.

Create data driven applications in Qlik’s free and easy to use coding environment, brought to you in partnership with Qlik.

Topics:
asp.net core ,swagger ,jwt ,service fabric ,microsoft

Published at DZone with permission of Andrej Medic. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}