DPS907 notes – Thu Nov 30 2017

Make progress on user account management, and custom claims management.

 

Test today

The weekly test will be done sometime during the class timeslot.

Also, keep in mind that the test questions will cover all of this weeks topics, from Tuesday and from today.

 

Code examples reminder

The GitHub code example repository for this week, has assets that support this topic.

 

Claims management – getting started

To get started, let’s assume that we agree with the design ideas discussed in the Tuesday class notes. There are some remaining questions:

What values are allowed for the role claims? Anything? In any format? (UPPER CASE, lower, MiXeD, with typoes typos?)

Or, a reasonably-limited number of values, with an agreed-upon way to manage the list of values? Yeah, probably this.

Next, what types and values are allowed for custom claims? We are probably comfortable starting with OU and Task claim types, but we have the same issue with their values.

How can we keep the types and values consistent and error-free?

Does the claims storage infrastructure help us in any way? Maybe not. Here is the class diagram of the entities that are involved in storing user account data in an app that uses the ASP.NET Identity system (repeated from the web apps course notes from February 22, 2017, which introduced security topics). Click the image to open it full-size in a new tab/window:

identity-models

 

The red-outlined entities – AspNetUser and AspNetUserClaim – are the most important to us. A simple one-to-many association – one user can have zero or more claims.

However, there’s no “master list” of claim types or values. No restrictions. Whatever we create in code – when registering a new user account, or later – will be stored. Which is not ideal for an app.

So, maybe we should design a “master list” of allowable claims. Then, we manage the creation of claims, during the user account creation and management activities, so that we have some guidance and best practices to rely on.

 

General approach to creating the “master list”

Open the code example ManageClaims, and follow along. The code example implements many of the tasks discussed below.

Let’s create a class named “AppClaim”. We can add its source code to the existing IdentityModels.cs file (and add its DbSet<TEntity> property in the ApplicationDbContext class).

Next, in the Controllers folder, create a source code file to hold the resource models. Create “add” and “base” resource model classes.

Add an AppClaims (Web API) controller.

The Manager class will have methods for the common CRUD tasks. In addition, it will have extra logic and some extra methods to support the claims management process.

Note… Study the Manager class in the code example to learn more. It has defined many/most of the tasks, but not all are implemented yet.

The Controller class will support the common use cases (e.g. get-all, get-one, add-new, etc.). It will work only for the “user account manager” user – therefore, it must be protected by a suitable [Authorize] attribute.

Note… Study the AppClaimsController class in the code example to learn more. Similar to above, it implements some of the basics, but not everything yet.

At this point in time, we will have the basic ability to manage a “master list” of custom claims.

Now, we can and must use that “master list”, during user account registration tasks.

 

New user account registration, with claims management

In the past, you have worked with the “account” web service controller to create/register new user accounts.

That process does not change much. Open and study the ManageClaims code example as we discuss this topic.

In summary, we must validate the requested claims, by looking at the master list. If a requested claim is NOT on the master list, then the create/register attempt will fail.

The existing code in the account controller’s Register() method shows how to get started doing this. The code will loop through the requested role claims, and validate each one against the master list.

Is the method complete? NO. It needs code that will loop through the requested custom claims. We’ll probably do that as an in-class exercise.

 

User account management introduction

The other important topic for today is user account management.

At this point in time, you have some experience with creating/registering new user accounts in a web service. And above, you just learned how to validate the requested claims against a master list of allowable claims.

Does the project template include the built-in ability to manage user accounts? No.

 

Getting-started rules and information

As you know, the ASP.NET Identity system stores user account data in the database-backed persistent store.

Therefore, it’s fair to ask whether we use the traditional data context (ApplicationDbContext) to work with user accounts.

No.

Never. (Well, almost never.)

What do we use instead?

ASP.NET Identity includes a UserManager class. It is an API to the identity management functions of the ASP.NET Identity system. Read/skim the documentation, and notice that it has functionality for almost everything that we would be interested in doing:

Properties:

  • Users collection (i.e. all user accounts)

Methods:

  • Add claim, remove claim (for a user)
  • Change password *
  • Create new user account *
  • Delete a user account
  • Find a user account *
  • Get claims for a user

The methods marked with an asterisk ( * ) are used in the account controller that’s already in the template. That controller is designed for some very specific tasks, but it was not designed as a general-purpose tool for user account administrators. Instead, it was designed to help one user create/register a user account, and then manage it (e.g. change password etc.).

 

Best practice:

ALWAYS prefer and attempt to use the ASP.NET Identity API – mostly the UserManager class – to work with user accounts and claims.

There are a few tasks that the API does not expose. In those situations – if you’re careful – you can go around the API, and work with the data context. But please prefer and attempt to use the API.

 

Adding user account management to a web service

While going through this section, open and study the ManageUserAccounts code example.

We established (above) that the existing account controller was not designed as a general-purpose tool for user account management. We could add more methods to that controller, but maybe we should create a new controller, with backing methods in the Manager class.

In addition, let’s continue to use the long-established design practices – including resource models – when we’re working with the ASP.NET Identity entities (user accounts and claims).

 

Resource models

Look again at the ASP.NET Identity entity for a user account. Its class name is ApplicationUser (as seen in the IdentityModels.cs source code file).

Use Visual Studio F12 (go to definition) to explore:

  1. ApplicationUser inherits from IdentityUser
  2. IdentityUser includes a handful of useful (to us) properties, e.g. Id, Email, UserName, and a collection of Claims

The claims collection data type is IdentityUserClaim, which has useful properties, e.g. ClaimType, ClaimValue, UserId.

One aspect of the ASP.NET Identity entity design is that the IdentityUser has a to-many association with IdentityUserClaim. This makes sense, in that a user account will have a collection of zero-or-more claims.

However, the IdentityUserClaim collection (as a whole) will actually have claims that have the same ClaimType and ClaimValue, differing only in their Id and UserId. That’s by design. So, in the future, if you’re looking for “a claim” that matches a given ClaimType and ClaimValue, there may be more than one match. In other words, the collection could hold dozens (or more) claims, for different users, with (for example) a ClaimType of “role” and a value of “student”.

When we design the resource models for user account management, let’s continue to follow a best practice that we learned when working with to-many associations:

  1. Create a “base” user account resource model class
  2. Create a “with claims” class that inherits from the base class
  3. Then, create an “add” claim resource model class
  4. Finally, create a “base” class that inherits from the add class

We will NOT create an “add” resource model for a user account. The existing RegisterBindingModel (used by the account controller’s Register() method) already fulfills that purpose.

Here’s a class diagram. Click to open the image full-size in a new tab/window.

uam_vm

 

Use cases (for controller actions and manager methods)

The ManageUserAccounts code example shows how to work with some of the getting-started use cases:

  • Get all user accounts
  • Get one by (string) identifier
  • Get one by email
  • Get all by matching surname

 

Notice that we’re typically working with string identifiers. As a result, we will be using attribute-based route definitions for most of the controller methods.

Notice also that the attribute-based route definitions embed the string identifier within the URI, instead of placing it at the end of the URI. This will lower the error and “not found” rate, because users/requestors don’t have to worry about arcane web server and runtime URI processing rules.

In the Manager class, notice the UAGetAllBySurname() method. It fetches the user accounts, from the store, into memory, before querying. That is a common pattern when using the ASP.NET Identity API. If you ever attempt to do a repeating task, or a sub-query task, and receive a “data reader still open” error message, then you can solve that by using this pattern.

 

Adding on functionality

Notice that the code example has only “get” methods, but we will want to work with claims (add, change, remove) in a real-world app.

We’ll leave that work for the next assignment, but we will discuss it during today’s class/session.

 

 

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

Advertisements
%d bloggers like this: