MVC 6

Auth0: Introduction and Initial Project Setup

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

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

Sign-up

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

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

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

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

Auth0 Setup

Client

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

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

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

API

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

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

Sample Solution Structure and Setup

The sample solution for this post has two projects.

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

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

To start, add a directory to contain the solution.

API Application

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

dotnet new webapi
Client Application

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

dotnet new angular

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

npm install
Solution

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

dotnet new sln --name AspNetCoreAngularAuth0

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

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

Securing the API Application

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

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

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

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

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

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

app.UseAuthentication();

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

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

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

Client Application

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

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

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

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

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

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

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

    options.ClaimsIssuer = "Auth0";

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

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

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

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

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

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

app.UseAuthentication();
Testing Setup

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

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

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

        return View();
    }

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

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

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

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

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

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

<p>
    @ViewBag.ApiResults
</p>

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

Wrapping Up

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

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

 

Auth0: Introduction and Initial Project Setup Read More »

Identity Server: Using Entity Framework Core for Configuration Data

This post is a continuation of a series of posts that follow my initial looking into using IdentityServer4 in ASP.NET Core with an API and an Angular front end. The following are the related posts.

Identity Server: Introduction
Identity Server: Sample Exploration and Initial Project Setup
Identity Server: Interactive Login using MVC
Identity Server: From Implicit to Hybrid Flow
Identity Server: Using ASP.NET Core Identity
Identity Server: Using Entity Framework Core for Configuration Data (this post)
Identity Server: Usage from Angular

This post is going to take the existing solution this series has been using and switch from using hard coded configuration data, found in the Config class of the Identity Application and moving it to a database using Entity Framework Core. As with prior entries, this will be following the intent of one of the official quick starts for Using Entity Framework Core for configuration data. This post is fairly different just because our example project already uses entity framework so a lot of steps can be skipped. The starting point of the code can be found here. All the changes in this post will be taking place in the Identity Application.

Identity Application

Thankfully the creators of IdentityServer provide a NuGet package that includes all the bits needed to move configuration data and operational data to Entity Framework Core. Start by added the following NuGet package.

  • IdentityServer4.EntityFramework
Startup

With the above NuGet package installed the ConfigureServices function of the Startup class needs to be changed to tell IdentityServer the new place to pull data from. The following is the new version of the AddIdentityServer call updated to use Entity Framework Core.

var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;

services
  .AddIdentityServer()
  .AddTemporarySigningCredential()
  .AddAspNetIdentity<ApplicationUser>()
  .AddConfigurationStore(builder =>
    builder
     .UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
                   options =>
            options.MigrationsAssembly(migrationsAssembly)))
  .AddOperationalStore(builder =>
    builder
     .UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
                   options =>
            options.MigrationsAssembly(migrationsAssembly)));

Notice that the following have all been replaced by AddConfigurationStore and AddOperationalStore.

  • AddInMemoryPersistedGrants
  • AddInMemoryIdentityResources
  • AddInMemoryApiResources
  • AddInMemoryClients

The other thing of note is the migrationsAssembly and its usage via options.MigrationsAssembly. This is moving the management of the migrations from the assembly that the contexts are defined to the Identity Application. This is needed in this case since the two contexts in question are defined in a NuGet package.

Migrations

Now that the configuration is done for the new contexts migrations need to be added to them. As always there are two ways to handle this either via the Package Manager Console or from a command prompt. I am going to use the command prompt this round to match the IdentityServer docs. Run the following two commands from the same path as the Identity Application’s csproj file.

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

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

This is the first time I have used the -o argument which controls where the migration is output and following the docs example I am putting the migrations that are for entities outside of the control of the application into a subdirectory. Speaking of the entities being outside of the control of the main application, this means anytime the NuGet package that contains the entity is updated a check will need to be made to see if new migrations are needed.

Database Migrations and Seed Data

Since the DbContext classes that need migrations run are outside of the control our application if automatic migrations must be handled in a different way than with the identity-related context used previously in this series. Following the official docs, I am going to create an InitializeDatabase function that will apply any needed migrations as well as add seed data. To do this I am adding a new IdentityServerDatabaseInitialization class in the Data/IdentityServer directory. The following is the full class.

public static class IdentityServerDatabaseInitialization
{
    public static void InitializeDatabase(IApplicationBuilder app)
    {
        using (var serviceScope = app.ApplicationServices
                                     .GetService<IServiceScopeFactory>()
                                     .CreateScope())
        {
            PerformMigrations(serviceScope);
            SeedData(serviceScope);
        }
    }

    private static void PerformMigrations(IServiceScope serviceScope)
    {
        serviceScope.ServiceProvider
                    .GetRequiredService<ConfigurationDbContext>()
                    .Database
                    .Migrate();
        serviceScope.ServiceProvider
                    .GetRequiredService<PersistedGrantDbContext>()
                    .Database
                    .Migrate();
    }

