How do I refer to ASP.NET identity Usermanager inside of a static class? - asp.net-core

I want to create a new user during the seed method of the program but how do I call CreateAsync??
public class DbInitializer
{
private readonly UserManager<ApplicationUser> _userManager;
public DbInitializer(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public static void Initialize(IApplicationBuilder app)
{
OneDbContext context = app.ApplicationServices.GetRequiredService<OneDbContext>();
context.Database.EnsureCreated();
//If there are no users, create a test user
if (!context.Users.Any())
{
var user = new ApplicationUser { UserName = "test", Email = "test#test.com" };
var result = _userManager.CreateAsync(user, "test");
}
error CS0120: An object reference is required for the non-static field, method, or property 'DbInitializer._userManager'

Related

Admin lock or unlock account user in .Net Core

I am doing the management of a user's account when necessary I can Lock a user's account in case they violate it. Or can be unlocked if required. I got an error like this. Where am I wrong, I use .Net Core 5 to build my program. Error: "An unhandled exception occurred while processing the request.
NullReferenceException: Object reference not set to an instance of an object."
enter image description here
Interface
public bool LockUser(string email);
public bool UnlockUser(string email);
Repo
public bool LockUser(string email)
{
var userTask = _userManager.FindByEmailAsync(email);
userTask.Wait();
var user = userTask.Result;
var lockUserTask = _userManager.SetLockoutEnabledAsync(user, true);
lockUserTask.Wait();
var lockDateTask = _userManager.SetLockoutEndDateAsync(user, DateTimeOffset.Now);
lockDateTask.Wait();
return lockDateTask.Result.Succeeded && lockUserTask.Result.Succeeded;
}
Controller
public ActionResult LockUser(string email)
{
if (!_userRepository.LockUser(email))
{
throw new ArgumentException("Error");
}
return RedirectToAction("Index");
}
Please refer the following sample code, the UserRepository should like this, add the usermanager via the constructor parameter:
public interface IUserRepository
{
public bool LockUser(string email);
public bool UnlockUser(string email);
}
public class UserRepository : IUserRepository
{
private readonly UserManager<IdentityUser> _userManager;
public UserRepository(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
public bool LockUser(string email)
{
var userTask = _userManager.FindByEmailAsync(email);
userTask.Wait();
var user = userTask.Result;
var lockUserTask = _userManager.SetLockoutEnabledAsync(user, true);
lockUserTask.Wait();
var lockDateTask = _userManager.SetLockoutEndDateAsync(user, DateTimeOffset.Now);
lockDateTask.Wait();
return lockDateTask.Result.Succeeded && lockUserTask.Result.Succeeded;
}
public bool UnlockUser(string email)
{
//...
throw new NotImplementedException();
}
}
Then, add the service to the service container:
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddScoped<IUserRepository, UserRepository>();
services.AddControllersWithViews();
}
Then, in the MVC controller:
public class HomeController : Controller
{
private readonly IUserRepository _userRepository;
public HomeController(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public IActionResult Index(int id)
{
string email = "aa#hotmail.com";
if (!_userRepository.LockUser(email))
{
throw new ArgumentException("Error");
}
return View();
}
The debug screenshot like this:

IdentityServer4 Reject Token Request If Custom Parameter Not Valid

I have this test client sending RequestToken:
var tokenResponse = await client.RequestTokenAsync(new TokenRequest
{
Address = disco.TokenEndpoint,
GrantType = "password",
ClientId = "My_Client",
ClientSecret = "mysecret",
Parameters =
{
{ "username", "user#entity.com" },
{ "password", "userpassword" },
{ "logged_entity_id", "143" },
{ "scope", "MyAPI" }
}
});
Now each user has a list of entity and I want to reject the token request if the value in the parameter "logged_entity_id" does not exist in the user's list of entity.
I was initially planning on checking it via IsActiveSync in my CustomProfileService but I can't seem to access the raw parameters in IsActiveSync method.
public class CustomProfileService : IProfileService
{
protected UserManager<User> _userManager;
public CustomProfileService(UserManager<User> userManager)
{
_userManager = userManager;
}
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var claims = new List<Claim>
{
new Claim("LoggedEntityId", context.ValidatedRequest.Raw["logged_entity_id"])
};
context.IssuedClaims.AddRange(claims);
return Task.FromResult(0);
}
public Task IsActiveAsync(IsActiveContext context)
{
var user = _userManager.GetUserAsync(context.Subject).Result;
// var entityId = Can't access logged_entity_id parameter here
context.IsActive = user != null && user.DeletingDate == null && user.entities.Contains(entityId);
return Task.FromResult(0);
}
}
I'm not really sure if this is where I should check and reject it.
In asp.net core you can register a dependency using the built-in dependency injection container. The dependency injection container supplies the IHttpContextAccessor to any classes that declare it as a dependency in their constructors:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpContextAccessor();
...
}
Then in your class ,for example , in the implement of IProfileService :
private readonly IHttpContextAccessor _httpContextAccessor;
public CustomProfileService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
Then in IsActiveAsync method get the value by :
var id = _httpContextAccessor.HttpContext.Request.Form["logged_entity_id"].ToString();
You can implement ICustomTokenValidator to validate token's request on your own way
You can run custom code as part of the token issuance pipeline at the token endpoint. This allows e.g. for
adding additional validation logic
changing certain parameters (e.g.token lifetime) dynamically
public class CustomValidator : ICustomTokenRequestValidator
{
public Task<TokenValidationResult> ValidateAccessTokenAsync(TokenValidationResult result)
{
throw new NotImplementedException();
}
public Task<TokenValidationResult> ValidateIdentityTokenAsync(TokenValidationResult result)
{
throw new NotImplementedException();
}
}
and in your startup.cs:
services.AddIdentityServer(options =>
{
...
})
.AddCustomTokenRequestValidator<CustomValidator>();

IdentityServer4: How to specify an identity provider as part of authorization request?

I've implemented IdentityServer4 with external providers in my project. Now when a restricted page is requested, the user is redirected to my IdentityServer login page, where he can either enter a username and password or login with Google or Facebook. How can I specify which identity provider to be used from the client side so that, my identity server will directly redirect to the particular provider without showing the login page?
You can pass custom parameter to the authorize endpoint .
If you are using the OpenID Connect Middleware , you can add the value to query string of authorize request of OnRedirectToIdentityProvider function :
options.Events.OnRedirectToIdentityProvider = async n =>
{
var headerValue = n.HttpContext.Request.Headers["X-idp"];
n.ProtocolMessage.SetParameter("X-idp", headerValue.ToString());
await Task.FromResult(0);
};
You could create custom CustomAuthorizeAttribute to pass the identity provider you want to login :
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class CustomAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
private readonly string _idp;
public CustomAuthorizeAttribute(string idp)
{
_idp = idp;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
context.HttpContext.Request.Headers.Add("X-idp", _idp);
}
}
In your controller :
[CustomAuthorizeAttribute("AAD")]
So that on Identity Server side , you could get the needed Identity provide information via query string :
AccountController.cs(ASP.Net Identity):
[Authorize]
[Route("[controller]/[action]")]
public class AccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailSender _emailSender;
private readonly ILogger _logger;
private readonly IIdentityServerInteractionService _interaction;
public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IEmailSender emailSender,
ILogger<AccountController> logger, IIdentityServerInteractionService interaction)
{
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
_logger = logger;
_interaction = interaction;
}
[TempData]
public string ErrorMessage { get; set; }
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> Login(string returnUrl = null)
{
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
var idp = context.Parameters["X-idp"];
var redirectUrl = Url.Action(nameof(ExternalLoginCallback), "Account", new { returnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(ipd, redirectUrl);
return Challenge(properties, idp);
//var customId = HttpContext.Request.Query["X-CustomId"].ToString();
//var queryString = HttpContext.Request.Query["returnUrl"].ToString();
//// Clear the existing external cookie to ensure a clean login process
//await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
//ViewData["ReturnUrl"] = returnUrl;
//return View();
}
.....
}
In above code sample , it uses IIdentityServerInteractionService method GetAuthorizationContextAsync to get the value , if you have external provider like :
services.AddAuthentication()
.AddOpenIdConnect("AAD", "Azure Active Directory", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.SignOutScheme = IdentityServerConstants.SignoutScheme;
options.Authority = "https://login.microsoftonline.com/xxxx.onmicrosoft.com";
options.ClientId = "xxxxxxxxxxxxxxxxxxxxxxxxxx";
options.Scope.Add("openid");
});
It will find that authentication schema by name AAD and start the Azure AD login process .

AspNetCore 2 oData - Missing context on get all identity users

I trying to create a web api with oData v4.
Now i try to get all Identity-User over oData.
This is working:
[EnableQuery]
public class UsersController : Controller
{
protected readonly UserManager<User> _userManager;
public UsersController(UserManager<User> userManager)
{
_userManager = userManager;
}
private static List<User> _users = new List<User>
{
new User { Id = 1, Name = "Flo", Email = ""},
new User { Id = 2, Name = "Felix", Email = ""},
new User { Id = 3, Name = "Andreas", Email = ""},
new User { Id = 4, Name = "Marco", Email = ""}
};
public IQueryable<User> Get()
{
return _users.AsQueryable();
}
}
And return this response:
{"#odata.context":"http://localhost:55503/oData/$metadata#Users(Id)","value":[{"Id":1},{"Id":2},{"Id":3},{"Id":4}]}
When i change the controller to return all Identity-Users this isn't working correctly.
[EnableQuery]
public class UsersController : Controller
{
protected readonly UserManager<User> _userManager;
public UsersController(UserManager<User> userManager)
{
_userManager = userManager;
}
public IQueryable<User> Get()
{
return _userManager.Users.AsQueryable();
}
}
And it returns this response:
[{"Id":"35909773-8b53-4d68-a770-b7cdfcffd0de"}]
But the response is missing the context. Can you give my a hint why?
I solved the problem:
var builder = new ODataConventionModelBuilder(serviceProvider);
builder.EntitySet<User>("Users");
return builder.GetEdmModel();

Getting IHostingEnvironment.ContentRootPath inside DatabaseInitializer

I'm trying to seed my db with some data stored in json files.
I need to inject an IHostingEnvironment inside my IDatabaseInitializer.Seed() method so I can read the json files using IHostingEnvironment.ContentRootPath.
This property is injected by the main container by default but the constructor of an DbConfiguration must be parameterless, so I can't pipe IHostingEnvironment through DbConfiguration into SetDatabaseInitializer(new DatabaseInitializer()).
/*
* Database Context
*/
[DbConfigurationType(typeof(DatabaseConfiguration))]
public class DatabaseContext : DbContext
{
public DbSet<User> Users { get; set; }
public DatabaseContext(string nameOrConnectionString) : base(nameOrConnectionString) { }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<System.Data.Entity.ModelConfiguration.Conventions.PluralizingTableNameConvention>();
base.OnModelCreating(modelBuilder);
}
}
/*
* Database Configuration
*/
public class DatabaseConfiguration : DbConfiguration
{
// Can't receive injected IHostingEnvironment env because constructor must be parameterless
public DatabaseConfiguration()
{
SetProviderServices("System.Data.SqlClient", SqlProviderServices.Instance);
// Could pass IHostingEnvironment through constructor
SetDatabaseInitializer(new DatabaseInitializer());
}
}
/*
* Database Initializer
*/
public class DatabaseInitializer : DropCreateDatabaseAlways<DatabaseContext>
{
private readonly IHostingEnvironment env;
// Receives IHostingEnvironment from DatabaseConfiguration
public DatabaseInitializer(IHostingEnvironment env)
{
this.env = env;
}
protected override void Seed(DatabaseContext context)
{
// Read some .json files
}
}
I'm initializing my db, and had to write a separate class, which I call from configure, you could do the same thing and pass in the path as a parameter
Here's how I call it:
using (var scope = scopeFactory.CreateScope())
{
// Initialise Database
var initializer = scope.ServiceProvider.GetService<IDbInitializer>();
initializer.SeedAsync().Wait();
// initialize plugin manager
var manager = scope.ServiceProvider.GetService<IPluginManager>();
manager.Initialize(dbConnection);
if (Configuration.GetSection("PluginService").GetValue<bool>("RunAtStartup") == true)
manager.Start();
}
and here's the db initialize class
public interface IDbInitializer
{
Task SeedAsync();
}
public class DbInitializer : IDbInitializer
{
private ApplicationDbContext _context;
private RoleManager<IdentityRole> _roleManager;
private UserManager<ApplicationUser> _userManager;
public DbInitializer(ApplicationDbContext context,
RoleManager<IdentityRole> roleManager,
UserManager<ApplicationUser> userManager)
{
_context = context;
_roleManager = roleManager;
_userManager = userManager;
}
public async Task SeedAsync()
{
await CreateRolesAsync();
await CreateAdminUserAsync();
await SeedMenuAsync();
}
private async Task CreateRolesAsync()
{
List<IdentityRole> roles = new List<IdentityRole>();
roles.Add(new IdentityRole { Name = "Admin", NormalizedName = "ADMINISTRATOR" });
roles.Add(new IdentityRole { Name = "Member", NormalizedName = "MEMBER" }); // An email confirmed memeber
roles.Add(new IdentityRole { Name = "Guest", NormalizedName = "GUEST" }); // Used for a user that has only checked out as guest
roles.Add(new IdentityRole { Name = "NotConfirmed", NormalizedName = "NOTCONFIRMED" }); // Used when a guest hasnt confirmed there registration
foreach (var role in roles)
{
var roleExists = await _roleManager.RoleExistsAsync(role.Name);
if (!roleExists)
{
await _roleManager.CreateAsync(role);
}
}
}
...
Hope that helps