Git Product home page Git Product logo

aspnetcore.identity.documentdb's Introduction

AspNetCore.Identity.DocumentDb

AspNetCore.Identity.DocumentDb is a storage provider for ASP.NET Core Identity that allows you to use Azure DocumentDB as it's data store instead of the default SQL Server store. It supports all features of Identity, including full role support and external authentication services.

Framework support

  • .NET Standard 1.6
  • .NET Standard 2.0
  • .NET Framework 4.6+

Add AspNetCore.Identity.DocumentDb to your project with NuGet

Run the following command in Package Manager Console:

Install-Package CodeKoenig.AspNetCore.Identity.DocumentDb

Supported Identity features

  • User Store:
    • Users
    • Claims
    • External Authentication (Logins)
    • Two-Factor-Authentication
    • Roles
    • Passwords
    • Security Stamps
    • Phone Numbers
    • Email
    • Lockout
  • Role Store:
    • Roles
    • Role-based Claims

Quickstart in ASP.NET MVC Core

AspNetCore.Identity.DocumentDb works just like the default SQL Server storage provider:

  • When registering services in ConfigureServices() in startup.cs, you first need to register your IDocumentClient instance that also AspNetCore.Identity.DocumentDb will resolve to access your DocumentDb database.
  • Next, register ASP.NET Identity by calling services.AddIdentity<DocumentDbIdentityUser, DocumentDbIdentityRole>() as you would with the SQL Server provider, just make sure you specify DocumentDbIdentityUser and DocumentDbIdentityRole as the generic type parameters to use with AspNetIdentity.
  • Finally, the actual storage provider can be registered with .AddDocumentDbStores()- be sure to configure the options for the store and specify at least the Database and UserStoreDocumentCollection to specify which database and document collection AspNetCore.Identity.DocumentDb should use to store data.
public void ConfigureServices(IServiceCollection services)
{
    // Add DocumentDb client singleton instance (it's recommended to use a singleton instance for it)
    services.AddSingleton<IDocumentClient>(new DocumentClient("https://localhost:8081/", "YourAuthorizationKey");

    // Add framework services.
    services.AddIdentity<DocumentDbIdentityUser, DocumentDbIdentityRole>()
        .AddDocumentDbStores(options =>
        {
            options.Database = "YourDocumentDbDatabase";
            options.UserStoreDocumentCollection = "YourDocumentDbCollection";
        });

    // Further service configurations ...
}

Important: AspNetCore.Identity.DocumentDb won't create any database or document collection in your DocumentDB. You have to take care that the database and any document collection that you want to use with it already exists.

For a complete working sample, look at the sample project in the /samples folder in this repository.

A deeper look

Storing roles

AspNetCore.Identity.DocumentDB supports roles. If you do not specify a separate collection for the role store, AspNetCore.Identity.DocumentDB will store roles in the collection that is already used for users. This is fully supported.

To specify a separate collection as the role store, pass the name of this collection in the DocumentDbOptions:

services.AddIdentity<DocumentDbIdentityUser, DocumentDbIdentityRole>()
    .AddDocumentDbStores(options =>
    {
        options.Database = "YourDocumentDbDatabase";
        options.UserStoreDocumentCollection = "YourUsersDocumentDbCollection";
        options.RoleStoreDocumentCollection = "YourRolesDocumentCollection";
    })

As with the user store collection and database, also the role collection won't be created by AspNetCore.Identity.DocumentDB if it doesn't exist. Make sure the collection is created beforehand.

Storing users and/or roles together with other documents in the same collection

As well as you can store users and roles in the same collection, it is also supported to store users and roles together with any other document. To be able to distinct users and roles from other documents, AspNetCore.Identity.DocumentDB stores the type name of the user and role class with the document in the documentType property.

Automatic partitioning

AspNetCore.Identity.DocumentDB does currently not support automatic partitioning in DocumentDB. Currently you can store users and roles only in a single partition (or in two separate partitions for users and roles).

Support for automatic partitioning is planned for a future release.

Indexing

As you need to create the document collections to store users and roles yourself, you are also responsible for setting up indexes in those document collections. If you go with the default index everything approach, you're good. If you want to use a more granular indexing approach to save storage and reduce RU cost on writing new documents, here's a recommendation which properties should be indexed for best possible read performance:

  • User documents:
    • userName
    • normalizedUserName
    • email
    • normalizedEmail
    • logins/
      • loginProvider
      • providerKey
    • roles/
      • roleName
      • normalizedRoleName
    • claims/
      • Type
      • Value
  • Role documents:
    • name
    • normalizedName

Custom user and role classes

You can inherit from DocumentDbIdentityUser as well as from DocumentDbIdentityRole if you want to extend those classes. Any additional properties that you provide will be stored in (and also retrieved from) DocumentDB.

Restrictions on the ID of a document

There are no restrictions. You can use whatever you see fit. If you don't set an ID for your user or role document before you store it for the first time, AspNetCore.Identity.DocumentDB will generate a GUID for the ID automatically, though.

Tests

This project utilizes a mix of unit and integration tests implemented in xUnit. Integration tests need a running test instance of DocumentDb to create a temporary test database - it is recommended to use the local emulator for this, but a test instance in Azure will also work fine.

aspnetcore.identity.documentdb's People

Contributors

caleblanchard avatar carl-hugo avatar codekoenig avatar joaomatossilva avatar louislewis2 avatar misinformeddna avatar projecteon avatar richmercer avatar thedarkcode avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

aspnetcore.identity.documentdb's Issues

Claims cannot be deserialized

If I create a user without claims it can log in without any errors.
If I add some claims to the user upon registration then the user cannot log in.

The error is thrown when the DocumentDbUserStore FindByNameAsync or FindByEmailAsync actions try to read the user data.

The problem is that the Document DB SDK does not respect the JsonConver.DefaultSettings. You wrote in a comment that this is a workaround but it is not the case.

 // TODO: Until DocumentDB SDK exposes it's JSON.NET settings, we need to hijack the global settings to serialize claims
JsonConvert.DefaultSettings = () =>
{
    return new JsonSerializerSettings()
    {
        Converters = new List<JsonConverter>() { new JsonClaimConverter(), new JsonClaimsPrincipalConverter(), new JsonClaimsIdentityConverter() }
    };
};

I forked the repository and I get the following error message:

{Newtonsoft.Json.JsonSerializationException: Unable to find a constructor to use for type System.Security.Claims.Claim. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'claims[0].Type', line 1, position 503.
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject(JsonReader reader, JsonObjectContract objectContract, JsonProperty containerMember, JsonProperty containerProperty, String id, Boolean& createdFromNonDefaultCreator)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at Newtonsoft.Json.Linq.JToken.ToObject(Type objectType, JsonSerializer jsonSerializer)
   at Microsoft.Azure.Documents.QueryResult.Convert(Type type)
   at Microsoft.Azure.Documents.QueryResult.AsType[T]()
   at Microsoft.Azure.Documents.Client.FeedResponseBinder.Convert[T](FeedResponse`1 dynamicFeed)
   at Microsoft.Azure.Documents.Linq.DocumentQuery`1.<GetEnumerator>d__31.MoveNext()
   at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Boolean& found)
   at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source)
   at AspNetCore.Identity.DocumentDb.Stores.DocumentDbUserStore`2.FindByNameAsync(String normalizedUserName, CancellationToken cancellationToken)

Is it considered to change dependency to Microsoft.Azure.Cosmos?

I have notice the project uses Microsoft.Azure.DocumentDB.Core, this library is deprecated and I understand is going to not be supported after August 31st, 2022.

My question is:
Is there a consideration to use Microsoft.Azure.Cosmos instead of DocumentDB?

remove claim does not work

hi,

the code below does not remove my claim from Cosmo db user object.

await _userManager.RemoveClaimAsync(user, new Claim(ClaimTypes.Role, role));

is there a bug or I am doing something wrong

ObjectDisposedException

Hi!

I am getting an ObjectDisposedException after a couple of operations.

Any ideas why?

StackTrace:

Exception has occurred: CLR/System.ObjectDisposedException
Exception thrown: 'System.ObjectDisposedException' in Microsoft.Extensions.Identity.Core.dll: 'Cannot access a disposed object.'
   at Microsoft.AspNetCore.Identity.UserManager`1.ThrowIfDisposed()
   at Microsoft.AspNetCore.Identity.UserManager`1.GetUsersInRoleAsync(String roleName)
   at Happenings.Web.Startup.<InitializeUsers>d__8.MoveNext() in 

Georgios

TypeLoadException: Method 'NormalizeName' in type 'LookupNormalizer' does not have an implementation

Just created new empty ASP.NET Core 3.1 project, added CodeKoenig.AspNetCore.Identity.DocumentDb, and facing this exception:

System.TypeLoadException: Method 'NormalizeName' in type 'AspNetCore.Identity.DocumentDb.LookupNormalizer' from assembly 'CodeKoenig.AspNetCore.Identity.DocumentDb, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.\r\n at AspNetCore.Identity.DocumentDb.IdentityDocumentDbBuilderExtensions.AddDocumentDbStores(IdentityBuilder builder, Action`1 setupAction)\r\n at CosmosIdentity.Startup.ConfigureServices(IServiceCollection services)

