Giter VIP home page Giter VIP logo

aspnetcore.identity.cosmosdb's Introduction

Cosmos DB Provider for ASP.NET Core Identity

CodeQL .Net 8 Tests NuGet

This is a Cosmos DB implementation of an Identity provider for .NET 8 that uses the "EF Core Azure Cosmos DB Provider".

Upgrading from version 2.x to 8.x

When upgrading to version 8 from 2, you will need to make two changes to your project:

The old way of creating the Database Context looked like this:

public class ApplicationDbContext : CosmosIdentityDbContext<IdentityUser, IdentityRole>

The new way is like this (with 'string' added):

public class ApplicationDbContext : CosmosIdentityDbContext<IdentityUser, IdentityRole, string>

Next, in your Program.cs or Startup.cs files, change from this:

 builder.Services.AddCosmosIdentity<ApplicationDbContext, IdentityUser, IdentityRole>

To this (with 'string' added):

 builder.Services.AddCosmosIdentity<ApplicationDbContext, IdentityUser, IdentityRole, string>

Installation

Tip: This package uses seven (7) "containers." If the RU throughput for your Cosmos Account is configured at the "container" level, this can require your Cosmos DB Account to require a higher minimum RU.

Sharing Throughput

To keep costs down, consider sharing throughput at the database level as described here in the documentation. This allows you to, for example, set the RU at the database to be 1,000 RU, then have all containers within that database share those RU's.

Autoscale

Next, set the RU to "autoscale." According to documentation, "Autoscale provisioned throughput in Azure Cosmos DB allows you to scale the throughput (RU/s) of your database or container automatically and instantly." This will allow your database to scale down and up as needed, thus reducing your monthly costs further.

Nuget Package

Add the following NuGet package to your project:

PM> Install-Package AspNetCore.Identity.CosmosDb

Create an Azure Cosmos DB account - either the free, serverless or dedicated instance. For testing and development purposes it is recommended to use a free account. See documentation to help choose which type of Cosmos account is best for you.

Set your configuration settings with the connection string and database name. Below is an example of a secrets.json file:

{
  "SetupCosmosDb": "true", // Importat: Remove this after first run.
  "CosmosIdentityDbName": "YourDabatabaseName",
  "ConnectionStrings": {
    "ApplicationDbContextConnection": "THE CONNECTION STRING TO YOUR COSMOS ACCOUNT"
  }
}

Update Database Context

Modify the database context to inherit from the CosmosIdentityDbContext like this:

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

namespace AspNetCore.Identity.CosmosDb.Example.Data
{
    public class ApplicationDbContext : CosmosIdentityDbContext<IdentityUser, IdentityRole, string>
    {
        public ApplicationDbContext(DbContextOptions dbContextOptions)
          : base(dbContextOptions) { }
    }
}

Modify Program.cs or Startup.cs File

After the "secrets" have been set, the next task is to modify your project's startup file. For Asp.net 6 and higher that might be the Project.cs file. For other projects it might be your Startup.cs.

You will likely need to add these usings:

using AspNetCore.Identity.CosmosDb;
using AspNetCore.Identity.CosmosDb.Containers;
using AspNetCore.Identity.CosmosDb.Extensions;

Next, the configuration variables need to be retrieved. Add the following to your startup file:

// The Cosmos connection string
var connectionString = builder.Configuration.GetConnectionString("ApplicationDbContextConnection");

// Name of the Cosmos database to use
var cosmosIdentityDbName = builder.Configuration.GetValue<string>("CosmosIdentityDbName");

// If this is set, the Cosmos identity provider will:
// 1. Create the database if it does not already exist.
// 2. Create the required containers if they do not already exist.
// IMPORTANT: Remove this setting if after first run. It will improve startup performance.
var setupCosmosDb = builder.Configuration.GetValue<string>("SetupCosmosDb");

Add this code if you want the provider to create the database and required containers:

// If the following is set, it will create the Cosmos database and
//  required containers.
if (bool.TryParse(setupCosmosDb, out var setup) && setup)
{
    var builder1 = new DbContextOptionsBuilder<ApplicationDbContext>();
    builder1.UseCosmos(connectionString, cosmosIdentityDbName);

    using (var dbContext = new ApplicationDbContext(builder1.Options))
    {
        dbContext.Database.EnsureCreated();
    }
}

Now add the database context in your startup file like this:

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseCosmos(connectionString: connectionString, databaseName: cosmosIdentityDbName));

Follow that up with the identity provider. Here is an example:

builder.Services.AddCosmosIdentity<ApplicationDbContext, IdentityUser, IdentityRole, string>(
      options => options.SignIn.RequireConfirmedAccount = true // Always a good idea :)
    )
    .AddDefaultUI() // Use this if Identity Scaffolding is in use
    .AddDefaultTokenProviders();

Adding Google or Microsoft OAuth providers

This library works with external OAuth providers, and below is an example of how we implement this.

Begin by adding these two NuGet packages to your project:

Then add the code below to your Project.cs file.

// Example of adding OAuth Providers
// Add Google if keys are present
var googleClientId = Configuration["Authentication_Google_ClientId"];
var googleClientSecret = Configuration["Authentication_Google_ClientSecret"];

// If Google ID and secret are both found, then add the provider.
if (!string.IsNullOrEmpty(googleClientId) && !string.IsNullOrEmpty(googleClientSecret))
{
    builder.Services.AddAuthentication().AddGoogle(options =>
    {
        options.ClientId = googleClientId;
        options.ClientSecret = googleClientSecret;
    });
}

// Add Microsoft if keys are present
var microsoftClientId = Configuration["Authentication_Microsoft_ClientId"];
var microsoftClientSecret = Configuration["Authentication_Microsoft_ClientSecret"];

// If Microsoft ID and secret are both found, then add the provider.
if (!string.IsNullOrEmpty(microsoftClientId) && !string.IsNullOrEmpty(microsoftClientSecret))
{
    builder.Services.AddAuthentication().AddMicrosoftAccount(options =>
    {
        options.ClientId = microsoftClientId;
        options.ClientSecret = microsoftClientSecret;
    });
}

To learn more about external OAuth providers, please see the Microsoft documentation on this subject.

Complete Startup File Example

The above instructions showed how to modify the startup file to make use of this provider. Sometimes it is easier to see the end result rather than peicemeal. Here is an example Asp.Net 6 Project.cs file configured to work with this provider, scaffolded identity web pages, and the SendGrid email provider:

An example website is available for you to download and try.

Supported LINQ Operators User and Role Stores

Both the user and role stores now support queries via LINQ using Entity Framework. Here is an example:

var userResults = userManager.Users.Where(u => u.Email.StartsWith("bob"));
var roleResults = roleManager.Roles.Where (r => r.Name.Contains("water"));

For a list of supported LINQ operations, please see the "Supported LINQ Operations" documentation for more details.

Help Find Bugs

Find a bug? Let us know by contacting us via NuGet or submit a bug report on our GitHub issues section. Thank you in advance!

Changelog

This change log notes major changes beyond routine documentation and NuGet dependency updates.

v8.0.0.3

  • Now supports generic keys.
  • Applied patch for issue #14.

v8.0.0.1

  • Now built for .Net 8, and removed support for 6 and 7.
  • Updated NuGet packages to latest releases.

v2.1.1

  • Added support for .Net 6 and .Net 7.

v2.0.20

  • Addressing bug #9, implemented interfaces IUserAuthenticatorKeyStore and IUserTwoFactorRecoveryCodeStore to support two factor authentication. Example website updated to demonstrate capability with QR code generation.

v1.0.6

  • Introduced support for IUserLoginStore<TUser> in User Store

v1.0.5

  • Introduced support for IUserPhoneNumberStore<TUser> in User Store