    private static void SeedData(IServiceScope serviceScope)
    {
        var context = serviceScope
                       .ServiceProvider
                       .GetRequiredService<ConfigurationDbContext>();

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

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

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

The InitializeDatabase takes an IApplicationBuilder in order to be able to control the lifetime of the two DbContext classes needed. Normally this wouldn’t be needed and the lifetime would be controlled automatically, but since this code is being called from the Startup class instead of during a request (which is how the DI system does auto scoping) the scope is being created by the app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope() call.

The PerformMigrations function pulls the two DbContext objects from the container and applies migrations. Finally in SeedData if the DbSets don’t already contain data then the seed data is pulled from the Config class and saved to the database.

Back to Startup

In the Configure function of the Startup class add the following call to make sure migrations and seed data are run when the application starts.

IdentityServerDatabaseInitialization.InitializeDatabase(app);

Wrapping up

With the above changes, the Identity Application is now using the database for all its persistence. The missing bits are of course UI to manage the related data, but those can be built out as needed. The code in its completed state can be found here.

The next steps for this project will be utilizing IdentityServer from Angular in the Client Application instead of the temporary IdentityController that has had to be used in all the examples so far.

Identity Server: Using Entity Framework Core for Configuration Data Read More »

Identity Server: Using ASP.NET Core Identity

This post is a continuation of a series of posts that follow my initial looking into using IdentityServer4 in ASP.NET Core with an API and an Angular front end. The following are the related posts.

Identity Server: Introduction
Identity Server: Sample Exploration and Initial Project Setup
Identity Server: Interactive Login using MVC
Identity Server: From Implicit to Hybrid Flow
Identity Server: Using ASP.NET Core Identity (this post)
Identity Server: Using Entity Framework Core for Configuration Data
Identity Server: Usage from Angular

This post is going to cover using ASP.NET Core Identity instead of an in-memory user store like the previous examples. As I write this I am working through the Using ASP.NET Core Identity quick start from the docs. This isn’t going to differ a whole lot from the official docs, but I still want to document it to help solidify everything in my head. The starting point of the code for this post can be found here.

Identity Application

The Identity Application will be where the bulk of the changes happen. Since it is much easier to add IdentityServer to a project than it is to add ASP.NET Core Identity we are going to delete the existing Identity Application project and re-create it with Identity from the start. Right click on the IdentityApp project and click remove.

This removes the project from the solution, but the files also need to be deleted off of disk or use a different name. I chose to rename the old project folder on disk so I could still grab any files I might need.

Create a new Identity Application

Right-click on the solution and select Add > New Project.

On the Add New Project dialog under Visual C# > .NET Core select ASP.NET Core Web Application and enter the name of the project (IdentityApp in this example) and click OK.

On the next dialog select the Web Application template.

Next, click the Change Authentication button and select the Individual User Accounts option.

Click OK on the Change Authentication dialog and then click OK on the template dialog. After a few seconds, the solution will contain a new IdentityApp that is using ASP.NET Core Identity with Entity Framework Core.

Adding Identity Server to the Identity App Project

Using NuGet install the IdentityServer4.AspNetIdentity package which will also install IdentityServer4 which the old project was using. Next, copy the Config class from the old IdentityApp project and delete the GetUsers function.

Startup Changes

In the Startup class at the end of ConfigureServices function add the following.

services.AddIdentityServer()
    .AddTemporarySigningCredential()
    .AddInMemoryIdentityResources(Config.GetIdentityResources())
    .AddInMemoryApiResources(Config.GetApiResources())
    .AddInMemoryClients(Config.GetClients())
    .AddAspNetIdentity<ApplicationUser>();

The only difference between this and the one used with the previous posts is instead of AddTestUsers being used to pull a hard coded list of uses our of the Config class users are pulled from the database using ASP.NET Core Identity using this AddAspNetIdentity<ApplicationUser>() call. Identity Server is very flexible and this is only of the option for an identity store.

Next, in the Configure function add app.UseIdentityServer() after app.UseIdentity().

app.UseStaticFiles();
app.UseIdentity();
app.UseIdentityServer();
app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");
});
Database

There are a couple of ways to make sure the database is created and migrated when changes happen. One is via the command line in the project directory using the following command.

dotnet ef database update

The way I normally us when at this stage in development is to add code to the DB context to automatically apply migrations. The following is the full ApplicationDbContext class modified to automatically run migrations when the context is constructed.

public sealed class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    private static bool _migrated;

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
        if (_migrated) return;
        Database.Migrate();
        _migrated = true;
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
    }
}
Now throw that all away

The above is good to go through to know how things work, but I got to this point and wanted the functionality to be on par with the previous entries. The docs made this sound simple, but it was not simple at all I had a list of at least 30 points of files to be moved and changes made to existing files. I am going to spare you all those details and recommend that you just pull the Identity Application from my GitHub repo for this project instead. I did basically the same thing out of the official samples repo to get things working as they should with contents, errors, and log out.

If you want the auto migrations then make sure and keep the version of the ApplicationDbContext from above.

Client Application

