Coder Social home page Coder Social logo

dlmelendez / identityazuretable Goto Github PK

View Code? Open in Web Editor NEW
103.0 16.0 30.0 6.88 MB

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

License: MIT License

C# 97.91% HTML 1.69% CSS 0.32% JavaScript 0.07%
identity asp azure-storage azure-table-storage asp-net-core asp-net-mvc aspnet aspnetidentity aspnetcoreidentity asp-net-core-identity

identityazuretable's Introduction

identityazuretable's People

Contributors

charmatzis avatar dependabot[bot] avatar dlmelendez avatar martincostello avatar remibou 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  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  avatar  avatar  avatar  avatar  avatar

identityazuretable's Issues

registration procedure error

Hi,
thank you for your wonderful work, that I'm testing to use it in my azure app.
I think there is a bug in the registration procedure, in particular:
when I register a new user with an email that already exist, I get this error

Object reference not set to an instance of an object
GetUserIdAsync

stack:
System.NullReferenceException: Object reference not set to an instance of an object.
at ElCamino.AspNetCore.Identity.AzureTable.UserStore7.GetUserIdAsync(TUser user, CancellationToken cancellationToken) at Microsoft.AspNetCore.Identity.UserManager1.d__78.MoveNext()
...
...samplemvccore\Controllers\AccountController.cs:line 109
(call from: var result = await _userManager.CreateAsync(user, model.Password);)

with default Identity Provider normally I go back to the registration form with the reporting of the error in the field --> User name 'email' is already taken.

thank you

Password Rules

I've hooked up your library to my project. To be honest, it's a great piece of work. But, how do i set password rules like in the EF library?

Claim Key Deletion Error

I am storing access token from an oAuth transaction in claims, when I add the claim it works fine but when I try to delete the claim, the azure table sends a 400 bad request every time.

After troubleshooting for a while, I removed the claim value from the rowkey and generated new keys with only using the claimType. Now it is working fine. UriEncodeKeyHelper.cs --> Line: 63.

I was wondering why are you using the claim value in the row key and if i should merge my changes or is there a better way to handle this.

Thanks
Rahul Gupta

Feature Request: Supporting Multi-tenancy w/ change to Partition Key

Hi, right now, for a given user, both the Partition Key and Row Key are set to the HTML encoded version of the user's email address.

However, to support multi-tenancy, I think all you would need to do is change Partition Key to the ID of the tenant website. Admittedly, most applications are not multi-tenant, so this would always be the name of the only tenant, the website's name or some default.

However, what if you want single code base to support the same user on multiple tenant sites, like an SaaS, then you could have:

PK: TENANT-WEBSITE01 RK: U_BOB_40DEVU.COM
PK: TENANT-WEBSITE02 RK: U_BOB_40DEVU.COM
PK: TENANT-WEBSITE03 RK: U_BOB_40DEVU.COM

Three separate login accounts across three separate sites, but one user / email address.

Then permissions could be different on a per tenant website basis, etc.

Yes, you do have support for multi-tenancy at the TABLE level (one set of tables PER TENANT) via the tablePrefix configuration property, however what I'm suggesting is multi-tenancy at the ROW level (one set of tables for ALL tenants).

The problem (for me) with each tenant getting its own set of tables is that it would make management more difficult. First, it would explode the number of tables in a given account. Second, I would have to deploy a single code base to multiple Azure Web accounts, one for each tenant. That might make sense in a low-volume scenario. However, consider what happens when you have potentially 1000 tenants (ex. a freemium business model).

Ideally, I could have a single code base deployed to a single Azure Web account. My AspNetMVC5 app would just handle the domain mapping ... for a given requested domain, look up the tenant ID (or use a Partition Key convention). For the given request El Camino would look in a single set of tables for the particular tenant (PK) + user (RK) combination.

WindowsAzure.Storage dependency issue

I can't update ElCamino.AspNetCore.Identity.AzureTable from version 1.6.1 to 1.6.2 because your NuGet strictly depends on WindowsAzure.Storage 7.1.3-preview. I have WindowsAzure.Storage 8.0.0 for all my solution projects. Please fix this issue.