v1.0.4

  • Introduced support for IUserEmailStore<TUser> in User Store

v2.0.0-alpha

  • Forked from source repository pierodetomi/efcore-identity-cosmos.
  • Refactored for .Net 6 LTS.
  • Added UserStore, RoleStore, UserManager and RoleManager unit tests.
  • Namespace changed to one more generic: AspNetCore.Identity.CosmosDb
  • Implemented IUserLockoutStore interface for UserStore

v2.0.1.0

  • Added example web project

v2.0.5.1

  • Implemented IQueryableUserStore and IQueryableRoleStore

Unit Test Instructions

To run the unit tests you will need two things: (1) A Cosmos DB Account, and (2) a connection string to that account. Here is an example of a secrets.json file created for the unit test project:

{
  "CosmosIdentityDbName" : "YOURDATABASENAME",
  "ConnectionStrings": {
    "ApplicationDbContextConnection": "AccountEndpoint=YOURCONNECTIONSTRING;"
  }
}

Choice of Cosmos DB Account Type

This implementation will work with the "Free" Cosmos DB tier. You can have one per account.

It also works the "serverless" and "provisioned" account types.

References

To learn more about Asp.Net Identity and items realted to this project, please see the following:

aspnetcore.identity.cosmosdb's People

Contributors

dependabot[bot] avatar etriebe avatar toiyabe 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

Watchers

 avatar  avatar

aspnetcore.identity.cosmosdb's Issues

How to update an IdentityUser within CosmosDB?

Discussed in #23

Originally posted by etriebe February 2, 2024
I'm trying to update objects within CosmosDB just like I've done with other objects but I get an exception when I do so. I'm only updating username but I get this error.

Microsoft.Azure.Cosmos.CosmosException
  HResult=0x80131500
  Message=Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: 2aaf5d6d-4433-4488-8f52-905849797594; Reason: (Message: {"Errors":["One of the specified inputs is invalid"]}
ActivityId: 2aaf5d6d-4433-4488-8f52-905849797594, Request URI: /apps/8ce365e5-ba15-438a-a471-07d6b564394d/services/22cbecbc-8764-4e5b-b75b-76fa9b95216b/partitions/fd5ad3bd-b31c-455f-868a-f9c2ca0c97c2/replicas/133265169497594395p/, RequestStats: Microsoft.Azure.Cosmos.Tracing.TraceData.ClientSideRequestStatisticsTraceDatum, SDK: Windows/10.0.22631 cosmos-netstandard-sdk/3.31.4);
  Source=Microsoft.Azure.Cosmos.Client
  StackTrace:
   at Microsoft.Azure.Documents.StoreResult.ToResponse(RequestChargeTracker requestChargeTracker)
   at Microsoft.Azure.Documents.ConsistencyWriter.WritePrivateAsync(DocumentServiceRequest request, TimeoutHelper timeout, Boolean forceRefresh)
   at Microsoft.Azure.Documents.BackoffRetryUtility`1.ExecuteRetryAsync[TParam,TPolicy](Func`1 callbackMethod, Func`3 callbackMethodWithParam, Func`2 callbackMethodWithPolicy, TParam param, IRetryPolicy retryPolicy, IRetryPolicy`1 retryPolicyWithArg, Func`1 inBackoffAlternateCallbackMethod, Func`2 inBackoffAlternateCallbackMethodWithPolicy, TimeSpan minBackoffForInBackoffCallback, CancellationToken cancellationToken, Action`1 preRetryCallback)
   at Microsoft.Azure.Documents.ShouldRetryResult.ThrowIfDoneTrying(ExceptionDispatchInfo capturedException)
   at Microsoft.Azure.Documents.BackoffRetryUtility`1.ExecuteRetryAsync[TParam,TPolicy](Func`1 callbackMethod, Func`3 callbackMethodWithParam, Func`2 callbackMethodWithPolicy, TParam param, IRetryPolicy retryPolicy, IRetryPolicy`1 retryPolicyWithArg, Func`1 inBackoffAlternateCallbackMethod, Func`2 inBackoffAlternateCallbackMethodWithPolicy, TimeSpan minBackoffForInBackoffCallback, CancellationToken cancellationToken, Action`1 preRetryCallback)
   at Microsoft.Azure.Documents.ConsistencyWriter.WriteAsync(DocumentServiceRequest entity, TimeoutHelper timeout, Boolean forceRefresh, CancellationToken cancellationToken)
   at Microsoft.Azure.Documents.ReplicatedResourceClient.<>c__DisplayClass31_0.<<InvokeAsync>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.Azure.Documents.RequestRetryUtility.ProcessRequestAsync[TRequest,IRetriableResponse](Func`1 executeAsync, Func`1 prepareRequest, IRequestRetryPolicy`2 policy, CancellationToken cancellationToken, Func`1 inBackoffAlternateCallbackMethod, Nullable`1 minBackoffForInBackoffCallback)
   at Microsoft.Azure.Documents.ShouldRetryResult.ThrowIfDoneTrying(ExceptionDispatchInfo capturedException)
   at Microsoft.Azure.Documents.RequestRetryUtility.ProcessRequestAsync[TRequest,IRetriableResponse](Func`1 executeAsync, Func`1 prepareRequest, IRequestRetryPolicy`2 policy, CancellationToken cancellationToken, Func`1 inBackoffAlternateCallbackMethod, Nullable`1 minBackoffForInBackoffCallback)
   at Microsoft.Azure.Documents.StoreClient.ProcessMessageAsync(DocumentServiceRequest request, CancellationToken cancellationToken, IRetryPolicy retryPolicy)
   at Microsoft.Azure.Cosmos.Handlers.TransportHandler.ProcessMessageAsync(RequestMessage request, CancellationToken cancellationToken)
   at Microsoft.Azure.Cosmos.Handlers.TransportHandler.SendAsync(RequestMessage request, CancellationToken cancellationToken)

  This exception was originally thrown at this call stack:
    [External Code]
    Pickem.Shared.Utilities.Data.IdentityDBUtils.UpdateUserAsync(Microsoft.AspNetCore.Identity.IdentityUser) in IdentityDBUtils.cs