No changes are actually required to the client application, but as in the official docs, I made changes to show hitting the API Application using both a user access token and client credentials. The following is the index action on the IdentityController which has been changed to call two functions one for each type of API access.

[Authorize]
public async Task<IActionResult> Index()
{
    var apiCallUsingUserAccessToken = await ApiCallUsingUserAccessToken();
    ViewData["apiCallUsingUserAccessToken"] 
        = apiCallUsingUserAccessToken.IsSuccessStatusCode 
          ? await apiCallUsingUserAccessToken.Content.ReadAsStringAsync() 
          : apiCallUsingUserAccessToken.StatusCode.ToString();

    var clientCredentialsResponse = await ApiCallUsingClientCredentials();
    ViewData["clientCredentialsResponse"] 
        = clientCredentialsResponse.IsSuccessStatusCode 
          ? await clientCredentialsResponse.Content.ReadAsStringAsync() 
          : clientCredentialsResponse.StatusCode.ToString();

    return View();
}

The following is the function to access the API Application using a user access token.

private async Task<HttpResponseMessage> ApiCallUsingUserAccessToken()
{
    var accessToken 
        = await HttpContext.Authentication.GetTokenAsync("access_token");

    var client = new HttpClient();
    client.SetBearerToken(accessToken);

    return await client.GetAsync("http://localhost:5001/api/identity");
}

Now the function to access the API Application using client credentials.

private async Task<HttpResponseMessage> ApiCallUsingClientCredentials()
{
    var tokenClient 
        = new TokenClient("http://localhost:5000/connect/token", 
                          "mvc", "secret");
    var tokenResponse 
        = await tokenClient.RequestClientCredentialsAsync("apiApp");

    var client = new HttpClient();
    client.SetBearerToken(tokenResponse.AccessToken);

    return await client.GetAsync("http://localhost:5001/api/identity");
}

Finally, Index.cshtml found in the Views/Identity directory has the following change.

Replace:
@ViewData["apiResult"]

With:
<dt>api response called with user access token</dt>
<dd>@ViewData["apiCallUsingUserAccessToken"]</dd>

<dt>api response called with client credentials</dt>
<dd>@ViewData["clientCredentialsResponse"]</dd>

Wrapping up

Now the Identity Application is using ASP.NET Core Identity with Entity Framework Core to store users in the database. The next post will cover moving the items now in the Config class into the database. The completed version of the code can be found here.

Identity Server: Using ASP.NET Core Identity Read More »

Identity Server: From Implicit to Hybrid Flow

This post is a continuation of a series of posts that follow my initial looking into using IdentityServer4 in ASP.NET Core with an API and an Angular front end. The following are the related posts.

Identity Server: Introduction
Identity Server: Sample Exploration and Initial Project Setup
Identity Server: Interactive Login using MVC
Identity Server: From Implicit to Hybrid Flow (this post)
Identity Server: Using ASP.NET Core Identity
Identity Server: Using Entity Framework Core for Configuration Data
Identity Server: Usage from Angularsing MVC

This post is going to cover adding back in the API access that was lost in the last post by changing the MVC client to use a hybrid grant instead of an implicit grant. This post was written while working through Switching to Hybrid Flow and adding API Access back in the official docs.

Identity Application

The changes to the Identity Application are pretty simple and only involve tweaking the settings on the MVC client found in the GetClients function of the Config class. First, change the AllowedGrantTypes from Implicit to HybridAndClientCredentials. Next, a client secret should be added.

ClientSecrets =
{
    new Secret("secret".Sha256())
}

This is, of course, a bad secret, but this is only an example. Next, add “apiApp” to the AllowedScopes and finally add AllowOfflineAccess = true. The following is the full client code.

new Client
{
    ClientId = "mvc",
    ClientName = "MVC Client",
    AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,

    ClientSecrets =
    {
        new Secret("secret".Sha256())
    },

    RedirectUris           = { "http://localhost:5002/signin-oidc" },
    PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },

    AllowedScopes =
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        "apiApp"
    },
    AllowOfflineAccess = true
};

Most of the above are straight forward. AllowedGrantTypes is what is moving to the hybrid flow which then needs a client secret to ensure everything is on the up and up. This client should be able to hit the API application so it is added to the allowed scopes. AllowOfflineAccess is less clear to me. According to the docs, it allows the requesting refresh tokens for long-lived API access. This would take some more digging before production to ensure authorization isn’t too long lived.

Client Application

Changes to the client application were pretty minimal as well. First, in the Configure function of the Startup class, the UseOpenIdConnectAuthentication call must pass a few more items. The following is the full set up.

app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
    AuthenticationScheme = "oidc",
    SignInScheme = "Cookies",

    Authority = "http://localhost:5000",
    RequireHttpsMetadata = false,

    ClientId = "mvc",
    ClientSecret = "secret",

    ResponseType = "code id_token",
    Scope = { "apiApp", "offline_access" },

    GetClaimsFromUserInfoEndpoint = true,
    SaveTokens = true
});