error: Version conflict detected for WindowsAzure.Storage. error: MobileWebService (>= 1.5.0) -> DataModel -> WindowsAzure.Storage (>= 8.0.0) error: MobileWebService (>= 1.5.0) -> ElCamino.AspNetCore.Identity.AzureTable (>= 1.6.2) -> WindowsAzure.Storage (= 7.1.3-preview). log : Writing lock file to disk. Path: E:\VSTS\tiksn\Grocery Checklist\Code\MobileWebService\project.lock.json log : E:\VSTS\tiksn\Grocery Checklist\Code\MobileWebService\MobileWebService.xproj log : Restore failed in 54689ms. Errors in E:\VSTS\tiksn\Grocery Checklist\Code\MobileWebService\MobileWebService.xproj Version conflict detected for WindowsAzure.Storage. MobileWebService (>= 1.5.0) -> DataModel -> WindowsAzure.Storage (>= 8.0.0) MobileWebService (>= 1.5.0) -> ElCamino.AspNetCore.Identity.AzureTable (>= 1.6.2) -> WindowsAzure.Storage (= 7.1.3-preview).

Not possible to override ReadEntity/WriteEntity

Hello,

While using your library, thanks by the way, we had the need to store a more complex data type in our user table. Since Azure Table Storage does not support any complex data types, we had to hook into the WriteEntity/ReadEntity calls from ITableEntity. In IdentityUser<T>, which implements ITableEntity, the methods are not virtual, meaning we could not simply override them. Instead, we had to explicitly implement ITableEntity in our user model that extends IdentityUser.
Just wanted to let you know that this is a possible use case for your API, and it would be nice if those methods were virtual so that people can hook into them and call their serialization logic if needed :)

Best regards

Refactor KeyHelper into IKeyHelper interface

Removes the concrete dependency on static class KeyHelper.
Opens up possibility for custom key formatters in the future.
First step in moving .Model namespace to separate assembly

Works identityazuretable with AspNetCore WebAPI?

Hi,

I want create a AspNet Core Web API application with a JWT Bearer Authentication for a mobile solotion.
Does anyone have experience creating an authentication API with Azue Tables?

Many thanks!
CodeEXc

Cannot add claims to user. An item with the same key has already been added.

Hello -

I've been using your libraries for a while now going back to asp.net 4 and am so thankful that you created this project :) ๐Ÿ‘

I updated my website to Asp.net core 2.1 using razor pages. Originally, I was targeting netcoreapp2.1 and everything was working great. I could create users and add claims to them no problem. Authorization Policies were able to see the claims and all was well. Then I had an issue with another package which required me to retarget to full framework 4.7.1 (net471). After doing this, I started getting this exception when trying to add claims to my users.