The whole project file:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="CodeKoenig.AspNetCore.Identity.DocumentDb" Version="2.0.0" />
  </ItemGroup>

</Project>

This is the only change I made to the template code:

public void ConfigureServices(IServiceCollection services) {
    services.AddSingleton<IDocumentClient>(new DocumentClient(
        serviceEndpoint: new Uri("https://localhost:8081/"),
        authKeyOrResourceToken: "LOCAL"));

    services.AddIdentity<DocumentDbIdentityUser, DocumentDbIdentityRole>()
        .AddDocumentDbStores(options => {
            options.Database = "CosmosIdentity";
            options.UserStoreDocumentCollection = "AspNetIdentity";
        })
        .AddDefaultTokenProviders();
}

Getting exception BadRequestException: Cross partition query is required but disabled. Please set x-ms-documentdb-query-enablecrosspartition to true, specify x-ms-documentdb-partitionkey, or revise your query to avoid this exception.

I followed steps in the Readme, and when attempting to register a new user, I get this exception: BadRequestException: Cross partition query is required but disabled. Please set x-ms-documentdb-query-enablecrosspartition to true, specify x-ms-documentdb-partitionkey, or revise your query to avoid this exception.

Full Exception race is here:
Microsoft.Azure.Documents.Routing.PartitionRoutingHelper.GetProvidedPartitionKeyRanges(SqlQuerySpec querySpec, bool enableCrossPartitionQuery, bool parallelizeCrossPartitionQuery, PartitionKeyDefinition partitionKeyDefinition, QueryPartitionProvider queryPartitionProvider, string clientApiVersion, out QueryInfo queryInfo) Microsoft.Azure.Documents.Query.DefaultDocumentQueryExecutionContext+<TryGetTargetPartitionKeyRangeAsync>d__5.MoveNext() System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) System.Runtime.CompilerServices.TaskAwaiter.GetResult() Microsoft.Azure.Documents.Query.DefaultDocumentQueryExecutionContext+<ExecuteOnceAsync>d__4.MoveNext() System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) System.Runtime.CompilerServices.TaskAwaiter.GetResult() Microsoft.Azure.Documents.BackoffRetryUtility+<>c__DisplayClass1_0+<<ExecuteAsync>b__0>d.MoveNext() System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) System.Runtime.CompilerServices.TaskAwaiter.GetResult() Microsoft.Azure.Documents.BackoffRetryUtility+<ExecuteRetry>d__3.MoveNext() System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() Microsoft.Azure.Documents.BackoffRetryUtility+<ExecuteRetry>d__3.MoveNext() System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) System.Runtime.CompilerServices.TaskAwaiter.GetResult() Microsoft.Azure.Documents.BackoffRetryUtility+<ExecuteAsync>d__1.MoveNext() System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) System.Runtime.CompilerServices.TaskAwaiter.GetResult() Microsoft.Azure.Documents.Query.DocumentQueryExecutionContextBase+<ExecuteWithRetryPolicy>d__46.MoveNext() System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) System.Runtime.CompilerServices.TaskAwaiter.GetResult() Microsoft.Azure.Documents.Query.DefaultDocumentQueryExecutionContext+<ExecuteInternalAsync>d__3.MoveNext() System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) System.Runtime.CompilerServices.TaskAwaiter.GetResult() Microsoft.Azure.Documents.Query.DocumentQueryExecutionContextBase+<ExecuteNextAsync>d__35.MoveNext()