ClientSecret should match what was set up for the client in the Identity Application. According to the docs setting ResponseType to code id_token means use a hybrid flow. This is another point that I would want to dig more on. Scope is requesting access to the API Application and offline access which is the matching part to the offline access set up in the Identity Application. GetClaimsFromUserInfoEndpoint tells the middleware to go to the user info endpoint to retrieve additional claims after getting an identity token.

Identity Controller

The Index action in the IdentityController ends up being much simpler than it was in the previous posts. The following is the full function.

[Authorize]
public async Task<IActionResult> Index()
{
  var accessToken = 
       await HttpContext.Authentication.GetTokenAsync("access_token");

  var client = new HttpClient();
  client.SetBearerToken(accessToken);

  var apiResponse = 
       await client.GetAsync("http://localhost:5001/api/identity");

  ViewData["apiResult"] = 
       apiResponse.IsSuccessStatusCode 
       ? await apiResponse.Content.ReadAsStringAsync() 
       : apiResponse.StatusCode.ToString();

  return View();
}

In the new version, the token can be retrieved from the HTTP context instead of using the DiscoveryClient and TokenClient like the previous version of this code did. The general idea is the same in both which is to get a token, use the token as part of a request to the API application, and finally display the response in a view.

Identity View

The last set of changes is to the Index.cshtml file in the View/Identity directory which is the view that goes with the Index action of the IdentityController. The view displays the access token, refresh token, results of the API call, and the logged in user’s claims.

@using Microsoft.AspNetCore.Authentication
@{
    ViewData["Title"] = "Identity";
}

<dt>access token</dt>
<dd>@await ViewContext.HttpContext.Authentication.GetTokenAsync("access_token")</dd>

<dt>refresh token</dt>
<dd>@await ViewContext.HttpContext.Authentication.GetTokenAsync("refresh_token")</dd>

@ViewData["apiResult"]

<h3>User claims</h3>

<dl>
    @foreach (var claim in User.Claims)
    {
        <dt>@claim.Type</dt>
        <dd>@claim.Value</dd>
    }
</dl>

<form asp-controller="Identity" asp-action="Logout" method="post">
    <button type="submit">Logout</button>
</form>

Wrapping up

Adding back API access was pretty easy and the new setup will make managing other resources pretty simple. The identity space is still pretty new to me but working through the IdentityServer quickstarts are helping get me up to a basic level of knowledge. The finished code for this post can be found here. Come back next week to convert this example to use ASP.NET Core Identity.

Identity Server: From Implicit to Hybrid Flow Read More »

Identity Server: Interactive Login using MVC

This post is a continuation of a series of posts that follow my initial looking into using IdentityServer4 in ASP.NET Core with an API and an Angular front end. The following are the related posts.

Identity Server: Introduction
Identity Server: Sample Exploration and Initial Project Setup
Identity Server: Interactive Login using MVC (this post)
Identity Server: From Implicit to Hybrid Flow
Identity Server: Using ASP.NET Core Identity
Identity Server: Using Entity Framework Core for Configuration Data
Identity Server: Usage from Angular

As before the end goal will be having authorization happen from Angular, but in the short term, the Client Application is using MVC/Razor for testing and verifications. The code as it stood before this post can be found here. If you are following along with the official docs I wrote this post while working through the Adding User Authentication with OpenID Connect quickstart.

The main point of this post is to add a way for a user to enter their username and password and get access to a page that requires authorization using the OpenID Connect protocol.

Identity Application

To enable this scenario the Identity Application will need MVC added along with some UI that will be used to handle login, permissions, and log off. First, using NuGet install the following two packages.

  • Microsoft.AspNetCore.Mvc
  • Microsoft.AspNetCore.StaticFiles

Next, in the ConfigureServices of the Startup class MVC needs to be added as a service.

services.AddMvc();

Then in the Configure function use static files and use MVC should be added after the use statement for IdentityServer.

app.UseIdentityServer();
app.UseDeveloperExceptionPage();

// Following are adds
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
UI Changes

For the type of flow being used in this sample, the Identity Application will be in control of the login, grant, log out, and related UI. This is not a small amount of thing to get set up properly. Thankfully the IdentityServer team provides a Quickstart UI for use with the in-memory items we are currently using. The files can be downloaded from the repo linked in the previous line or an easier way is to open a Powershell prompt in the same directory of the Identity application as the Startup.cs file and run the following command.

iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI/release/get.ps1'))

After the download the project will contain a Quickstart folder with the needed controllers, a Views with of course the needed views, and wwwroot will have all the related files that need to be served with the views.

Config Changes

The Config class needs to be changed to return some more in-memory information to make this new process work. The first is to add a new client for MVC to the GetClients function. The following is the full function, but it is the second Client is the new one.

public static IEnumerable<Client> GetClients()
{
    return new List<Client>
    {
        new Client
        {
            ClientId = "clientApp",
            AllowedGrantTypes = GrantTypes.ClientCredentials,

            // secret for authentication
            ClientSecrets =
            {
                new Secret("secret".Sha256())
            },

            // scopes that client has access to
            AllowedScopes = { "apiApp" }
        },

        // OpenID Connect implicit flow client (MVC)
        new Client
        {
            ClientId = "mvc",
            ClientName = "MVC Client",
            AllowedGrantTypes = GrantTypes.Implicit,

            RedirectUris = { "http://localhost:5002/signin-oidc" },
            PostLogoutRedirectUris = 
                { "http://localhost:5002/signout-callback-oidc" },

            AllowedScopes =
            {
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Profile
            }
        }
    };
}

Notice that for the OpenID Connect implicit flow there are URLs that are needed that so this flow knows how to call back into the client application. At this point, I haven’t dug into everything that is going on in the client. The ClientId, ClientName, and URLs related properties are pretty clear. I am not 100% on the AllowedGrantTypes and AllowedScopes, but at this point, I am not going to dive into on these two options.

Next, add a GetIdentityResources function matching the following. This fall in the same category as the two properties above, we are using them without fully digging into them.

public static IEnumerable<IdentityResource> GetIdentityResources()
{
    return new List<IdentityResource>
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile(),
    };
}

The last change to the Config class is to add a function to return the in-memory users.

public static List<TestUser> GetUsers()
{
    return new List<TestUser>
    {
        new TestUser
        {
            SubjectId = "1",
            Username = "alice",
            Password = "password",

            Claims = new List<Claim>
            {
                new Claim("name", "Alice"),
                new Claim("website", "https://alice.com")
            }
        },
        new TestUser
        {
            SubjectId = "2",
            Username = "bob",
            Password = "password",

            Claims = new List<Claim>
            {
                new Claim("name", "Bob"),
                new Claim("website", "https://bob.com")
            }
        }
    };
}
Startup Changes

The last change in the Identity Application is to add the new in-memory items to the IdentityServer service in the ConfigureServices function. The following is the full function.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddIdentityServer()
        .AddTemporarySigningCredential()
        .AddInMemoryIdentityResources(Config.GetIdentityResources())
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients())
        .AddTestUsers(Config.GetUsers());
}

Client Application

In order to get the client application to play well with the changes in the Identity Application, a few changes need to be made. First, the following NuGet packages need to be installed.

  • Microsoft.AspNetCore.Authentication.Cookies
  • Microsoft.AspNetCore.Authentication.OpenIdConnect

Next, in the Configure function of the Startup class, the application’s middleware pipeline needs some changes. Add the following line to turn off the JWT claim type mapping. This must be done before calling UseOpenIdConnectAuthentication.

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

Now add in the cookie authentication middleware.

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationScheme = "Cookies"
});

The last change is to add OpenID Connect authentication to the pipeline placed after the cookies middleware.

app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
    AuthenticationScheme = "oidc",
    SignInScheme = "Cookies",

    Authority = "http://localhost:5000",
    RequireHttpsMetadata = false,

    ClientId = "mvc",
    SaveTokens = true
});

Notice that the URL of the authority is the URL the Identity Application runs on as well as the client ID match the one we set up in the GetClients function of the Config class in the Identity Application.

Identity Controller

Now that the above is set up we can switch over to the IdentityController and add the Authorize attribute to the Index function.

[Authorize]
public async Task<IActionResult> Index()

This means if a user hits the index action of this controller and isn’t logged in they will be presented with the login page and after login, they will be redirected back to the above index action. That whole process is handled by the OpenId Connect Authentication middleware. The first time I tested the flow and it just worked was magical.

 

The final set of changes for this post is going to be added a way to log out. In the IdentityController add a Logout function.

public async Task Logout()
{
    await HttpContext.Authentication.SignOutAsync("Cookies");
    await HttpContext.Authentication.SignOutAsync("oidc");
}
Identity View Changes

The last change is to add a logout button to the Index.cshtml found in the Views/Identity directory. At the bottom of the page, the following was added to call the Logout action.

<form asp-controller="Identity" asp-action="Logout" method="post">
    <button type="submit">Logout</button>
</form>

Wrapping Up

I already liked the idea of IdentityServer before this post, but after playing with it with the changes above it is emphasized how nice it is. I am very happy I am going down this path instead of trying to work this all out on my own. Stay tuned as this exploration will continue in future posts.

The code in the finished state can be found here.

Update

Turns out there is a bug in the code that goes with this example. In the client application’s IdentityConroller the call to get a token is using clientApp instead of mvc for the client ID when requesting a token. With that change, the call to the API Application will fail since the MVC client doesn’t have access to the API scope. Look for next weeks post where API access will be added to the MVC client.

Identity Server: Interactive Login using MVC Read More »

Entity Framework Core with SQLite Scaffolding

This is the third in what is turning into a series of post about using SQLite with Entity Framework Core. This post is going to cover adding a migration, scaffolding a controller and related views, and a few things that are harder to do using SQLite. The following are the first two post.

Entity Framework Core with SQLite
Entity Framework Core Errors Using Add-Migration

Adding Model, DbContext, Controller, and Views

