the cloud way

View on GitHub

el camino cloud logoIdentityazuretable

This project provides a high performance cloud solution for ASP.NET Identity Core using Azure Table storage replacing the Entity Framework / MSSQL provider.

Home | Getting Started | Quick Starts | Data Migration | Technical Overviews
Download this project as a .zip file Download this project as a tar.gz file

 

Technical Overview

  ElCamino.AspNetCore.Identity.AzureTable > 1.7 <= 2.1

High Level Dependencies

AzureIdentityTableDependency2.png

ElCamino.AspNetCore.Identity.AzureTable.dll acts as the data access layer under the Microsoft.AspNetCore.Identity.dll classes UserManager and RoleManager by implementing the necessary interfaces on the UserStore and RoleStore respectively. These classes consume the IdentityCloudContext and the other Identity Model classes described below to persist the user and role information according to the business logic dictated by the respective UserManager and RoleManager classes.

UserStore and RoleStore Classes

IdentityUserStoreRoleStore2.png

IdentityCloudContext and Connections

IdentityCloudContext that enables access to the Azure Table storage service and exposes the tables.

The IdentityCloudContext default constructor will look for the IdentityConfiguration object that contains the table storage connection and other settings. The example is shown storing and loading these settings in a .json configuration file. Add the default local connection string to the Azure Storage Emulator or to an Azure Table Storage account. TablePrefix can be left blank, any text here will prefix the Azure table storage names of AspNetUser, AspNetRoles, and AspNetIndex. StorageConnectionString is the Azure table storage connection string. LocationMode can be left empty (defaults to PrimaryOnly). Other storage location modes are listed here and must be used with a RA-GRS storage account if changed from the default.

{
...
    "IdentityAzureTable": {
        "IdentityConfiguration": {
            "TablePrefix": "mvc6"
            "StorageConnectionString": "UseDevelopmentStorage=true;"
            "LocationMode": "PrimaryOnly"
        }
    }...
}

Identity Model to Azure Table Relationships

IdentityTableStorageModels.png
IdentityTableStorageModels2.png
IdentityTableStorageModels3.png
AspNetUsers
Identity Model PartitionKey RowKey Cardinality
IdentityUser Escaped(UserName) Escaped(UserName) 1 UserName : 1 IdentityUser, UserName uniquely identifies IdentityUser.
IdentityUserClaim Escaped(UserName) Hashed(ClaimType, ClaimValue) 1 IdentityUser : 0,Many IdentityUserClaim(s)
IdentityUserLogin Escaped(UserName) Escaped(LoginProvider, ProviderKey) 1 IdentityUser : 0,Many IdentityUserLogin(s)
IdentityUserRole Escaped(UserName) Escaped(RoleName) 1 IdentityUser : 0,Many IdentityUserRole(s)
IdentityUserToken Escaped(UserName) Hashed(LoginProvider, Name) 1 IdentityUser : 0,Many IdentityUserToken(s)
AspNetIndex
Identity Model Type PartitionKey RowKey Id Cardinality
IdentityUserIndex User Email Index Escaped(User Email) Escaped(UserName) Escaped(UserName) 1 Email : 1,Many IdentityUser(s) (unless the UserManager is set to enforce unique email addresses per user, then 1 Email : 1 IdentityUser)
IdentityUserIndex User Login Index Escaped(LoginProvider, ProviderKey) Escaped(LoginProvider, ProviderKey) Escaped(UserName) 1 IdentityUserLogin : 1 IdentityUser
IdentityUserIndex User Claim Index Hashed(ClaimType, ClaimValue) Escaped(UserName) Escaped(UserName) 1 IdentityUserClaim : 1,Many IdentityUser(s). Used to lookup users by claim.
IdentityUserIndex User Role Index Escaped(RoleName) Escaped(UserName) Escaped(UserName) 1 IdentityUserRole : 1,Many IdentityUser(s). Used to lookup users by role.
AspNetRoles
Identity Model PartitionKey RowKey
IdentityRole Escaped(First Char of RoleName) Escaped(RoleName)

The properties of the respective identity model class are stored as a field in the table unless marked with the ignore attribute. Inheriting and extending these classes one can add fields whenever your extended class is saved. Also, the Escaped() psydo-code is a reference to the respective methods found in the UriEncodeKeyHelper class. Hashed() psydo-code is a SHA256 hash of the inputfound in the HashKeyHelper class. The Hashed() algorithm was introduced to overcome the size limitation of the rowkey for claim values and overall is a better algorithm for setting deterministic key sizes.

Performance

The AspNetUsers table is designed to keep all of the user's data in a single table and in a table partition for each user. This results in a single query using the user's id (partition key) to get all of the data in a single query when the user's id is known. The user's id is an 'Escaped' version of the username and the traditional guid is not used. This makes the UserStore.FindByUserNameAsync() and UserStoreFindByUserNameAsync() use the same query. When the user id is not known, the AspNetIndex table is queried first either by user's email UserStore.FindByEmailAsync() or external login information UserStore.FindAsync() to get the user's id to then query by user id for the complete user information. The AspNextIndex is always queried by partition and rowkey making that query extremely fast. See above for the details of the AspNetIndex partition and rowkey strategy. In addition, Roles and Claims mapping to users are also indexed for use by GetUsersInRoleAsync() and GetUsersForClaimAsync(), respectively.

AspNetRoles table also uses a composite of the RoleName for the partition and row keys making it possible query the table by its primary key optimizing performance.

Testing

A full suite of integration tests against this assembly ElCamino.AspNetCore.Identity.AzureTable.Tests.csproj

Release Management

  • Full suite of integration tests against this assembly at 100% pass rate against the Azure Local Emulator and against a live Azure Storage account. 100% pass rate is required before release. The tests are at about 90% code coverage currently and will work to keep the up.
  • Releases available on NuGet and are strong name signed.
  • NuGet Badge

A blog for modern software development

Subscribe and get the latest blog post in your inbox.

Software as a Service

 
price steady pricing page ad
Price Steady

Customized, Hosted Subscription Pricing Pages Up Quickly With No And Low Code Options.

optic nerve ai cyborg looking into space ad
Optic Nerve AI

No code, first class import/publish with Azure, Google cloud custom vision AI services.

Open Source Projects

 
image
Project Title

Project intro lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes.

NuGet Badge   NuGet Badge

image
Project Title

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.

NuGet Badge   NuGet Badge

image
Project Title

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.

NuGet Badge

image
Project Title

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.

NuGet Badge