CSS using Bootstrap – Adding Users and Role Authentication to existing Web Forms project

More wrestling going on here than with a WWE title fight, eh?

The project I’m working on is starting to lean away from Bootstrap. It is a GREAT advantage to use this as a framework – especially in terms of responsive design, and not having to muck around with CSS. Bootpress to roll your own themes.

I do love using Page Inspector – a new feature in Visual Studio 2013. Here below I can select different parts of the page, and see the CSS its using – a huge advantage in finding out why the dang CSS is being overridden in display!

 

web forms bootstrap

 

Adding Users and Role Authentication to my Project

Now however I want to move on and start adding users and roles to my budding web app. Microsoft’s made some significant changes to its model – and we’re using Owin now as a pattern.

First right click on the project, select Manage NuGet Packages, and make sure that you have Microsoft.ASP.NET Identity provider set up. Below, the green checkmark tells me all is golden. (Oh NuGet, how did we ever survive without you? M-wah!!!)

web forms bootstrap

The following tables are created – all empty initially. There’s creation scripts for this in the Appendix.

web forms bootstrap

Go into the database provider and seed your table with the following roles:

web forms bootstrap

 

And then fill in the AspNetUserRoles tables with integers that bind together your User and your Role tables.

The best shortcut I found was – since the page had been stomped on pretty good and a lot of key references were missing – was creating a new ASP.NET website with webforms – and copying over to your project the Login, Manage, OpenAuthProvider user control, Register.aspx.

 

web forms bootstrap

 

Now reorganize your site so its neatly divided into folders. I like dropping all the webforms I want screened off to unauthorized access in a folder called, wait for it, Forms. (So creative! Somebody buy me a black beret!) In most of these forms you’ll have a web.config fragment like the following:

<?xml
version=“1.0“?>

<configuration>

 

<system.web>

<authorization>

<deny
users=“?“/>

</authorization>

</system.web>

 

</configuration>

 

 

Since this project uses both MVC and webforms, I went into the Controllers and opened up each class and set the following:

namespace PerceptiveCS.Controllers

{

[Authorize]
public
class
BillOfMaterialController : Controller

That [Authorize] attribute is key – it tells me only authorized users should be viewing that site.

Next in the Models folder add a class called IdentityModel, and fill it with the code in the Appendix.

In Global.asax add the following fragment so we’re not getting such an ugly error message when Unauthorized:
//need to redirect to login page if the user is unauthorized
protected
void Application_EndRequest(Object sender, EventArgs e)

{
if (HttpContext.Current.Response.Status.StartsWith(“401″))

{
HttpContext.Current.Response.ClearContent();

Response.Redirect(“~/Account/Login.aspx”);

}

}

 

Make sure your Site.Master master page has the correct register/login fields like this – with the events added (esp the Unnamed_LoggingOut) – see the sample Site.Master file that ASP.NET creates for an example of this.

Make sure you have a Startup.cs class created and registered – again using the default logic as a setup.

Last, in your web.config on the root, add the following so people can at least view your default web page – and log in as well:

<system.web>

<authorization>

<allow
users=“*“/>

</authorization>

Notes and Findings

This posting has a great writeup on enabling database migrations with seeding data. It’s not necessary for my purposes – but it’s good to know it’s there.

http://www.codeproject.com/Articles/674760/Code-First-Migration-and-Extending-Identity-Accoun

This article has a great writeup on minifying the pages and code you’ll need to get going with the AspNet.IdentityService class: here

Appendix – Database Creation Scripts

Run the following script to create these locally on your SQL server:

CREATE
TABLE
[dbo].[__MigrationHistory]
(
[MigrationId]
NVARCHAR (150)
NOT
NULL,
[ContextKey]
NVARCHAR (300)
NOT
NULL,
[Model]
VARBINARY (MAX)
NOT
NULL,
[ProductVersion]
NVARCHAR (32)
NOT
NULL,
CONSTRAINT
[PK_dbo.__MigrationHistory]
PRIMARY
KEY
CLUSTERED ([MigrationId]
ASC,
[ContextKey] ASC)

);

CREATE
TABLE
[dbo].[AspNetRoles]
(
[Id]
NVARCHAR (128)
NOT
NULL,
[Name]
NVARCHAR (MAX)
NOT
NULL,
CONSTRAINT
[PK_dbo.AspNetRoles]
PRIMARY
KEY
CLUSTERED ([Id]
ASC)

);

 

CREATE
TABLE
[dbo].[AspNetUsers]
(
[Id]
NVARCHAR (128)
NOT
NULL,
[UserName]
NVARCHAR (MAX)
NULL,
[PasswordHash]
NVARCHAR (MAX)
NULL,
[SecurityStamp]
NVARCHAR (MAX)
NULL,
[Discriminator]
NVARCHAR (128)
NOT
NULL,
CONSTRAINT
[PK_dbo.AspNetUsers]
PRIMARY
KEY
CLUSTERED ([Id]
ASC)

);

 

(note above could also have our FirstName, etc values.)

 

CREATE
TABLE
[dbo].[AspNetUserRoles]
(
[UserId]
NVARCHAR (128)
NOT
NULL,
[RoleId]
NVARCHAR (128)
NOT
NULL,
CONSTRAINT
[PK_dbo.AspNetUserRoles]
PRIMARY
KEY
CLUSTERED ([UserId]
ASC,
[RoleId] ASC),
CONSTRAINT
[FK_dbo.AspNetUserRoles_dbo.AspNetRoles_RoleId]
FOREIGN
KEY ([RoleId])
REFERENCES [dbo].[AspNetRoles]
([Id])
ON
DELETE
CASCADE,
CONSTRAINT
[FK_dbo.AspNetUserRoles_dbo.AspNetUsers_UserId]
FOREIGN
KEY ([UserId])
REFERENCES [dbo].[AspNetUsers]
([Id])
ON
DELETE
CASCADE

);

 

GO

CREATE
NONCLUSTERED
INDEX
[IX_RoleId]
ON
[dbo].[AspNetUserRoles]([RoleId]
ASC);

 

GO

CREATE
NONCLUSTERED
INDEX
[IX_UserId]
ON
[dbo].[AspNetUserRoles]([UserId]
ASC);

 

 

 

CREATE
TABLE
[dbo].[AspNetUserLogins]
(
[UserId]
NVARCHAR (128)
NOT
NULL,
[LoginProvider]
NVARCHAR (128)
NOT
NULL,
[ProviderKey]
NVARCHAR (128)
NOT
NULL,
CONSTRAINT
[PK_dbo.AspNetUserLogins]
PRIMARY
KEY
CLUSTERED ([UserId]
ASC,
[LoginProvider] ASC,
[ProviderKey] ASC),
CONSTRAINT
[FK_dbo.AspNetUserLogins_dbo.AspNetUsers_UserId]
FOREIGN
KEY ([UserId])
REFERENCES [dbo].[AspNetUsers]
([Id])
ON
DELETE
CASCADE

);

 

GO

CREATE
NONCLUSTERED
INDEX
[IX_UserId]
ON
[dbo].[AspNetUserLogins]([UserId]
ASC);

 

CREATE
TABLE
[dbo].[AspNetUserClaims]
(
[Id]
INT
IDENTITY (1, 1)
NOT
NULL,
[ClaimType]
NVARCHAR (MAX)
NULL,
[ClaimValue]
NVARCHAR (MAX)
NULL,
[User_Id]
NVARCHAR (128)
NOT
NULL,
CONSTRAINT
[PK_dbo.AspNetUserClaims]
PRIMARY
KEY
CLUSTERED ([Id]
ASC),
CONSTRAINT
[FK_dbo.AspNetUserClaims_dbo.AspNetUsers_User_Id]
FOREIGN
KEY ([User_Id])
REFERENCES [dbo].[AspNetUsers]
([Id])
ON
DELETE
CASCADE

);

 

GO

CREATE
NONCLUSTERED
INDEX
[IX_User_Id]
ON
[dbo].[AspNetUserClaims]([User_Id]
ASC);

/Models/IdentityModel.cs

using Microsoft.AspNet.Identity;

using Microsoft.AspNet.Identity.EntityFramework;

using Microsoft.Owin.Security;

using System.Web;

using System;

using PerceptiveCS.Models;

// note – this is required below

using System.ComponentModel.DataAnnotations;

namespace PerceptiveCS.Models

{
// You can add User data for the user by adding more properties to your User class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public
class
ApplicationUser : IdentityUser

{
//public class ApplicationUser : IdentityUser { public DateTime BirthDate { get; set; } }

[Required]
public
string FirstName { get; set; }

 

[Required]
public
string LastName { get; set; }

 

[Required]
public
string Email { get; set; }

 

}
public
class
ApplicationDbContext : IdentityDbContext<ApplicationUser>

{
public ApplicationDbContext()

: base(“DefaultConnection”) //was Entities

{

}

}

 

#region Helpers
public
class
UserManager : UserManager<ApplicationUser>

{
public UserManager()

: base(new
UserStore<ApplicationUser>(new
ApplicationDbContext()))

{

}

}

}

 

namespace PerceptiveCS.Models

{
public
static
class
IdentityHelper

{
// Used for XSRF when linking external logins
public
const
string XsrfKey = “XsrfId”;
public
static
void SignIn(UserManager manager, ApplicationUser user, bool isPersistent)

{
IAuthenticationManager authenticationManager =HttpContext.Current.GetOwinContext().Authentication;

authenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = manager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);

authenticationManager.SignIn(new
AuthenticationProperties() { IsPersistent = isPersistent }, identity);

}
public
const
string ProviderNameKey = “providerName”;
public
static
string GetProviderNameFromRequest(HttpRequest request)

{
return request[ProviderNameKey];

}
public
static
string GetExternalLoginRedirectUrl(string accountProvider)

{
return
“/Account/RegisterExternalLogin?” + ProviderNameKey + “=” + accountProvider;

}
private
static
bool IsLocalUrl(string url)

{
return !string.IsNullOrEmpty(url) && ((url[0] == ‘/’ && (url.Length == 1 || (url[1] != ‘/’&& url[1] != ‘\’))) || (url.Length > 1 && url[0] == ‘~’ && url[1] == ‘/’));

}
public
static
void RedirectToReturnUrl(string returnUrl, HttpResponse response)

{
if (!String.IsNullOrEmpty(returnUrl) && IsLocalUrl(returnUrl))

{

response.Redirect(returnUrl);

}
else

{

response.Redirect(“~/”);

}

}

}

#endregion

}