Microsoft.WindowsAzure.Storage.StorageException
  HResult=0x80131500
  Message=An item with the same key has already been added.
  Source=Microsoft.WindowsAzure.Storage
  StackTrace:
   at Microsoft.WindowsAzure.Storage.Core.Executor.Executor.EndExecuteAsync[T](IAsyncResult result)
   at Microsoft.WindowsAzure.Storage.Table.CloudTable.EndExecuteBatch(IAsyncResult asyncResult)
   at Microsoft.WindowsAzure.Storage.Core.Util.AsyncExtensions.<>c__DisplayClass1`1.<CreateCallback>b__0(IAsyncResult ar)
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at ElCamino.AspNetCore.Identity.AzureTable.Helpers.BatchOperationHelper.<>c__DisplayClass4_1.<<ExecuteBatchAsync>b__1>d.MoveNext()
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.<>c.<ThrowAsync>b__6_1(Object state)
   at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

Inner Exception 1:
ArgumentException: An item with the same key has already been added.

This code works when targeting netcoreapp2.1 and does not work when targeting net471.

var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
    _logger.LogInformation("User created a new account with password.");
    
    if (user.UserName.EndsWith("@mycompany.com", StringComparison.OrdinalIgnoreCase))
    {
        await this._userManager.AddClaimAsync(user, new System.Security.Claims.Claim("Access", "Administrator"));
    }

I tried using WindowsAzure.Storage Version="9.3.0" and WindowsAzure.Storage Version="8.6.0" with same result.

To retarget, replace Microsoft.AspNetCore.App package with the following:

    <PackageReference Include="Microsoft.AspNetCore" Version="2.1.1" />
    <PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.1.1" />
    <PackageReference Include="Microsoft.AspNetCore.CookiePolicy" Version="2.1.1" />
    <PackageReference Include="Microsoft.AspNetCore.HttpsPolicy" Version="2.1.1" />
    <PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="2.1.1" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.1.1" />
    <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.1.1" />
    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.1.1" PrivateAssets="All" />
    <PackageReference Include="ElCamino.AspNetCore.Identity.AzureTable" Version="2.1.0" />
    <PackageReference Include="WindowsAzure.Storage" Version="9.3.0" />

and update the property group with

    <TargetFramework>net471</TargetFramework>
    <DebugType>Full</DebugType>

Cosmos db support

Is it normal that it can't connect to cosmos (table storage)?
Gives me connection errors. Is this normal and should I just connect wit the normal storage table or am I setting it up badly?

Invalid Table Prefix Fails Silently

After going through code again and again and comparing my code to the samples, I realized that the underscore ("_") in my prefix might be causing for my setup not to work.

Sure enough, changing from ID_ to ID fixed the issue. The tables were created as expected.

I think that an exception should be thrown to keep people sane :)

Use hash of login details as index key for external/social logins

We are planning on only allowing registration via external identity providers: Google, FB, Yahoo and MS. The implemented strategy for external logins will only allow a small number of partitions for this table, which could adversely affect performance, although according to Troy Hunt (http://www.troyhunt.com/2013/12/working-with-154-million-records-on.html), partitions with 30M+ entities still provide good response times.

A suggested alternative index is a hash of the provider and user Id, spread across a specified number of partitions. e.g.:

        // taken from http://stackoverflow.com/questions/9545619/a-fast-hash-function-for-string-in-c-sharp
        UInt64 CreateIntHash(string read)
        {
            // not for cryptography!!!! NOT SECURE!!!!
            // from Knuth - this is used for e.g sorting strings into buckets
            UInt64 hashedValue = 3074457345618258791ul;
            for (int i = 0; i < read.Length; i++)
            {
                hashedValue += read[i];
                hashedValue *= 3074457345618258799ul;
            }
            return hashedValue;
        }

        const int MAX_PARTITIONS = 1000;
        public override string GeneratePartitionKeyIndexByLogin(string plainProvider, string providerKey)
        {
            var hash = CreateIntHash(EscapeKey(plainProvider + '_' + providerKey)) % MAX_PARTITIONS;
            return hash.ToString();
            // return EscapeKey(plainProvider); <- too few partitions
        }

Of course specifying the number of partitions is not required as the plain hash could be used for a partition key as is.

Docs Sample?

I'm working on docs for writing custom identity providers for aspnet core. Would you be interested in assisting me with using your azure provider as a sample in the documentation?

UserManager.GetRolesAsync failing in 3.1 after migration from 1.7

I migrated from 1.7 to latest using the tool. Used samplemvccore to test.
login works fine, I injected UserManager in home controller and used
var roles = UserManager.GetRolesAsync(user).Result;
to test roles. It always returns empty list.

Debugging it, I get the count of expected two roles in UserStore.cs on line number 140.
int userRoleTotalCount = userRoles.Count();
But the query after that returns empty list.

BTW Role table is empty for some reason.

Adding User Role functionality to web app

I'm using ASP.NET 3.1 Core and I'm trying to get user role functionality enabled and working so I can assign roles to users.

Following the 'instructions' and the source code to samplemvccore4, I get an app that has everything but user roles. When I try to follow ASP.NET documentation and add something like this in my startup:

                .AddRoles<IdentityUserRole>()

It blows up the CreateAzureTablesIfNotExists method in IdentityAzureTableBuilderExtensions.

So, how do you enable user roles in an ASP.NET application using this library?

Exception on wrong table prefix or table name

Environment: your net core sample

When happen:
Change table prefix with underscore or one unsupportable symbols.
Tables on startup will not created and there are no warning or exceptions. When login action are invoked, they throw a exception.

Exception:
System.AggregateException: One or more errors occurred. (Cannot access a disposed object.
Object name: 'System.Net.Http.WinHttpResponseStream'.) ---> Microsoft.WindowsAzure.Storage.StorageException: Cannot access a disposed object.
Object name: 'System.Net.Http.WinHttpResponseStream'. ---> System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.Http.WinHttpResponseStream'.
at System.Net.Http.WinHttpResponseStream.CheckDisposed()
at System.Net.Http.WinHttpResponseStream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken token)
at System.Net.Http.DelegatingStream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
at Microsoft.WindowsAzure.Storage.Core.Util.StreamExtensions.d__11.MoveNext() in C:\Program Files (x86)\Jenkins\workspace\release_dotnet_master\Lib\Common\Core\Util\StreamExtensions.cs:line 299 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.WindowsAzure.Storage.Core.Executor.Executor.<ExecuteAsyncInternal>d__41.MoveNext() in C:\Program Files (x86)\Jenkins\workspace\release_dotnet_master\Lib\WindowsRuntime\Core\Executor\Executor.cs:line 179
--- End of inner exception stack trace ---
at Microsoft.WindowsAzure.Storage.Core.Executor.Executor.d__41.MoveNext() in C:\Program Files (x86)\Jenkins\workspace\release_dotnet_master\Lib\WindowsRuntime\Core\Executor\Executor.cs:line 315 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.WindowsAzure.Storage.Table.TableQuery.<>c__DisplayClass28_0.<<ExecuteQuerySegmentedAsync>b__0>d.MoveNext() in C:\Program Files (x86)\Jenkins\workspace\release_dotnet_master\Lib\WindowsRuntime\Table\TableQuery.cs:line 75 --- End of inner exception stack trace --- at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at Microsoft.WindowsAzure.Storage.Table.AzureSdkHelper.ExecuteQuery(CloudTable ct, TableQuery tq) at ElCamino.AspNetCore.Identity.AzureTable.UserStore7.GetUserAggregateAsync(String userId)
at ElCamino.AspNetCore.Identity.AzureTable.UserStore7.FindByNameAsync(String normalizedUserName, CancellationToken cancellationToken) at Microsoft.AspNetCore.Identity.UserManager1.FindByNameAsync(String userName)
at Microsoft.AspNetCore.Identity.SignInManager1.<PasswordSignInAsync>d__33.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult()
at AzureTableApplication.Controllers.AccountController.d__7.MoveNext() in E:\workspaces_testlab\AzureTableApplication\src\AzureTableApplication\Controllers\AccountController.cs:line 62
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__27.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__25.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__22.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ResourceExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__20.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Builder.RouterMiddleware.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware1.<Invoke>d__18.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware1.d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware1.<Invoke>d__18.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware1.d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware1.<Invoke>d__18.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware1.d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware1.<Invoke>d__18.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware1.d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.MigrationsEndPointMiddleware.d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.d__7.MoveNext()
---> (Inner Exception #0) Microsoft.WindowsAzure.Storage.StorageException: Cannot access a disposed object.
Object name: 'System.Net.Http.WinHttpResponseStream'. ---> System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.Http.WinHttpResponseStream'.
at System.Net.Http.WinHttpResponseStream.CheckDisposed()
at System.Net.Http.WinHttpResponseStream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken token)
at System.Net.Http.DelegatingStream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
at Microsoft.WindowsAzure.Storage.Core.Util.StreamExtensions.d__11.MoveNext() in C:\Program Files (x86)\Jenkins\workspace\release_dotnet_master\Lib\Common\Core\Util\StreamExtensions.cs:line 299 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.WindowsAzure.Storage.Core.Executor.Executor.<ExecuteAsyncInternal>d__41.MoveNext() in C:\Program Files (x86)\Jenkins\workspace\release_dotnet_master\Lib\WindowsRuntime\Core\Executor\Executor.cs:line 179
--- End of inner exception stack trace ---
at Microsoft.WindowsAzure.Storage.Core.Executor.Executor.d__4`1.MoveNext() in C:\Program Files (x86)\Jenkins\workspace\release_dotnet_master\Lib\WindowsRuntime\Core\Executor\Executor.cs:line 315
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.WindowsAzure.Storage.Table.TableQuery.<>c__DisplayClass28_0.<b__0>d.MoveNext() in C:\Program Files (x86)\Jenkins\workspace\release_dotnet_master\Lib\WindowsRuntime\Table\TableQuery.cs:line 75
Request Information
RequestID:ba34a46e-0002-0041-1cfb-4a3d14000000
RequestDate:Wed, 30 Nov 2016 14:15:44 GMT
StatusMessage:BadRequest
ErrorCode:InvalidResourceName
<---

