identitysever and AspNetUsers - c#

I have a set up where I have
services.AddIdentity<AppUser, AppRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddUserManager<userManage>()
.AddDefaultTokenProviders();
where AppUser and AppRole is used, but it seems to fail because of that. I keep getting
ArgumentNullException: Value cannot be null. Parameter name: type for the claims at System.Security.Claims.Claim..ctor
after
Microsoft.AspNetCore.Identity.IdentityUserClaim1.ToClaim()
Whole log at the bottom
Everything was working before I introduced the extension for the IdentityUser and IdentityRole
for the IDS4 set up I have:
services.AddIdentityServer(options => {
options.UserInteraction.LoginUrl = "/Account/Login";
})
.AddSigningCredential(new X509Certificate2(Path.Combine(".", "certs"
, "IdentityServer4Auth.pfx")))
.AddAspNetIdentity<AppUser>()
.AddConfigurationStore(options => {
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connection_string, sql => sql.MigrationsAssembly(migrations_assembly));
})
.AddOperationalStore(options => {
//options.DefaultSchema = "token";
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connection_string, sql => sql.MigrationsAssembly(migrations_assembly));
})
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddJwtBearerClientAuthentication()
.AddProfileService<IdentityProfileService>();
that was working fine, but the switch from
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{ })
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddDistributedMemoryCache();
to
services.AddIdentity<AppUser, AppRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddUserManager<userManage>()
.AddDefaultTokenProviders();
and that kills it now. I know it works as far as the AppUser, AppRole, and userManage are set up because they the same set up used in many of my apps, but as soon as it is mix with the IDS4 it is now failing. When it was working i had extended the IdentityUser
public class ApplicationUser : IdentityUser {}
The was working too, it was when I mixed the 2 apps together, so both had the AppUser, AppRole, and userManage, is when it went bad. I will put the models below
Side note too, I have followed the logs, and what is killing me here is the query run in the DB is correct. When I run it I see no nulls for any type values. I have even scrubbed the DB for any nulls in any of the claim areas, like the roles or user level just to be safe. I have put a break point on the area that causes the fault,
var signin_result = await _signInManager.PasswordSignInAsync(_user, test, model.RememberMe, false);
which when I look at the _user I see that it has the security stamp and all values correctly filled
it is on the user claims
SELECT [uc].[Id], [uc].[ClaimType], [uc].[ClaimValue], [uc].[UserId]
FROM [AspNetUserClaims] AS [uc]
WHERE [uc].[UserId] = #__user_Id_0
but I don't see where the issue is when that returns nothing with a null
the log
2018-10-08 13:17:47.164 -07:00 [Information] Entity Framework Core "2.1.4-rtm-31024" initialized '"ApplicationDbContext"' using provider '"Microsoft.EntityFrameworkCore.SqlServer"' with options: "SensitiveDataLoggingEnabled "
2018-10-08 13:17:47.189 -07:00 [Information] Executed DbCommand ("1"ms) [Parameters=["#__user_Id_0='11325643' (Size = 450)"], CommandType='Text', CommandTimeout='30']"
""SELECT [uc].[Id], [uc].[ClaimType], [uc].[ClaimValue], [uc].[UserId]
FROM [AspNetUserClaims] AS [uc]
WHERE [uc].[UserId] = #__user_Id_0"
2018-10-08 13:17:47.370 -07:00 [Error] An exception occurred in the database while iterating the results of a query for context type '"test.app.Data.ApplicationDbContext"'."
""System.ArgumentNullException: Value cannot be null.
Parameter name: type
at System.Security.Claims.Claim..ctor(String type, String value, String valueType, String issuer, String originalIssuer, ClaimsIdentity subject, String propertyKey, String propertyValue)
at System.Security.Claims.Claim..ctor(String type, String value)
at Microsoft.AspNetCore.Identity.IdentityUserClaim`1.ToClaim()
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ProjectionShaper.TypedProjectionShaper`3.Shape(QueryContext queryContext, ValueBuffer& valueBuffer)
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ProjectionShaper.TypedProjectionShaper`3.Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.IShaper<TOut>.Shape(QueryContext queryContext, ValueBuffer& valueBuffer)
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.BufferlessMoveNext(DbContext _, Boolean buffer, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.MoveNext(CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext(CancellationToken cancellationToken)"
System.ArgumentNullException: Value cannot be null.
Parameter name: type
at System.Security.Claims.Claim..ctor(String type, String value, String valueType, String issuer, String originalIssuer, ClaimsIdentity subject, String propertyKey, String propertyValue)
at System.Security.Claims.Claim..ctor(String type, String value)
at Microsoft.AspNetCore.Identity.IdentityUserClaim`1.ToClaim()
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ProjectionShaper.TypedProjectionShaper`3.Shape(QueryContext queryContext, ValueBuffer& valueBuffer)
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ProjectionShaper.TypedProjectionShaper`3.Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.IShaper<TOut>.Shape(QueryContext queryContext, ValueBuffer& valueBuffer)
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.BufferlessMoveNext(DbContext _, Boolean buffer, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.MoveNext(CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext(CancellationToken cancellationToken)
2018-10-08 13:17:48.680 -07:00 [Information] Executed action "WSU.Sso.Controllers.AccountController.Login (test.app)" in 2022.4589ms
The Models, and set up (note in the none IDS4 app these are working just fine)
public class AppUser : IdentityUser {}
public class AppRole : IdentityRole {}
public class AppUserClaim : IdentityUserClaim<string> {}
public class AppUserRole : IdentityUserRole<string> {}
public class AppRoleClaims : IdentityRoleClaim<string> {}
public partial class CoreDbContext : IdentityDbContext
<
AppUser, // TUser
AppRole, // TRole
string, // TKey
AppUserClaim, // TUserClaim
AppUserRole, // TUserRole,
IdentityUserLogin<string>, // TUserLogin
AppRoleClaims, // TRoleClaim
IdentityUserToken<string> // TUserToken
>//, ICoreDbContext
{
//etc..
}
Update
I believe the issue is here, https://github.com/IdentityServer/IdentityServer4.AspNetIdentity/blob/dev/src/UserClaimsFactory.cs where UserManager<TUser> userManager needs to be userManage because I extended that. I am guessing I need to role my own it seems?
Update 2
Turns out, after removing the userManage extending UserManager<TUser> that it still fails with
System.Security.Claims.Claim..ctor(string type, string value, string valueType, string issuer, string originalIssuer, ClaimsIdentity subject, string propertyKey, string propertyValue)
System.Security.Claims.Claim..ctor(string type, string value)
Microsoft.AspNetCore.Identity.IdentityUserClaim.ToClaim()
There was hope that simplifying it would be the anwser so i didn't have to rewrite the .AddAspNetIdentity<AppUser>() but that didn't work. It is clear that it is not something that comes from the DB that is null, but must be something added that it's value is null. I can't tell what, just that it must be between the Microsoft.AspNetCore.Identity.IdentityUserClaim<TKey>.ToClaim() and the IdentityServer4.AspNetIdentity.UserClaimsFactory<TUser>.CreateAsync(TUser user) in UserClaimsFactory.cs ... What I can say for sure is that in https://github.com/IdentityServer/IdentityServer4.AspNetIdentity/blob/dev/src/UserClaimsFactory.cs all of the parts that is is trying to set is verifaied in the DB as not null.
It is also worth nothing that the IDS4 was working before I added the the custom role. I am not 100% sure that is not still in play here.
Side note
So I am left wondering, as i rule things out, if it is the name of the ID that is the issue here. In the update the tables for AspNetUser has the table set as user_id not Id (Microsoft Owin Security - Claims Constructor Value cannot be null is what is leading me to think about this). With that said, I believe there is nothing wrong with the set up, here is what I have in my OnModelCreating(ModelBuilder builder),
builder.Entity<AppUser>().Property(p => p.Id).HasColumnName("user_id");
We have not had any issue up to this point on using it, but I'm running down all differences in the user that would be an issue root.
Update 3
after stepping through public class IdentityUserClaim<TKey> where TKey : IEquatable<TKey> and putting a break point on Claim ToClaim() the type is the null. Which is a duh on the log, but I can't seem to back track in the call stack where that roots from. My question sits at, if the DB query returns a proper set of types and values, then why is there a null type and value set to process first? The very first time that breakpoint is hit, the type is null.
UPDATE 4 major stop
after hitting every break point I am now stuck and wounder if i am hitting a bug here. I get in to the UserManager and follow the stack and can see that the main claims are there and ok.
And then the next part is to run the claims store which executes the query above, and at that point is when it fails. I can't see why. I run the query in SQL Manager and it is ok, not one NULL.
Does anyone see what is going on? I am at a loss at this moment.
UPDATE 5 - narrowing down
So failing to notice the locals when i hit the claim being set as null, I see that the values I want are in the scope of this
The issue here is now how is the scope off and I have the values wanted in the wrong place

The issue took a bit to walk around, but it is clear now why there was a failing. AppUserClaim is the real issue. It was hard to tell, and over looking the locals at that step, is why I had to go the long way around here.
The extended IdentityUserClaim<string> I had was
public class AppUserClaim : IdentityUserClaim<string>
{
public int Id { get; set; }
/// <summary>
/// Gets or sets the primary key of the user associated with this claim.
/// </summary>
public virtual string UserId { get; set; }
public virtual AppUser user { get; set; }
/// <summary>
/// Gets or sets the claim type for this claim.
/// </summary>
public virtual string ClaimType { get; set; }
/// <summary>
/// Gets or sets the claim value for this claim.
/// </summary>
public virtual string ClaimValue { get; set; }
}
But the properties of ClaimType and ClaimValue where not needing to be overwrote. Once I had done that, it set the values where they where out of scope for the methods. Changing the model to be
public class AppUserClaim : IdentityUserClaim<string> {
public virtual AppUser user { get; set; } // note this is the reason why i had to extend
}
Solves the issue. It did't appear in the other apps because claims where set from the SSO, so that method was always skipped.
Lessons for me to remember, always make sure you look at the locals when stepping through. It took me stepping away for a day before I saw the duh moment.

Related

How do I make the AspNetUsers table temporal?

As of Entity Framework Core 6.0 temporal tables are available. In the example, the blog post says it's as easy as adding IsTemporal():
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Customer>()
.ToTable("Customers", b => b.IsTemporal());
modelBuilder
.Entity<Product>()
.ToTable("Products", b => b.IsTemporal());
modelBuilder
.Entity<Order>()
.ToTable("Orders", b => b.IsTemporal());
}
I created a new ASP.NET Core 7 Razor Pages application via the Visual Studio 2022 template with Individual Accounts for authentication (in a Linux docker container, but I don't think that matters).
In the Data\Migrations\ folder, a migration called 00000000000000_CreateIdentitySchema.cs is automatically created for you. I tried to make the AspNetUsers table temporal by changing this line in 00000000000000_CreateIdentitySchema.Designer.cs:
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
// All the column and constraint definitions
b.ToTable("AspNetUsers", c => c.IsTemporal());
});
When I added the c => c.IsTemporal() argument as shown in the instructions and I try to run dotnet ef database update, I get a very vague error message:
Value cannot be null. (Parameter 'name')
The stack trace didn't help me any, but here it is for completeness:
Applying migration '00000000000000_CreateIdentitySchema'.
System.ArgumentNullException: Value cannot be null. (Parameter 'name')
at Microsoft.EntityFrameworkCore.Utilities.Check.NotEmpty(String value, String parameterName)
at Microsoft.EntityFrameworkCore.Metadata.Internal.EntityType.FindProperty(String name)
at Microsoft.EntityFrameworkCore.Metadata.Internal.EntityType.Microsoft.EntityFrameworkCore.Metadata.IEntityType.FindProperty(String name)
at Microsoft.EntityFrameworkCore.SqlServer.Metadata.Internal.SqlServerAnnotationProvider.For(IColumn column, Boolean designTime)+MoveNext()
at Microsoft.EntityFrameworkCore.Infrastructure.AnnotatableBase.AddAnnotations(AnnotatableBase annotatable, IEnumerable`1 annotations)
at Microsoft.EntityFrameworkCore.Infrastructure.AnnotatableBase.AddAnnotations(IEnumerable`1 annotations)
at Microsoft.EntityFrameworkCore.Metadata.Internal.RelationalModel.Create(IModel model, IRelationalAnnotationProvider relationalAnnotationProvider, IRelationalTypeMappingSource relationalTypeMappingSource, Boolean designTime)
at Microsoft.EntityFrameworkCore.Metadata.Internal.RelationalModel.Add(IModel model, IRelationalAnnotationProvider relationalAnnotationProvider, IRelationalTypeMappingSource relationalTypeMappingSource, Boolean designTime)
at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelRuntimeInitializer.InitializeModel(IModel model, Boolean designTime, Boolean prevalidation)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelRuntimeInitializer.Initialize(IModel model, Boolean designTime, IDiagnosticsLogger`1 validationLogger)
at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.FinalizeModel(IModel model)
at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.GenerateUpSql(Migration migration, MigrationsSqlGenerationOptions options)
at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.<>c__DisplayClass16_2.<GetMigrationCommandLists>b__2()
at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String connectionString, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabaseImpl(String targetMigration, String connectionString, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabase.<>c__DisplayClass0_0.<.ctor>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
If I remove c => c.IsTemporal(), dotnet ef database update succeeds, but the table is just a normal table.
The method call seems legit, so I'm not sure what's wrong here.
However, you can also override OnModelCreating, which, if possible, is always to be preferred over changing migration code. For types belonging to ASP.Net Identity, additional configuration should be done after the base call (although, in this case it's indifferent):
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<IdentityUser<Guid>>().ToTable("AspNetUsers", t => t.IsTemporal());
}
Where MyUser is derived from IdentityUser with, in this example, customized to have Guid as primary key type. Of course it also works with the default (primary key is string).
This creates a system-versioned AspNetUsers table in the database.

Restrict authorization or reduce result in case specific parameter arent satisfy requirements

Describing University via software I faced an auth issue.
Previously I had only a Headmaster role, which can do and access anything.
But as for now, I need to integrate a Teacher role.
The Teacher role should have an option to access certain features, which could be easily restricted by Authorize attribute. But in some cases, I want to reduce the number of data allowed to access for this role, e.g. not all students of the universe, but the ones who study Teacher's Subject.
All of this is already described in EF, (e.g. teacher-subject, subject-students relationships). But now I struggle to refuse (return 403) request for subject or students which are not allowed to access by Teacher.
I thought about a Specification pattern usage for my Services, so the resulting data will be reduced with a Specification's filter, as it helps reduce the data amount, sometimes to no-data, but didn't help to fully refuse a request.
Could you kindly provide me a link or an architectural idea to satisfy expectations for both use-cases specified above?
// entity models
class Subject {
...
public Teacher Teacher { get; set; }
public List<Students> { get; set; }
...
}
class Teacher {
...
public List<Subject> Subjects { get; set; }
...
}
class Student {
...
public List<Subject> StudiedSubjects {get; set; }
...
}
// first use-case I want to solve
public ActionResult<List<Student>> GetStudent()
{
// previously I just did
return Ok(_studentsService.GetStudents());
// but as for now in case of Teacher role accessed the method I want to
// reduce the number of returned students
}
// second use-case I want to solve
public ActionResult<Subject> GetSubjectDetails(int subjectId)
{
// previously I just did
return Ok(_subjectService.GetSubject(subjectId);
// but as for now in case of Teacher role I need to check whether its
// allowed to get the subject and return Access denied in case its not
}
For your first case, because the Action have no parameters at all, it'll make more sense to return Students that are accessible for a Teacher, or no Students at all if no one take all the subjects of certain Teacher, so 403 are not needed in this case. You could pass the User from controller or inject HttpContextAssessor to StudentService and use it for filtering.
as for your second case, its a perfect situation to return 403 if the SubjectId is not related to the Teacher in context. If you dont mind getting data from database for each request, you can use Requirement combined AuthorizationHandler in a Policy-Based Authorization by retrieving any data you need for authorization from database thus determine if the teacher has access to certain Subject(s). Steps to achieve it:
First setup the policy for the Teachers-Subjects relation and the handlers in Startup.ConfigureServices :
services.AddAuthorization(options =>
{
options.AddPolicy("TeacherSubject", policy => policy.Requirements.Add( new TeacherSubjectRequirement() ));
});
services.AddScoped<IAuthorizationHandler, TeacherSubjectHandler>();
next create the AuthorizationHandler for that policy:
public class TeacherSubjectHandler : AuthorizationHandler<TeacherSubjectRequirement>
{
readonly IHttpContextAccessor _contextAccessor;
readonly UserManager<AppUser> _usermanager;
readonly UserToTeacherService _userToTeacherService;
public ThePolicyAuthorizationHandler(IHttpContextAccessor c, UserManager<AppUser> u, _userToTeacherService s)
{
_contextAccessor = c;
_userManager = u;
_userToTeacherService = s;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext authHandlerContext, TeacherSubjectRequirement requirement)
{
var user = _userManager.GetUserAsync(_contextAccessor.HttpContext.User);
var teacher = _userToTeacherService(user); //I assume this service will also retrieve teacher's subjects
var subjectIds = teacher.Subjects.Select(s => s.SubjectId).ToList();
if (context.Resource is AuthorizationFilterContext filterContext)
{
var subjectIdStr = filterContext.RouteData.Values["id"].ToString();
if ( int.TryParse(subjectIdStr, out var subjectId) && subjectIds.Contains(subjectId) )
{
context.Succeed(requirement);
}
}
}
}
as for the Requirement class, its just an empty class:
public class TeacherSubjectRequirement: IAuthorizationRequirement
{
}
Since we're doing the authorization mechanism in AuthorizationHandler, we can leave this class empty. But it will still needed for the policy-based authorization to works.
And then for the policy to take effect, add attribute to the controller
[Authorize(Policy = "TeacherSubject")]
public ActionResult<Subject> GetSubjectDetails(int subjectId)
{
//existing code
}
But to be honest, I haven't tried putting policy-based attribute in an Action. If this doesn't work, putting the attribute in controller will surely works.
Hope this helps.
Your scenario is very practical which makes the question very interesting. But It would be nice for you to look at this documentation.
Use claims to give the teach access to the information they must be able to access. You can save a claim as .i.e. "TeacherName-TargetInforNameTheyCanAccess"; you can have as many claims as possible depending on how much information should be accessible by the teacher.
This goes the same for Students as well. You can create claims for Students that are under that Lecturers Class. Like say: "StudentName-LectureName" an you can then base your authentication by checking if the student is claimed to be under a specific lecturers class.

Entity Framework tracking issue with multiple objects of the same ID

I'm learning.
I'm using Microsoft.EntityFrameworkCore in a .netcoreapp 2.1, the point is to expose static data through an API.
Currently, I have the data in a json object. Lets keep the examples simple, I'm trying to expose a list of sandwhiches and ingredients for those sandwhiches. Sandwhiches come in various type (bagels, long, short, etc.)
First of all, I'm not entirely sure that using EF is the correct tool, but as I'll have to manage those items later on (being able to order food) I started there. If there is a better tool for it I'm all ears, but for now my question is to expose that using EF.
I'm reading the json that is hardcoded in the app and using it as a starting point for my DbContext. I just deserialize it in my constructor and load it up in my context object, which would then be exposed through the API. Works like a charm with the todo-list template project.
Here's what it looks like, I just added more DBSets for my needs
public class EatupContext : DbContext
{
public DbSet<FoodType> Types { get; set; }
public DbSet<Ingredient> Ingredients { get; set; }
public DbSet<FoodItem> Items { get; set; }
}
FoodType is an int with an ID and a name. Same for Ingredients.
Items are the sandwhiches and look like this
public class FoodItem
{
public int Id { get; set; }
public string Name { get; set; }
public FoodType Type { get; set; }
public IEnumerable<Ingredient> Ingredients { get; set; }
}
In the json that I'm reading (which is mapped like the c# objects), all id's start at 0 for all objects. So Types go from 0 to 7, ingredients from 0 to 105, and food items from 0 to 60.
This is causing an id tracking issue for the entity framework, because there are multiple objets with the same ID. Even though they're in different DBSets, which is what confuses me. From my (flawed?) understanding, two tables (DBSets?) can have duplicate id's. I can have a sandwich id 0, of type 0, with ingredients 0, 1, 2 and 3. It would seem even more confusing to me to have types going from 0 to 7, then ingredients from 8 to 113, and sandwiches from 114 to 174. That'd be just really odd to me in a database point of view.
Here is the exact error I am getting :
An unhandled exception occurred while processing the request.
InvalidOperationException: The instance of entity type 'Ingredient' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked.
When attaching existing entities, ensure that only one entity instance with a given key value is attached.
Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap<TKey>.ThrowIdentityConflict(InternalEntityEntry entry)
With the following stacktrace :
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap<TKey>.ThrowIdentityConflict(InternalEntityEntry entry)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap<TKey>.Add(TKey key, InternalEntityEntry entry, bool updateDuplicate)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, bool acceptChanges)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState entityState, bool acceptChanges, Nullable<EntityState> forceStateWhenUnknownKey)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode node, bool force)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph<TState>(EntityEntryGraphNode node, TState state, Func<EntityEntryGraphNode, TState, bool> handleNode)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph<TState>(EntityEntryGraphNode node, TState state, Func<EntityEntryGraphNode, TState, bool> handleNode)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState entityState, bool forceStateWhenUnknownKey)
Microsoft.EntityFrameworkCore.DbContext.SetEntityState(InternalEntityEntry entry, EntityState entityState)
Microsoft.EntityFrameworkCore.DbContext.SetEntityStates(IEnumerable<object> entities, EntityState entityState)
Microsoft.EntityFrameworkCore.DbContext.UpdateRange(IEnumerable<object> entities)
Microsoft.EntityFrameworkCore.Internal.InternalDbSet<TEntity>.UpdateRange(IEnumerable<TEntity> entities)
EatUp.Backend.Controllers.EatupController..ctor(EatupContext context) in EatupController.cs
+
_context.Items.UpdateRange(completeModel.Items);
lambda_method(Closure , IServiceProvider , object[] )
Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider+<>c__DisplayClass4_0.<CreateActivator>b__0(ControllerContext controllerContext)
Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider+<>c__DisplayClass5_0.<CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
And this happens in the controller, in the following code :
private readonly EatupContext _context;
public EatupController(EatupContext context)
{
_context = context;
var completeModel = JsonConvert.DeserializeObject<EatUpDataModel>(EatUpDataSet.Complete);
_context.Items.UpdateRange(completeModel.Items); //fails here
_context.Types.UpdateRange(completeModel.Types);
_context.Ingredients.UpdateRange(completeModel.Ingredients);
_context.SaveChanges();
}
It also fails if I use AddRange ; I'm using Update so I dont have to check if the set is empty or not, I just erase it with the latest data from the json.
I'm not sure what approach I should take from this, I really don't want to edit that json manually, but I don't see how I can tell EF that those are separate objects other than what I'm already doing.
EDIT:
I have edited all my id's manually to have only unique ones, and I still get the error.
The only time an ID appears twice, is when the same ingredient is used in different sandwiches, which should be acceptable.
Now i'm 200% confused, what am I missing ?
Generally you shouldn't need to tell EF that those are separate objects. It should work it out. Can you copy and paste your migration?

CreateUserIdenityAsync returns "UserId not found" exception for custom IdentityUser

I'm following along the bitoftech tutorial about creating Identity and role based claims with JWT. My application user is a custom User table with int PK.
Currently, the GenerateUserIdentityAsync method just returns a weird UserId not found error. here's my code:
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
and the implementation in User entity:
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<User, int> manager, string authenticationType)
{
//error on this line: CreateIdentityAsync throws error
var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
return userIdentity;
}
My UserManager class is defined like so:
public class AppUserManager : UserManager<User, int>
Weirdly enough, when I debug, the instance this in GenerateIdentityAsync does have a UserId property, but the base only has an id and I wonder if that is where it's erroring out? (it doesnt sound right)
I was looking at the source code (line 80) but I can't figure out where the exception is being thrown.
The exact exception being thrown is:
UserId not found.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details:
System.InvalidOperationException:
UserId not found.
And stack trace isn't all that helpful (to me)
How do I find out why / where the UserId is not available?
Mode details:
My GrantResourceOwnerCredentials():
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] {"*"});
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
User user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null) // this is NOT null
{
context.SetError("invalid_grant", "The username or password is incorrect");
return;
}
// this line fails
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
var ticket = new AuthenticationTicket(oAuthIdentity, null);
context.Validated(ticket);
}
And the ApplicationUser (which, in my case, is just User)
public partial class User : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public int UserId { get; set; }
public string Fullname { get; set; }
public string Address { get; set; }
public string ContactNumber { get; set; }
}
As you found out while debugging IdentityUser has an Id which in your case would represent the User's Id.
You need to remove the UserId from your User class, use the base Id from IdentityUser and rename the UserId column in your custom User table to Id.
Any properties you have in your User class needs to also have a matching column in your user table in the database. If not then you will get the same error for properties that do not match.
That would mean Fullname, Address and ContactNumber must have matching column names in the AspNetUsers table or else you will get the same error for those properties as well.
You have both UserId and Id properties in your User class - Id is inherited from IdentityUser. The problem is that you probably configured UserId to be the primary key for User.
The exception you get is thrown in ClaimsIdentityFactory.CreateAsync method, on line 97 UserManager.GetSecurityStampAsync. As you can see, user.Id used for retrieving a security stamp.
If you look inside UserManager.GetSecurityStampAsync you will see that the exception you get is thrown exactly here:
public virtual async Task<string> GetSecurityStampAsync(TKey userId)
{
ThrowIfDisposed();
var securityStore = GetSecurityStore();
var user = await FindByIdAsync(userId).WithCurrentCulture();
if (user == null)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.UserIdNotFound,
userId));
}
return await securityStore.GetSecurityStampAsync(user).WithCurrentCulture();
}
Thus, remove UserId property from User class and start using Id (inherited from IdentityUser) instead.
I faced exact same issue. After much of a head ache I could sort out the real problem.
When you change data type of the Id property of User class to string, a Guid().ToString() is assigned to the Id property in the constructor and the same value is saved to the database and Identity retrieves the user details using that value.
However, if you changed the data type of the Id property to int and did not provide a value for that property in the constructor, the Identity still tries to retrieve the User details by using the default int value (0) this causes throws the message "System.InvalidOperationException: UserId not found".
I solved this by retrieving the value from the database by command.ExecuteScalar() and assign the value to user.Id.
Hope this will help some one facing similar problem.
What does your ApplicationUser class look like?
What does this method look like in your application?
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context){}
Taiseer's comments about GrantResourceOwnerCredentials are:
"we are building an identity for the logged in user, this identity
will contain all the roles and claims for the authenticated user"
I had to add the ClaimTypes.NameIdentifier to the ClaimsIdentity to resolve a similar issue. Here is the important part of my GrantResourceOwnerCredentials method:
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
In my case, this error happened because I added another SignInManager inside the method Login in the Account Controller (following an example of adding a role). It was executing SignInManager.PasswordSignInAsync and SignInManager.SignInAsync in the same method and this calls GenerateUserIdentityAsync(...) in ApplicationUser twice, for which the first one succeeded and the second one gave me the exception "UserId" Not Found.
Server=xxxx;Initial Catalog=xxxx;;User ID=xxxx_user;Password=xxxx; where user id has a space in it as opposed to
Server=xxxx;Initial Catalog=xxxx;;UserID=xxxx_user;Password=xxxx; WRONG!!!!!!!
See https://www.connectionstrings.com/sql-azure/ for confirmation.

How can I get all users with ASP.NET Identity based on a property?

I trying something quite simple but for whatever reason it does not work, and for that reason I here to see if something gives me any light, and I could not find examples or solutions somewhere else (Bing/google)
The problem is quite simple I am using a slightly modified ApplicationUser class:
public class ApplicationUser : IdentityUser
{
public virtual Guid BusinessId { get; set; }
}
With the following helper (Extension method):
public static Guid GetBusinessId(this IIdentity user)
{
var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
var currentUser = manager.FindById(user.GetUserId());
return currentUser.BusinessId;
}
Then finally the method that is "causing" the issue:
public IQueryable<ApplicationUser> Get()
{
var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
Guid businessId = this.User.Identity.GetBusinessId();
var users = userManager.Users.Where(u => u.BusinessId == businessId);
return users;
}
What happens here is that when I query Users, I get nothing but it should at least get the current logged in User, since he has the proper businessId.
If I put a break point on the users query and try anything like "ToList()" or "ToList()[0]" on the immediate window I get the following:
?users.ToList()
Count = 1
[0]: Could not evaluate expression
?users.ToList()[0]
An internal error has occurred while evaluating method System.Collections.Generic.List`1[T].get_Item().
Any ideas why, or is there a "right" way of doing this?
Thanks
have a look here How to obtain a list of Users from ASP.NET Identity? qouted from answer
I found out that I wasn't using the derived ApplicationUser object for anything, so I just went ahead and changed all uses of it for
plain old User. Then I just changed ApplicationDbContext definition
for the following:
public class ApplicationDbContext : IdentityDbContext<
User, UserClaim, UserSecret, UserLogin,
Role, UserRole, Token, UserManagement>
{
}
And now I can access the user list:
UsersContext = new ApplicationDbContext();
...
UsersContext.Users.ToList();
However, I think this will come back and haunt me in the future (I'll
probably need to add more fields to User) so probably I'll have to use
the same approach as in this question:
Get all role names in ASP.NET MVC5 Identity system
Edit: Since I got the need to add a new property, I had to revert my
changes. So I went ahead and did a line by line comparison with the
ASP.NET Identity Sample Project, and found out that the generated
project had the following line:
IdentityManager = new AuthenticationIdentityManager(new IdentityStore());
while the Sample application had included the database context in the
constructor. So I added it in my constructor, recreated the database
and the problem went away.

Categories