.NET MVC5 Application using Identity 2.
I need to call a method of my service at application startup, but the method needs to access the database. where and how can I do this?
IdentityModel.cs is the place needs modification
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit https://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
instead of default connection, you can create multiple environment variables. Hope this answered your question
Related
I have a Core 2.0 project that has reached the point where I need to start linking users to the data I have. It started as a one user test bed that now requires me to have multiple per user.
User Model:
public class ApplicationUser : IdentityUser
{
public virtual ICollection<Balance> Balances { get; set; }
}
Balance model:
public virtual ApplicationUser User { get; set; }
...
ApplicationDbContext.cs:
//Users to balances
builder.Entity<ApplicationUser>()
.HasMany(b => b.Balances)
.WithOne(u => u.User);
In my code, when creating a new Balance, I cannot figure out how to link my current logged in user:
var myNewBalance = new Balance();
myNewBalance.User = User;
^ boom
How do I relate Balances to the currently logged in user?
The User property you've highlighted in your question is a ClaimsPrincipal that belongs to the ControllerBase class. The User property on your Balance class is an ApplicationUser, which is not compatible with ClaimsPrincipal. You already know this, but I'm just making it explicit in order to explain what you need to do.
As you are using ASP.NET Core Identity here, you can use the UserManager class, which is available using dependency injection. UserManager contains a function, GetUserAsync, that can convert your ClaimsPrincipal into the corresponding ApplicationUser.
Here's some code:
public class SomeController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
public SomeController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public async Task<IActionResult> SomeAction()
{
var myNewBalance = new Balance();
myNewBalance.User = await _userManager.GetUserAsync(User);
return View(); // etc, etc.
}
}
The key points are the use of the generic UserManager that uses ApplicationUser and then the invocation of GetUserAsync using the User (ClaimsPrincipal) property.
I am using Identity in a project that has extended properties In ApplicationUser class as OrganisationId.
I am using ApplicationUserManager to read user details.
here is my ApplicationUser class:
public class ApplicationUser
: IdentityUser<int, ApplicationUserLogin,
ApplicationUserRole, ApplicationUserClaim>, IUser<int>
{
public async Task<ClaimsIdentity>
GenerateUserIdentityAsync(UserManager<ApplicationUser, int> manager)
{
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
userIdentity.AddClaim(new Claim("OrganisationId", OrganisationId.ToString()));
return userIdentity;
}
public int OrganisationId { get; set; }
}
And in the AccountController class I inject the ApplicationUserManager object.
Then user manager object gives me a way to find whether a user in role or not.
var isUserAdmin = await _userManager.IsInRoleAsync(userId, adminRoleName);
But what I need is a way to find all users whom has admin rights in an organisation.
Something like this:
_userManager.Users.Where(u=>u.OrganisationId=1 && u.Roles.Contains(adminRole))
But this doesnt work as Roles is collection of ApplicationUserRole.
Any idea how I could manage to get all the admin users in an organisation?
Just use:
u.Roles.Any(m => m.RoleId == adminRole.Id)
Instead.
I tried to extend the asp.net identity user with a "Name" property, I therefore followed the description in this post How to extend available properties of User.Identity
But after I did that and tried to login I get this error "The model backing the 'ApplicationDbContext' context has changed since the database was created. Consider using Code First Migrations to update the database"
Can I fix this or can you only extend the asp.net ueser identity before the database is created the first time?
Based on Asp.Net template database will generate the bellow structure:
In order to extend asp.net identity user and keep the things simple, please update IdentityModels.cs with following code:
//CODE
using System.Data.Entity;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System.Data.Entity.Migrations;
namespace WebApplication.Models
{
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
public string Name { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<ApplicationDbContext, ApplicationDbContextConfiguration>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<IdentityRole>().ToTable("AspNetRoles");
modelBuilder.Entity<IdentityUserRole>().ToTable("AspNetUserRoles");
modelBuilder.Entity<IdentityUserLogin>().ToTable("AspNetUserLogins");
modelBuilder.Entity<IdentityUserClaim>().ToTable("AspNetUserClaims");
modelBuilder.Entity<ApplicationUser>().ToTable("AspNetUsers");
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
internal sealed class ApplicationDbContextConfiguration : DbMigrationsConfiguration<ApplicationDbContext>
{
public ApplicationDbContextConfiguration()
{
ContextKey = "WebApplication.Models.ApplicationDbContext"; //Retrieved from the database table dbo.__MigrationHistory
#if DEBUG
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
#else
AutomaticMigrationsEnabled = false;
AutomaticMigrationDataLossAllowed = false;
#endif
}
protected override void Seed(ApplicationDbContext context)
{
base.Seed(context);
}
}
}
The output is:
PS: You can rename default names for asp.net generated tables, personally I prefer to remove AspNet suffix
public class MyContext: IdentityDbContext<ApplicationUser>
{
public MyContext()
: base("MyContext", throwIfV1Schema: false)
{
}
public static MyContext Create()
{
return new MyContext();
}
public System.Data.Entity.DbSet<xxxx> xxxxx { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
Startup.Auth:
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(MyContext.Create); // ALTERED THIS
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create); // ALTERED THIS
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}
}
ApplicationUser:
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
// this is the old code, no longer referenced in startup.auth:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("MyContext", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
So basically, Ive set my new context to inherit from IdentityDbContext, and replaced any reference of the old ApplicationDbContext with my new one. It creates the Identity tables, but none of my DbSet tables I specify (omitted, left one crossed out for sample
I'm just guessing here because I can't see your code, but I'm reasonably sure I'm correct. When you start a new project, you have to enable migrations. How this works is that it inspects your project for objects derived from DbContext and then creates a Migrations folder with a Configuration.cs file inside. This file explicitly references the context that is used for migrations. If you do this with more than one object derived from DbContext in your project (here, you have two, MyContext and ApplicationDbContext), it will fuss at you and tell you that you need to specify which one to use. This probably didn't happen because you had previously enabled migrations with the generated context from Identity, and only later added your own context.
Long and short, check this configuration file and change any offending references there. Or, you can just delete the Migrations folder completely and run Enable-Migrations again in the package manager console, making sure to specify MyContext as the context to be used.
Annoyingly small changes:
no throwIfV1Schema: false
public class MyContext: IdentityDbContext<ApplicationUser>
{
public MyContext() : base("MyContext")
{
}
public static MyContextCreate()
{
return new MyContext();
}
public System.Data.Entity.DbSet<xxx> xxx{ get; set; }
}
I have 3 projects in my solution, Domain, API and Web. Recently I've updated Asp.net Identity form 1.0 to 2.0. And everything works fine in my Web project but when I try to get Token in Web API project I get this error (an before upgrading Identity from 1.0 to 2.0 everything worked):
The entity type User is not part of the model for the current context
My User class looks like this:
public class User : IdentityUser
{
//more code here
}
Here is My Database context class:
public class DatabaseContext : IdentityDbContext<User>
{
public DatabaseContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
Configuration.LazyLoadingEnabled = true;
}
//more code here
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<IdentityUser>()
.ToTable("AspNetUsers");
modelBuilder.Entity<User>()
.ToTable("AspNetUsers");
}
}
In my Web API project I've replaced all references from IdentityUser to User, for example method for getting Token looks like:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
using (UserManager<User> userManager = _userManagerFactory())
{
var user = userManager.Find(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await userManager.CreateIdentityAsync(user,
context.Options.AuthenticationType);
ClaimsIdentity cookiesIdentity = await userManager.CreateIdentityAsync(user,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
}
How can I fix this ?
I've fixed it,
So the problem was with updating Asp.net Identity from 1.0 to 2.0 as ASP.net Web API handles things bit differently for 2.0 so what I did was:
Updated VS 2013 (Update 2)
Created new Web API 2 project in another solution, and compared it with my Web API project
And added missing classes to it (just copy/paste)
To my DatabaseContext class I've added method:
public static DatabaseContext Create()
{
return new DatabaseContext();
}
And to my User class:
public async Task GenerateUserIdentityAsync(UserManager manager, string authenticationType)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
// Add custom user claims here
return userIdentity;
}
And I've changed all ApplicationUser references in Web API project with my User reference