Sample app references v. 1.6 which doesn't exist in NuGet

When attempting to load the MVC5 sample, (\identityazuretable-master\sample), to work with the "samplemvc" project, and then Restore NuGet Packages for Solution, you'll get an exception:

NuGet Package restore failed for project samplemvc: Unable to find version '1.6.0' of package 'ElCamino.AspNet.Identity.AzureTable'.
https://www.myget.org/f/aspnetmaster/api/v3/index.json: Package 'ElCamino.AspNet.Identity.AzureTable.1.6.0' is not found on source 'https://www.myget.org/f/aspnetmaster/api/v3/index.json'.
https://api.nuget.org/v3/index.json: Package 'ElCamino.AspNet.Identity.AzureTable.1.6.0' is not found on source 'https://api.nuget.org/v3/index.json'.
.

There doesn't seem to be a 1.6 published to NuGet, only 1.5.4.

Improper string escaping (possible security issue)

Class UriEncodeKeyHelper does not properly escape user input and different strings can be escaped into the same value, potentially opening up space for security issues.

E.g. method UriEncodeKeyHelper.EscapeKey would produce the same outputs for inputs [email protected] and [email protected].

Additionally, the use of _ as both the delimiter and the replacement for % encoding leads to additional ambiguity.

Suggested fix:

  1. In Constants.cs, use | instead of _ as the field delimiter

  2. In UriEncodeKeyHelper.cs, replace occurrences of "{0}_{1}" with "{0}|{1}"

  3. In UriEncodeKeyHelper.EscapeKey, replace the escaping code with something like:

    return System.Uri.EscapeDataString(keyUnsafe)
      // Percent-encode underscore even though it is an 'unreserved character' (see https://www.ietf.org/rfc/rfc2396.txt) because we use it instead of "%" in our encoding.
      .Replace("_", "%5F")
      .Replace("%", "_").ToUpper();