```</div>

Support for .Net 7

.Net 7 is now in “preview” and will be released soon. An implementation is now needed for .Net 7 and 6.

Add support for latest Blazor Web template

Is your feature request related to a problem? Please describe.
There is a new template that seems incompatible with this nuget package. I have a clean solution here.

https://github.com/etriebe/BlazorWebAppClean

And here is where I'm trying to match the format for a solution based on what they had.
https://github.com/etriebe/BlazorWebAppClean/tree/etriebe/test-aspnetcore-identity-cosmosdb

And this is the error I get:

System.InvalidOperationException: 'Scheme already exists: Identity.Application'

MaxAsync does not work on IdentityUserClaim<string> and IdentityRoleClaim<string> records

Describe the bug
MaxAsync does not work on IdentityUserClaim and IdentityRoleClaim records.

To Reproduce
Steps to reproduce the behavior:
Following code produces SQL like

SELECT MAX(c["Id"]) AS m FROM root c WHERE(c["Discriminator"] = "IdentityUserClaim<string>")
'Id' field is stored as string value, therefore the MAX operation does not work as expected.
Example data set: "Id": "9", "Id": "10"

Current behavior max result is "9" instead of "10".

Expected behavior
Current behavior max result has to be "10" instead of "9".

Screenshots
image
image

Desktop (please complete the following information):

  • TargetFramework net6.0
  • Version="2.1.4"

Additional context
I tried to add claims to a user record and discovered this issue.

NotSupportedException: No IUserTwoFactorTokenProvider<TUser> named 'Default' is registered.

Describe the bug
When registering a new user, the following error occurs:

System.NotSupportedException: No IUserTwoFactorTokenProvider<TUser> named 'Default' is registered. at Microsoft.AspNetCore.Identity.UserManager 1.GenerateUserTokenAsync(TUser user, String tokenProvider, String purpose) at Microsoft.AspNetCore.Identity.UserManager 1.GenerateEmailConfirmationTokenAsync(TUser user) at AspNetCore.Identity.CosmosDb.Example.Areas.Identity.Pages.Account.RegisterModel.OnPostAsync(String returnUrl) in C:\Users\eric\source\repos\CosmosSoftware\AspNetCore.Identity.CosmosDb\AspNetCore.Identity.CosmosDb.Example\Areas\Identity\Pages\Account\Register.cshtml.cs:line 119 at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory.GenericTaskHandlerMethod.Convert[T](Object taskAsObject) at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory.GenericTaskHandlerMethod.Execute(Object receiver, Object[] arguments) at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeHandlerMethodAsync() at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeNextPageFilterAsync() at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Rethrow(PageHandlerExecutedContext context) at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeInnerFilterAsync() at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

To Reproduce
Steps to reproduce the behavior:

  1. Start with no database or containers created.
  2. Set the secrets so that the Cosmos DB provider can automatically create the database and containers.
  3. Run the website, and try to register a new user.
  4. See error.

Expected behavior
User account is created and a confirmation token is emailed to the new users

Additional context
It looks like the provider injection needs to add the default IUserTwoFactorTokenProvider.

Changing email password not working as expected on some existing accounts

Describe the bug
I run through a password change workflow for an account and it doesn't let me login with the newly created password right after. I see the database get updated but it seems as though that new password won't actually work.

To Reproduce
Steps to reproduce the behavior:

  1. Create an account (also has a google auth on the account)
  2. Try and change a password for that account (change successful)
  3. Use the new password to login
  4. See an error "Invalid login attempt."

Expected behavior
For the password change to happen.

Additional context
I've tried this with another account and the change password workflow does work but not on my primary account that is used on the mail website. Any idea how I can diagnose these failed login attempts?

How to setup external login providers such as google

Please can you suggest how to setup external login providers such as google, facebook etc. with this setup using default packages such as Microsoft.AspNetCore.Authentication.Google. As per the Microsoft documentation the configuration needs to be as below

services.AddAuthentication().AddGoogle(googleOptions =>
    {
        googleOptions.ClientId = configuration["Authentication:Google:ClientId"];
        googleOptions.ClientSecret = configuration["Authentication:Google:ClientSecret"];
    });

But the AddAuthentication in package AspNetCore.Identity.CosmosDb the extension method AddAuthentication is inside the package.

Upgrade to .net 8

Having an error that says it cannot find an implementation of Clone() in EntityFramework.Cosmos 7.0.2

Two-factor authentication Bug

Hi
I got an error on the Two-factor authentication.

after registering the user when you go to the profile page and click on Two-factor authentication you get this error :

Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.NotSupportedException: Store does not implement IUserAuthenticatorKeyStore.
at Microsoft.AspNetCore.Identity.UserManager1.GetAuthenticatorKeyStore() at Microsoft.AspNetCore.Identity.UserManager1.GetAuthenticatorKeyAsync(TUser user)
at CosmosDB_Test.Areas.Identity.Pages.Account.Manage.TwoFactorAuthenticationModel.OnGetAsync() in C:\Users\Sam\source\repos\CosmosDB_Test\CosmosDB_Test\Areas\Identity\Pages\Account\Manage\TwoFactorAuthentication.cshtml.cs:line 68

==========================================================
I use these packages:

<ItemGroup>
    <PackageReference Include="AspNetCore.Identity.CosmosDb" Version="2.0.18" />
    <PackageReference Include="AspNetCore.Identity.Services.SendGrid" Version="2.0.6">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.10" />
    <PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="6.0.10" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Cosmos" Version="6.0.10" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.10">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    
    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.10" />
  </ItemGroup>

@toiyabe

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.