If you have any experience with Entity Framework Core or have read any of my past entries on the subject this section is going to repeat some of the same information, but I am including it so someone who is looking for a full example will have it.

Model

In the Models folder add a Contact class similar the following.

public class Contact
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string PostalCode { get; set; }
    public string Phone { get; set; }
    public string Email { get; set; }
}
DbContext

In the Data folder add a ContactsDbContext that inherits from DbContext. The following is an example that auto applies migrations to a database, if you don’t need that functionality it can be dropped out.

public sealed class ContactsDbContext : DbContext
{
    private static bool _created;

    public DbSet<Contact> Contacts { get; set; }

    public ContactsDbContext(DbContextOptions<ContactsDbContext> options)
        : base(options)
    {
        if (_created) return;
        Database.Migrate();
        _created = true;
    }
}

Now that the application has a model and a related DbContext the following can be used to add a migration that will create a Contacts in the SQLite database. Run from the Package Manager console.

Add-Migration AddContacts -Context ContactsDbContext

Add-Migration is a Powershell command to add a migration (surprise!), AddContacts is the name of the migration and -Context ContactsDbContext is an argument that lets the command know which DbConext to use. The Context is only needed if your application has more than one DbContext.

Controller and Views

With the above complete Visual Studio provides some tooling that makes it very fast to create a controller with views for listing, adding, editing, and deleting items. To begin right-click on the Controllers folder and select Add > New Scaffolded Item.

Select the MVC Controller with views, using Entity Framework option and click Add.

On the next dialog use the drop downs to select a model class and a data context class. Then verify the controller name and click add.

When the process completes the following items will have been added to your project.

Controllers
 - ContactsController.cs
Views
 - Contacts
   - Create.cshtml
   - Delete.cshtml
   - Details.cshtml
   - Edit.cshtml
   - Index.cshtml
Add to nav bar

To add a link to the new section of the app to the nav bar open the _Layout.cshtml in the Views/Shared/ directory. The following is the section of the file that needs to be changed to add an item to the nav bar.

<ul class="nav navbar-nav">
    <li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>
    <li><a asp-area="" asp-controller="Contacts" asp-action="Index">Contacts</a></li>
    <li><a asp-area="" asp-controller="Home" asp-action="About">About</a></li>
    <li><a asp-area="" asp-controller="Home" asp-action="Contact">Contact</a></li>
</ul>

Specifically, the following line was added to provide access to the contact list page.

<li><a asp-area="" asp-controller="Contacts" asp-action="Index">Contacts</a></li>

Wrapping up

With the above, the application will be runnable. The code for this post can be found here. The next post in this series will cover the limitations of migrations when using SQLite with Entity Framework Core.

 

Entity Framework Core with SQLite Scaffolding Read More »

Add Aurelia to an ASP.NET Core Project

In this post I am going to add a new project to the my existing ASP.NET Core Basics solution which can be found in this repository. The new project will be MVC 6  to which I will add in Aurelia. With both ASP.NET and Aurelia now being at RTM I thought this would be a good time to cover getting a new project setup.

Over time the ASP.NET Core Basics repo used in this post is going to be replacing my ASP.NET Core SPAs repo based on some feedback that having Aurelia and Angular 2 in the same project made it harder to see how each individual framework is setup.

Part 1 – Add Aurelia to an ASP.NET Core Project (This Post)
Part 2 – Aurelia with an ASP.NET Core API
Part 3 – Aurelia with an ASP.NET Core API: Modeling and Displaying Data in Aurelia
Part 4 – Aurelia with ASP.NET Core: Host Aurelia from a Controller
Github repo with the code for all of the parts with a release to go with each post

 Adding a new project to an existing solution

To add a new project to the existing solution right click on the solution and then click Add > New Project.

AddProjectExistingSolution

On the Add New Project dialog select ASP.NET Core Web Application (.NET Core), enter a name and then click OK.

AddNewProjectDialog

On the New ASP.NET Core Web Application (.NET Core) dialog select Web Application. This application doesn’t need authentication so leave it set to No Authentication. Finally click OK.

NewASPNetCoreWebApplication

After a few seconds the project creation will complete and the solution will contain two projects. The existing Contacts project that contains both a razor/normal implementation of a contacts list as well as an API implementation. The second project is the newly created Aurelia project.

SolutionWithTwoProjects

Changing the startup project

Notice in the screenshot above that the Contacts project is in bold. This means that the Contacts project is set as the startup project and it will be the project that starts when the application is run (F5 or Cntrl + F5). In this post we will just be working with the Aurelia project so we need to make it the startup project. To do this right click the Aurelia project and select Set as StartUp Project.

SetAsStartupProject

Now if you hit F5 the Aurelia project will run. Visual Studio provides a lot of flexibility around which projects start up. You can select a single, have which ever project you have to have select, or even multiple projects.  In a later post we will need both projects to start up and I will cover that when we have the need.

Install the Aurelia CLI

Make sure you have a minimum of NodeJs 4.x or above installed. If you need the installer it can be found here. After the install is complete open a command prompt and run the following command to install the Aurelia CLI.