Note: I am not submitting a PR because I haven't had time to fully test the suggestions above. Additionally, some effort is needed to make these changes without breaking existing clients.

How to make the storageConnectionString an env variable?

Hey David,

I was following this guide to connect my app with table storage and it all works fine.

However, I had a question - the connection string of the table storage need to be specified like this according to the documentation: <elcaminoIdentityConfiguration tablePrefix="" storageConnectionString="UseDevelopmentStorage=true" /> but when I want to deploy this app to the cloud, lets say Azure, how would I make this as an environment variable (for Azure, an Application Setting/Connection String)?

If this was a key-value pair in the <appSettings> section, it can easily made as a env variable in the hosting provider. Likewise the connection string in the <connectionStrings> section. Keen to hear your thoughts!

Thanks,
Clyde

Question about Login/Register views and controllers

I recently created a new ASP.NET MVC project using dotnet new mvc --auth Individual and tested that everything was working. i.e. I clicked on the Register button, I created an account, I then logged out, and logged back out again, and everything worked as expected. All of this was done using the Sqlite Database for persistence.

I then followed the guide here:

https://dlmelendez.github.io/identityazuretable/#/walkthroughcore2

To set about switching to use Azure Table Storage as the persistence layer. After a little bit of messing around, I was able to get it to create the tables in the locally running Azure Storage Emulator, and I thought I was all set to start creating users. However, when I ran the site, and tried to click on the Register and Login buttons, they no longer worked.

@dlmelendez can you confirm that I will need to re-implement the Register/Login View and Controllers in order to work with your NuGet package for storing information in Azure Table Storage?

Thank you for your help in building out this library, I believe that it is going to be very useful to me!

Save config values in ServiceConfiguration.cscfg

My app settings go in ServiceConfiguration.XXXX.cscfg for my various cloud deployments, is there a way of overwriting the config settings so I can pass in my own IdentityConfiguration? (rather than it pulling values from a configSection of the Web.Config)