Figure out a way to support Partitioning

In Identity, sometimes all we have is a UserId or UserName only, but querying for as User with those already requires to know the according PartitionKey. A generic, fast and convenient way to get a PartitionKey from either a UserId or a UserName, when the PartitionKey can really be anything, needs to be figured out (just for example: when creating a user with a PartitionKey, store a UserId-UserName-PartitionKey mapping in DocumentDb).

Error running application after upgrade to core 2.0

Hi,

I am running into a runtime error when logging in with a newly created user through:
result = await _userManager.AddLoginAsync

The same issue is mentioned here.

Any input on how to solve this or if its more deeply related to this library?

Not able to compile samples in Visual Studio for Mac

Hello,

I can not get the project to run, as the project reference in IdentitySample.Mvc to AspNetCore.Identity.DocumentDb states: "Incompatible target framework 4.6.1".

And the other problem is that the packages are unable to be restored, at the end of the Package Console output this is the message:

Errors in /Users/daniel/Documents/Xamarin/AspNetCore.Identity.DocumentDb/src/AspNetCore.Identity.DocumentDb/AspNetCore.Identity.DocumentDb.csproj Package Microsoft.Azure.DocumentDB.Core 1.1.2 is not compatible with net46 (.NETFramework,Version=v4.6). Package Microsoft.Azure.DocumentDB.Core 1.1.2 supports: netstandard1.6 (.NETStandard,Version=v1.6) One or more packages are incompatible with .NETFramework,Version=v4.6.

Unable to log back in after creating a user, null as part of the Claims Process

Not sure if it's just me doing something wrong but i can create a user no problem (and then use that straight away). But if i log in again with that user i get:

ArgumentNullException: Value cannot be null.
Parameter name: value
System.Security.Claims.Claim..ctor(string type, string value, string valueType, string issuer, string originalIssuer, ClaimsIdentity subject, string propertyKey, string propertyValue)

As far as i can tell the actual document stored seems okay. So i'm a bit confused where this is coming from.

Any pointers?

thanks
Paul

deserializing claims

Getting an error I'm not quite understanding because the JsonClaimConverter looks right to me:

Json.JsonSerializationException: Unable to find a constructor to use for type System.Security.Claims.Claim. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'claims[0].Type'

Which makes sense since Claim does not have an empty constructor. Any help would be appreciated.

Take document db collection name as parameter

Hi,

Is it possible to configure the solution to take document db collection name as parameter? It looks like currently the solution defaults to "default" collection in a document db. If so I would like to contribute this feature to the solution.

Thanks,
Himanshu.

Missing method exception after upgrading to SDK 2.1.3

MissingMethodException: Method not found: 'System.Threading.Tasks.Task1<Microsoft.Azure.Documents.Client.ResourceResponse1<Microsoft.Azure.Documents.Document>> Microsoft.Azure.Documents.IDocumentClient.ReadDocumentAsync(System.Uri, Microsoft.Azure.Documents.Client.RequestOptions)'.
AspNetCore.Identity.DocumentDb.Tools.DocumentDbExtensions.ReadDocumentAsync(IDocumentClient client, Uri documentUri, RequestOptions requestOptions)
System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start(ref TStateMachine stateMachine)
AspNetCore.Identity.DocumentDb.Tools.DocumentDbExtensions.ReadDocumentAsync(IDocumentClient client, Uri documentUri, RequestOptions requestOptions)
AspNetCore.Identity.DocumentDb.Tools.DocumentDbExtensions.ReadDocumentAsync(IDocumentClient client, Uri documentUri)
AspNetCore.Identity.DocumentDb.Stores.DocumentDbUserStore<TUser, TRole>.FindByIdAsync(string userId, CancellationToken cancellationToken)

Question - How are the document updated?

This might be a really simple question... How are the documents updated?

Looking through the code, I can see where a document might be created, and I can see how documents are found (based on id or name etc) - these all reference the DocumentClient.

However, I can't see anything that performs the write to update a document. Am I being really stupid? Where does this happen?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.