npm install aurelia-cli -g

Add Aurelia to existing ASP.NET Core Project

In a command prompt navigate to the folder that contains the xproj file for the ASP.NET Core project created above. Now the Aurelia CLI can be used to setup a new Aurelia project at the current location using the following command.

au new --here

There will be a series of prompts the first of which is the selection of which platform to use. Select the option for ASP.NET Core (option 2). I used the defaults for most of the remaining prompts. The exception was for unit testing which I selected no on just to keep the project simpler not because I think testing is a bad idea.

When the Aurelia CLI finishes its file creation and dependency restore your project will contain the highlighted new files and folders.

auaddedfiels

Notice that I have a warning on Dependencies that something is not installed. There is a quirky issue with Visual Studio that Scott Hanselman has blogged about here. He goes in to a good bit of detail about what is going on as well as suggesting a work around. It has to do with npm and not being about to restore an optional package that isn’t meant for Windows machines.

gulp

The Aurelia CLI creates a set of tasks to help with building, transpiling the Aurelia part of the applications. I wrote a couple of posts over the couple few weeks dealing with converting a project to use glup as well as how to get gulp working with ES 2015.

I am going to cover the abbreviated version of those two post here. Add a new file called gulpfile.babel.js in the root of the project, where your project.json is located. The Aurelia CLI added all the needed items in the devDependencies section of package.json.

gulp no go

At this point I attempted to include the tasks under aurelia_project/tasks using require(‘require-dir’)(‘aurelia_project/tasks’);. This failed completely. I couldn’t get any of the items in the tasks folder to show up. I am not sure why this didn’t work. My best guess is that the tasks in the tasks folder are exporting gulp.series and not gulp.task. I just don’t know enough about gulp at this point to now how to fix/work around this or if what I am trying to do is just not the right way it should be used.

The gulp work around

I spent more time that I would have like working on getting gulp to pick up the items in the tasks folder, but I don’t want to have to run a CLI command every time I do a build to make sure all the Aurelia related files are up to date. As a work around I decided to add a gulp task to invoke the CLI command for me.

To start open package.json  and add the following to the devDependencies section which allows shell commands to be run from gulp.

"gulp-shell" :  "0.3.0"

Next in gulpfile.babel.js added the proper imports and created tasks for the CLI commands I wanted to run. In the case I am just showing the build command.

import gulp from 'gulp';
import shell from 'gulp-shell';

gulp.task('bulid', shell.task(['au build']));

Using the Task Runner Explorer this task can now be set to run after a build of the MVC project.

treafterbuild

This accomplishes what I wanted, but it feels like a hack. If anyone knows a better way please let me know.

It’s Alive!

At this point if you run the application it will go to the normal default home page that gets created by the Visual Studio template. For me that address is http://localhost:37472/. From there if you add index.html, the full address is http://localhost:37472/index.html, you will be invoking the Aurelia application.

At this point all you will see is “Hello World!”. Not that impressive I know, but it is a starting point that we will build on in future posts.

The associated code can be found here.

Add Aurelia to an ASP.NET Core Project Read More »

ASP.NET Core Basics: Project Creation

With ASP.NET Core released it seems like a good time to do a series of posts on the basics of this new platform starting with getting a new project up and running.  In this post I am going to walk through installation of ASP.NET Core and then move to project creation. This project will end up being a basic contact list application although there will not be much specific to that end goal in this post.

Installation

All the software needed for this post can be found at http://dot.net. I will be using Visual Studio 2015 in my examples and if you don’t have it installed already it can be downloaded using the Download Visual Studio 2015 button on the right of the above page or by clicking here. If you already have Visual Studio 2015 installed please ensure you have installed Update 3.

The next bit of software you will need is found by clicking the Download .NET Core 1.0 or clicking here. This page has a good write up getting started with .NET Core, but all you need is the install for .NET Core 1.0 for Visual Studio which can be found here.

To verify that .NET Core is installed open a command prompt and run dotnet –version which will print the current version of .NET Core you have installed. As of this writing this my version is 1.0.0-preview2-003121. While .NET Core its self has been officially released the tooling is still in preview which is why the version contains preview2-003121.

Project Creation

Launch Visual Studio and from the File > New menu select Project…

NewProject

This will load the New Project dialog. On the left side under Templates > Visual C# > .NET Core select ASP.NET Core Web Application (.Net Core) or you can use the search box in the upper right of the screen to search for “ASP.NET Core Web Application (.Net Core)”.

NewProjectDialogContacts

Next on the New ASP.NET Core Web Application dialog select Web Application. This option creates an application with example MVC Views (razor) and an example controller. Notice the note that this template can be used for RESTful HTTP services as well. Next click the Change Authentication button.

NewAspNetCoreWebApplication

For this example I am going to use Individual User Accounts. This option adds UI, models, controllers, etc. to allow registration and management of user accounts. Since the option needs a database to store account information it include Entity Framework Core. Click OK on the Change Authentication dialog and then click ON new web application dialog.

ChangeAuthentication