Trying to use this in API project

services.AddIdentityServer() .AddApiAuthorization<CloudUser, CloudContext>();

It shows 2 errors:

  1. Error CS0311 The type 'DataAccess.CloudContext' cannot be used as type parameter 'TContext' in the generic type or method 'IdentityServerBuilderConfigurationExtensions.AddApiAuthorization<TUser, TContext>(IIdentityServerBuilder)'. There is no implicit reference conversion from 'DataAccess.CloudContext' to 'Microsoft.EntityFrameworkCore.DbContext'. Api C:\Users\arvin\Documents\Source\Projects...\Api\Startup.cs 67 Active
  2. Error CS0311 The type 'DataAccess.CloudContext' cannot be used as type parameter 'TContext' in the generic type or method 'IdentityServerBuilderConfigurationExtensions.AddApiAuthorization<TUser, TContext>(IIdentityServerBuilder)'. There is no implicit reference conversion from 'DataAccess.CloudContext' to 'IdentityServer4.EntityFramework.Interfaces.IPersistedGrantDbContext'. Api C:\Users\arvin\Documents\Source\Projects...\Api\Startup.cs 67 Active

Further, could not modify dlmelendez/identityazuretable project to make ElCamino.AspNetCore.Identity.AzureTable.IdentityCloudContext derive from Microsoft.EntityFrameworkCore.DbContext, gives Microsoft.EntityFrameworkCore is missing.

Breaking Changes in 2.2?

I upgraded to 2.2 not thinking to much about it and it seems that there are breaking changes related to how usernames are saved based on user ID and user names (from what I was able to walk though in the latest commits). Logins that previously worked no longer work.

Recreating the same user created different entries.

Is that right?

Tables Names in IdentityConfiguration

Hi,

could you please include the Tables Names from the "Constants.TableNames" into the "IdentityConfiguration" class?
Maybe user the constants as defaults.

I don't want to fork or copy paste your code. I like to stay up to date with your changes ;)

Kind Regards
Mario

Error on removing from Role

David, thanks for your work on this great library; have been able to successfully implement and use the basic cases fully.

However, I am running into some unexpected behavior when removing a User from a Role with a full-on many-to-many scenario, i.e. with many Users in many Roles. This is using the ASP.NET MVC5 version.

Example:

  • Role A, Role B, Role C
  • User 1
  • User 1 is in all Roles

Using the method RoleManager.RemoveFromRole(userId, roleId), I am observing the following:

Removing User 1 from Role B: results in removal of User 1 from Role A. User 1 remains in Role B and C.
Removing User 1 from Role C: results in removal of User 1 from Role B. User 1 remains in Role C.
Removing User 1 from Role C: results in removal of User 1 from Role C. User 1 is no longer in any Role.

In sum: removal actually does work, just that the removal doesn't seem to "target" correctly. Not sure if I'm describing it correctly -- seems almost as if the removal targets the Role which precedes the targeted Role in whatever default sort order the Roles are in, in the list of Roles the User belongs to.

I have verified (debug) that the given User is indeed in whatever Role I am testing; that the list of Role memberships for the User is correct and complete; that the IDs of the User and Role in any given remove operation are correct; and, finally, that RoleManager.RemoveFromRole(userId, roleId) does return IdentityResult.Success = true.

And yet: the User is removed from the wrong Role.

I am seeing the User-to-Role relationships stored in the AspNetUsers Azure Table according to the specifications described in your technical documentation.

Is there something you can recommend that I try? Or perhaps I am misusing the RoleManager, UserManager functionality?

Thank you!

Claims Support for IdentityUserV2

The latest release has an obsolete warning on IdentityUser and recommends the move to V2. However, V2 does not support claims (or roles).

Is there guidance on using V2 with claims or is there a different plan all together for claims?

Incorrect time shift for LockoutEndDateUtc

My local time (Germany) is UTC+2.

I set
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(4);

and forced the lockout at 7.30 (UTC). However, in the database 9.30 is saved instead of the expected result 11.30. I think it is caused by your time conversions. Using the EntityFramework ASP.NET Identity storage it works as expected.

StorageException when registering user

When I register a user in the .net core sample website (the tables are newly created), I get this exception thrown every time:

"An item with the same key has already been added. Key: UserName"

at Microsoft.WindowsAzure.Storage.Core.Executor.Executor.d__41.MoveNext()\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at System.Runtime.CompilerServices.TaskAwaiter.GetResult()\n at ElCamino.AspNetCore.Identity.AzureTable.UserStore7.d__10.MoveNext() in /Users/ryanoconnor/Documents/fluid-cms/ElCamino.AspNetCore.Identity.AzureTable/UserStoreV2.partial.cs:line 248"

As you can see I've put the package into my project to debug it (the project is simply the dotnet core sample template with identity), line 248 is throwing the exception. I have tried to debug this but I don't understand why there is a collision. My UserName is my email address and I have checked in Storage Explorer and there are no rows inserted into the users table (the error is with the users table, not the index table - which populates fine despite the exception).

Any ideas? Any more info I can provide?

@if (SignInManager.IsSignedIn(User)) in _LoginPartial.cshtml never returns Success

Hi,

I'm using the _LoginPartial.cshtml from identity scaffolding. It is supposed to switch to logged on view based on this 'if' statement but it never does. ClaimsPrincipal User is empty.

I'd appreciate your advise.

Thanks for putting this together. It really feels solid and much needed!
I just onboarded this project, and trying to integrate into a new solutions (and new to asp.net core, so I'm sure I'm still missing some fundamentals...).

Cheers,

Eyal

-- code snippet --

@using Microsoft.AspNetCore.Identity
@using App.Areas.Identity.Data
@inject SignInManager<AppUser> SignInManager
@inject UserManager<AppUser> UserManager

@if (SignInManager.IsSignedIn(User))
{
** NEVER REACHED ALTHOUGH SIGNIN SUCCESSFUL **
    <form asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Page("/Index", new { area = "" })" method="post" id="logoutForm" class="navbar-right">
        <ul class="nav navbar-nav navbar-right">
            <li>
                <a asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @UserManager.GetUserName(User)!</a>
            </li>
            <li>
                <button type="submit" class="btn btn-link navbar-btn navbar-link">Logout</button>
            </li>
        </ul>
    </form>
}
else
{
    <ul class="nav navbar-nav navbar-right">
        <li><a asp-area="Identity" asp-page="/Account/Register">Register</a></li>
        <li><a asp-area="Identity" asp-page="/Account/Login">Login</a></li>
    </ul>
}

Extending the basekeyhelper class to support tenant id

@dlmelendez, I read the "Feature Request to support Multi-tenancy w/ change to Partition Key" post. You mention a plan to extend the basekeyhelper class. Did you complete this change? If so, how would you extend it to support putting a tenant id in the partition key?

Thanks for considering my request,
Mark

Multitenancy support

Is multi tenant supported by either

  • StorageConnectionString per tenant
  • Individual tables per tenant
  • Tenant id on the ApplicationUser

If so, any samples.

How To Manually Add Role to User

I have only two users that need and admin role for my app and I was hoping to avoid creating a user management UI. Is there a way to manually add the roles to a user using Azure Storage Explorer?

I assume I need to make an entry in the AspNetRoles table, but I'm not sure what to put in each column.

how to create branch

Hello,

I'd like to create a branch and submit pull request for this project. Is it possible? If not, I would like to suggest a few things here.

  1. ForEach extension method. This will help you remove intermediate list creation just to get ForEach semantics.
/// <summary>
    /// Extension methods for <see cref="IEnumerable{T}"/>
    /// </summary>
    public static class EnumerableExtensions
    {
        /// <summary>
        /// Performs the specified action on each element of the <see cref="IEnumerable{T}"/>
        /// </summary>
        /// <typeparam name="T">Type of item in the enumerable</typeparam>
        /// <param name="enumerable">
        /// The enumerable for which to perform an action on each element
        /// </param>
        /// <param name="action">
        /// The <see cref="Action{T}"/> delegate to perform on each element of the
        /// <see cref="IEnumerable{T}"/>
        /// </param>
        public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
        {
            if (enumerable is List<T> list)
            {
                list.ForEach(action);
                return;
            }

            foreach (var item in enumerable)
            {
                action(item);
            }
        }
    }
  1. Improved LINQ for building task lists.
    In UserStoreV2, This:
List<Task> tasks = new List<Task>(listTqs.Count);
            listTqs.ForEach((q) =>
            {
                tasks.Add(Task.Run(async () =>
                {
                    (await _userTable.ExecuteQueryAsync(q))
                    .ToList()
                    .GroupBy(g => g.PartitionKey)
                    .ToList()
                    .ForEach((s) =>
                    {
                        bag.Add(MapUser(s.Key, s));
                    });
                }));
            });
            await Task.WhenAll(tasks);

can be simplified to this:

            await Task.WhenAll(
                listTqs.Select(async q =>
                {
                    (await _userTable.ExecuteQueryAsync(q).ConfigureAwait(false))
                                     .GroupBy(g => g.PartitionKey)
                                     .ForEach(s => bag.Add(MapUser(s.Key, s)));
                })).ConfigureAwait(false);

and this:

List<Task> tasks = new List<Task>(listTqs.Count);
            listTqs.ForEach((q) =>
            {
                tasks.Add(
                     _userTable.ExecuteQueryAsync(q)
                     .ContinueWith((taskResults) =>
                     {
                         //ContinueWith returns completed task. Calling .Result is safe here.
                         var r = taskResults.Result;
                         r.ToList()
                         .GroupBy(g => g.PartitionKey)
                         .ToList().ForEach((s) =>
                         {
                             var userAgg = MapUserAggregate(s.Key, s);
                             bool addUser = true;
                             if (whereClaim != null)
                             {
                                 if (!userAgg.Claims.Any(whereClaim))
                                 {
                                     addUser = false;
                                 }
                             }
                             if (whereRole != null)
                             {
                                 if (!userAgg.Roles.Any(whereRole))
                                 {
                                     addUser = false;
                                 }
                             }
                             if (addUser)
                             {
                                 bag.Add(userAgg.User);
                             }
                         });
                     }, TaskContinuationOptions.ExecuteSynchronously)                   
                    );
            });
            await Task.WhenAll(tasks);

can be simplified to this:

var tasks = listTqs.Select(async q =>
            {
                (await _userTable.ExecuteQueryAsync(q).ConfigureAwait(false))
                                 .GroupBy(g => g.PartitionKey)
                                 .ForEach(s =>
                                 {
                                     var userAgg = MapUserAggregate(s.Key, s);
                                     bool addUser = true;
                                     if (whereClaim != null)
                                     {
                                         if (!userAgg.Claims.Any(whereClaim))
                                         {
                                             addUser = false;
                                         }
                                     }
                                     if (whereRole != null)
                                     {
                                         if (!userAgg.Roles.Any(whereRole))
                                         {
                                             addUser = false;
                                         }
                                     }
                                     if (addUser)
                                     {
                                         bag.Add(userAgg.User);
                                     }
                                 });
            });
            await Task.WhenAll(tasks).ConfigureAwait(false);
  1. nameof instead of magic strings
    eg,
TableQuery.GenerateFilterCondition(nameof(TableEntity.PartitionKey), QueryComparisons.Equal, userId),
                TableOperators.And,
                TableQuery.GenerateFilterCondition(nameof(TableEntity.RowKey), QueryComparisons.Equal, rowKey));
  1. Consider retrieve instead of query with partition and row key
var tableResult = await _userTable.ExecuteAsync(TableOperation.Retrieve(userId, rowKey));
(TUserLogin)tableResult.Result;

How Role, Claim and Token is supported?

Hi,
first of all great job!!!!

I just want to ask a simple question which I don't get it 100%

Here https://github.com/dlmelendez/identityazuretable/blob/master/sample/samplemvccore2/Startup.cs#L39 you are saying "if your code depends on the Role, Claim and Token โ€ฆ to use .AddAzureTableStores"

If I want to use Role, Claims and Tokens should I use .AddAzureTableStores with IdentityUser?
and if my implementation is simpler is should use .AddAzureTableStoresV2 with IdentityUserV2?
(https://github.com/dlmelendez/identityazuretable/blob/master/sample/samplemvccore2/Models/ApplicationUser.cs#L10 )

Am I getting it right?

What's the difference between "V2" and "old version", which is obsolete?

Thanks in advance,
Christos

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.