Project Overview

After the creation process is finished got to the Solution Explorer window and you will see a set up similar to the following.

SolutionExplorer

I am going to point out a few of the files and folder that are part of a newly created application. Fist is wwwroot which is where static files will be severed as long you are using the static files middleware.  This is where images, CSS and JavaScript should go.

appsettings.json is where you will find connection strings and logging settings by default. Your own settings can be added here as well.

project.json is where you will find all your project’s dependencies (using the NuGet UI or Package Manager console both write to this file), tools, frameworks, build options, runtime options, publish options and scripts are all defined. Thankfully this file has good support for intellisense if you decide to edit it manually. This file covers a lot, but for this introduction I am going to avoid digging into the specifics.

Startup.cs is the last file I want to call out. Its generated contents are fine for this example project, but it has a couple of functions you should be aware of. First ASP.NET Core comes with dependency injection built in and the ConfigureServices function is where items are registered with the built in container. The second function is Configure and this is the function where the HTTP request pipeline for your application is configured using various middleware.

Wrapping Up

At this point you have a web application that can be run (press F5 to run with the debugger attached or Ctrl + F5 to run without the debugger). With no code changes you now have a web application that has basic navigation, controllers, views and authentication.

With this series of post things are being kept intentionally short and focused on one or two main topics. Next week will build on this basic project by adding in the ability to manage contacts, using a new controller and associated razor view which will be persisted to a database using Entity Framework Core.

The code that goes with this post can be found in this GitHub repository.

 

ASP.NET Core Basics: Project Creation Read More »

Enable Scaffolding without Entity Framework in ASP.NET Core

Background

While working on a MVC 6 application backed by a web API application which resides in a separate project I found myself wanting a quick way to use a model as a base to generate a controller and associated CRUD razor views. If a project is using entity framework this is simple to accomplish, but since my project is using web API calls instead of entity framework scaffolding is unavailable. Instead of manually creating all the needed parts I decided to try and enable the scaffolding bits that are available in projects using entity framework.

Getting Started

The first step I took was to look at the project.json from an application using entity framework. This helped me identity the dependencies and tools that might be needed for the scaffolding process. The following are the new items from the dependencies section.

"Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
  "version": "1.0.0-preview2-final",
  "type": "build"
},
"Microsoft.VisualStudio.Web.CodeGenerators.Mvc": {
  "version": "1.0.0-preview2-final",
  "type": "build"
}

And the new items from the tools section (spoiler Microsoft.VisualStudio.Web.CodeGenerators.Mvc isn’t actually needed).

"Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
  "version": "1.0.0-preview2-final",
  "imports": [
    "portable-net45+win8"
  ]
},
"Microsoft.VisualStudio.Web.CodeGenerators.Mvc": {
  "version": "1.0.0-preview2-final",
  "type": "build"
}

Failed dotnet restore

With the above changes dotnet restore fails with this error:

Package Microsoft.Composition 1.0.27 is not compatible with netcoreapp1.0 (.NETCoreApp,Version=v1.0). Package Microsoft.Composition 1.0.27 supports: portable-net45+win8+wp8+wpa81 (.NETPortable,Version=v0.0,Profile=Profile259)
One or more packages are incompatible with .NETCoreApp,Version=v1.0.

I double checked that the entity framework project was using the same versions. It was indeed using the same versions, but its restore worked fine.

The fix

This is a known issue which can be found on github here. The issues is caused by Microsoft.VisualStudio.Web.CodeGenerators.Mvc in the tools sections which I finally found out is not actually needed to enable scaffolding. To be clear the only change needed in the tools section the addition of the following and Microsoft.VisualStudio.Web.CodeGenerators.Mvc is not needed.

"Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
  "version": "1.0.0-preview2-final",
  "imports": [
    "portable-net45+win8"
  ]
}

Scaffolding

Right click the Controllers folder and select Add > Controllers… (this is the menu enabled by the changes above).

AddController

The above launches the Add Scaffold dialog. Here select “MVC Controller with views, using Entity Framework”. This is the only built in option that creates views. Since this project isn’t using entity framework it will require a little clean up, but it is still faster than creating the views manually.

AddScaffold

After clicking add you will see the following screen. Select the model class to be used during the scaffold. For the data context class click the plus (+) button and enter a name (don’t spend any time on the name the resulting file will be deleted in the next step. Finally enter a controller name and click Add.

AddScaffoldController

Cleaning up the scaffold

In the models folder delete the data context class that was created, TestContext in this case. The removal of the TestContext will cause errors in the TestController which can all be removed (the constructor and private field used to hold the context) and replaced with calls to whatever back end system you happen to be using in your application. Finally in the Startup class remove the last reference to the TestContext.

Wrapping up

That is all the changes that were required other than adding a reference to the test index page to the home page of the application. Not sure this is the most efficient method ever, but for me it is easyer than creating the razor pages myself manually. In the future I may do some digging and determine what is required to create my own scaffolding option.

Enable Scaffolding without Entity Framework in ASP.NET Core Read More »