I Need to initiate the password reset from my web api. I have identity server application and web api applications separately. I use OIDC client to communicate with identity server. With this client I can only call signin, signout and some standard methods.
What I need is to generate password reset token and get the reset token in API.
I have tried to include 'Microsoft.Extensions.Identity.Core' in my webapi core layer which have all the entities. But I get
Unable to resolve service for type
Microsoft.AspNetCore.Identity.IUserStore1[Application.Core.Entities.User]
while attempting to activate 'Microsoft.AspNetCore.Identity.UserManager
code
private readonly UserManager<User> _userManager;
public AccountController(UserManager<User> userManager)
{
_userManager = userManager;
}
private async Task<string> GeneratePasswordResetLinkAsync(User user)
{
string token = await _userManager.GeneratePasswordResetTokenAsync(user);
return token;
}
I've also tried adding below code in startup Injection
services.AddScoped<UserManager<User>, UserManager<User>>();
What is the proper way of injecting UserManager in my web api ?
You need to configure asp.net core identity in the Startup class of you web api. Unfortunately you can not simply call services.AddIdentity(... in ConfigureServices because behind the scenes a cookie-based authentication scheme is registered and set as the default challenge scheme, as you can see in the code here for asp.net core 2.2
or here for asp.et core 3.1.
Thus the solution I end up with is to copy & update AddIdentity method like this:
For ASP.NET CORE 2.2:
public static IdentityBuilder AddIdentityForWebApi<TUser, TRole>(
this IServiceCollection services,
Action<IdentityOptions> setupAction)
where TUser : class
where TRole : class
{
// Hosting doesn't add IHttpContextAccessor by default
services.AddHttpContextAccessor();
// Identity services
services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
services.TryAddScoped<ILookupNormalizer, UpperInvariantLookupNormalizer>();
services.TryAddScoped<IRoleValidator<TRole>, RoleValidator<TRole>>();
// No interface for the error describer so we can add errors without rev'ing the interface
services.TryAddScoped<IdentityErrorDescriber>();
services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
services.TryAddScoped<ITwoFactorSecurityStampValidator, TwoFactorSecurityStampValidator<TUser>>();
services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser, TRole>>();
services.TryAddScoped<UserManager<TUser>>();
services.TryAddScoped<SignInManager<TUser>>();
services.TryAddScoped<RoleManager<TRole>>();
if (setupAction != null)
{
services.Configure(setupAction);
}
return new IdentityBuilder(typeof(TUser), typeof(TRole), services);
}
For ASP.NET CORE 3.1:
static IdentityBuilder AddIdentityForWebApi<TUser, TRole>(
this IServiceCollection services,
Action<IdentityOptions> setupAction)
where TUser : class
where TRole : class
{
// Hosting doesn't add IHttpContextAccessor by default
services.AddHttpContextAccessor();
// Identity services
services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
services.TryAddScoped<ILookupNormalizer, UpperInvariantLookupNormalizer>();
services.TryAddScoped<IRoleValidator<TRole>, RoleValidator<TRole>>();
// No interface for the error describer so we can add errors without rev'ing the interface
services.TryAddScoped<IdentityErrorDescriber>();
services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
services.TryAddScoped<ITwoFactorSecurityStampValidator, TwoFactorSecurityStampValidator<TUser>>();
services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser, TRole>>();
services.TryAddScoped<IUserConfirmation<TUser>, DefaultUserConfirmation<TUser>>();
services.TryAddScoped<UserManager<TUser>>();
services.TryAddScoped<SignInManager<TUser>>();
services.TryAddScoped<RoleManager<TRole>>();
if (setupAction != null)
{
services.Configure(setupAction);
}
return new IdentityBuilder(typeof(TUser), typeof(TRole), services);
}
Then you should call AddIdentityForWebApi in the Startup of your web api application.
This will register the UserManager and it will now be injected in your controller's constructor.
Then you should configure Data Protection API (DPAPI) properly so that the token generated by your web api (when you call _userManager.GeneratePasswordResetTokenAsync... from your question) could be unprotected by your identity server application.
So I set the "DPAPI application name" across the two applications, in both ConfigureServices methods:
services.AddDataProtection()
.dataProtectionBuilder.SetApplicationName("YOUR_DPAPI_APPLICATION_NAME");
For production, in a web farm environment, you will have to share the DPAPI keys. Depending on your situation you have several options.
Please refer to the official documentation for more details on how to configure DPAPI.
I have tested on Kestrel running Windows, I am not sure about IIS.
The injection code does not look correct. Try changing it to
services.AddScoped<UserManager<Application.Core.Entities.User>>();
Related
It is possible to have host address in app services?
For example we want send email to customer with specific link point to site address. How is this possible?
This came up via Google & the existing answer didn't really help. I don't necessarily agree that app services are the wrong domain for this; in ABP for example, a service is closely connected to a controller and services usually only exist in order to service web requests there. They often execute code in an authorised state that requires a signed-in user, so the whole thing is happening in the implicit domain context of an HTTP request/response cycle.
Accordingly - https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-context?view=aspnetcore-2.2#use-httpcontext-from-custom-components
Add services.AddHttpContextAccessor(); to something like your Startup.cs - just after wherever you already call services.AddMvc().
Use dependency injection to get hold of an IHttpContextAccessor in your service - see below.
Using constructor-based dependency injection, we add a private instance variable to store the injected reference to the context accessor and a constructor parameter where the reference is provided. The example below has a constructor with just that one parameter, but in your code you probably already have a few in there - just add another parameter and set _httpContextAccessor inside the constructor along with whatever else you're already doing.
using HttpContext = Microsoft.AspNetCore.Http.HttpContext;
using IHttpContextAccessor = Microsoft.AspNetCore.Http.IHttpContextAccessor;
// ...
public class SomeService : ApplicationService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public SomeService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
}
Now service code can read the HTTP context and from there things like the HTTP request's host and port.
public async Task<string> SomeServiceMethod()
{
HttpContext context = _httpContextAccessor.HttpContext;
string domain = context.Request.Host.Host.ToLowerInvariant();
int? port = context.Request.Host.Port;
// ...
}
HttpContext.Current.Request.Url
can get you all the info on the URL. And can break down the url into its fragments.
I'm writing a service in Nancy and I'm using some middleware for monitoring the service:
app.UseOwin(buildFunc =>
{
var log = ConfigureLogger();
buildFunc.UseMonitoringAndLogging(log, HealthCheck);
buildFunc.UseNancy();
});
The middleware is configured to use a HealthCheck() function defined in the Startup class as:
public async Task<bool> HealthCheck()
{
return await SomeRepo.HealthCheck();
}
SomeRepo has a HealthCheck() method that queries the database to confirm it is available/responding. But how to inject SomeRepo into the Startup class, or alternatively access the container to resolve SomeRepo?
At this point you are still in Owin and not in the Nancy pipeline. What host are you running on ? If you are using aspnetcore the you can register your deps in RegisterServices() method and it will handle the injection into your method. You can use Autofac or StructureMap so you can share your container with Nancy as well like this.
In previous versions of ASP.NET, if I wanted to have a custom class as my current logged in user, what I did was: I let the FormsAuthentication module do its work, and then, in the PostAuthenticateRequest event I replaced the current Principal (HttpContext.Current.User) with my custom principal object that I fetched from the database (with some caching for performance).
How can I achieve the same in ASP.NET Identity? I have my own ApplicationUser (not the default that comes with the EntityFramework ASP.NET Identity) and my own UserStore.
In every authenticated request, I have the HttpContext.User as a ClaimsPrincipal object. Is there a way to replace that with my CustomClaimsPrincipal?
Is there another, better way, to retrieve the current ApplicationUser instance based on the current ClaimsPrincipal?
If you have your own IUserStore you can implement IUserClaimStore to customize the claims identity which is passed to the claims principal.
If you need to replace the default claims principal you should implement the IUserClaimsPrincipalFactory and pass your implementation to the SignInManager and register the configured manager to your owin context.
It should look like this along the lines.
(Assuming you are using ASP.NET Core Identity, for Identity v2 the interfaces and constructors may differ!)
class CustomClaimsFactory<TUser> : Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory<TUser>
where TUser : class
{
public Task<ClaimsPrincipal> CreateAsync(TUser user)
{
// create and return your custom principal
}
}
class OwinStartup
{
public void Configuration(IAppBuilder app)
{
app.CreatePerOwinContext(CreateSignInManager);
}
Microsoft.AspNetCore.Identity.SignInManager CreateSignInManager()
{
UserManager manager; // the manager that uses your custom IUserStore
IHttpContextAccessor accessor; // I don't know where to get it from...
var factory = new CustomClaimsFactory();
return new SignInManager(manager, accessor, factory, null, null, null);
}
}
For ASP.Net Core the OWIN-like startup configuration is done via dependency injection.
My web api looks like:
[Route("api/[controller]")]
public class ValuesController : Controller
{
private readonly ApplicationDbContext _context;
public ValuesController(ApplicationDbContext context)
{
_context = context;
}
[HttpGet]
public string Get()
{
// get random user
var user = _context.Users.SingleOrDefault();
return user?.Email ?? "";
}
}
Trying to call it via jquery:
$.get('/api/values', function (email) {
console.log(email)
})
I've 2 questions:
1/. Why didn't console.log(email) work although I was getting the response successful?
There was nothing in the Console tab.
2/. When I made a request to server (/api/values), the breakpoint had been caught:
My question: where had ValuesController been called within context? I'd sent a request from client, then the constructor was hit (I'm sure that I didn't send something like ApplicationDbContext from client to server :v)
UPDATE: By changing $.get('/api/values', {}, function (email) {} to $.get('/api/values', function (email) {}. I've fixed the first problem. It's my bad. Sorry about that.
Shorter Answer
My question: where had ValuesController been called within context? I'd sent a request from client, then the constructor was hit.
The HTTP Request arrived. ASP.NET MVC Routing told the application to use the ValuesController to handle the request. The application constructed the ValuesController, supplying an instance of ApplicationDbContext via dependency injection.
Longer Answer
The Startup.Configure method is used to specify how the ASP.NET application will respond to individual HTTP requests. Since you are using Web API, you have configured app.UseMvc(). Result: when an HTTP Request arrives, MVC Routing tells the application to use the appropriate controller.
The Startup.ConfigureServices method is used to specify services that are available via dependency injection. Since you are injecting an ApplicationDbContext into your constructor, you have configured services.AddDbContext<ApplicationDbContext>(). Result: when the application constructs a ValuesController, ASP.NET Dependency Injection will provide an instance of the ApplicationDbContext.
I am trying to setup Integration tests with my IIS Hosted WebAPI 2.2 application. I use Autofac for DI and I am using the new ASP.net Identity stack which uses OWIN. I am running into an issue with Autofac where the HttpContext class is always null. Here is how I am setting up my base integration test class-
[TestClass]
public class TestBase
{
private SimpleLifetimeScopeProvider _scopeProvider;
private IDependencyResolver _originalResolver;
private HttpConfiguration _configuration;
public TestServer Server { get; private set; }
[TestInitialize]
public void Setup()
{
Server = TestServer.Create(app =>
{
//config webpai
_configuration = new HttpConfiguration();
WebApiConfig.Register(_configuration);
// Build the container.
var container = App_Start.IocConfig.RegisterDependencies(_configuration);
_scopeProvider = new SimpleLifetimeScopeProvider(container);
//set the mvc dep resolver
var mvcResolver = new AutofacDependencyResolver(container, _scopeProvider);
_originalResolver = DependencyResolver.Current;
DependencyResolver.SetResolver(mvcResolver);
//set the webapi dep resolvers
_configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(_configuration);
app.UseAutofacMvc();
});
}
[TestCleanup]
public void Cleanup()
{
// Clean up the fake 'request' scope.
_configuration.Dispose();
DependencyResolver.SetResolver(_originalResolver);
_scopeProvider.EndLifetimeScope();
Server.Dispose();
}
}
When a simple test starts, I get an ArgumentNullException "Value cannot be null" httpContext. Which if I track down into the autofac code, I think it is coming from this extension method -
public static class AutofacMvcAppBuilderExtensions
{
internal static Func<HttpContextBase> CurrentHttpContext = () => new HttpContextWrapper(HttpContext.Current);
/// <summary>
/// Extends the Autofac lifetime scope added from the OWIN pipeline through to the MVC request lifetime scope.
/// </summary>
/// <param name="app">The application builder.</param>
/// <returns>The application builder.</returns>
[SecuritySafeCritical]
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
public static IAppBuilder UseAutofacMvc(this IAppBuilder app)
{
return app.Use(async (context, next) =>
{
var lifetimeScope = context.GetAutofacLifetimeScope();
var httpContext = CurrentHttpContext();
if (lifetimeScope != null && httpContext != null)
httpContext.Items[typeof(ILifetimeScope)] = lifetimeScope;
await next();
});
}
}
loacted in the Core/Source/Autofac.Integration.Mvc.Owin/AutofacMvcAppBuilderExtensions.cs file. Is there a problem with my setup, or a proper way to use Autofac in integration tests with a WebApi application using IIS Host and OWIN Middleware?
It appears you already asked this as an issue over on the Autofac project. I'll copy/paste the answer here (though in the future it'd probably be better to go with one or the other and not both).
Part of the awesomeness of OWIN-only apps is that you don't need HttpContext anymore. Nothing is tied to that; instead, it's all HttpContextBase and things that are separate from the legacy IIS. Like, in Web API, the current context is always shipped around with the HttpRequestMessage - there's no global static HttpContext.Current because that's legacy stuff.
Thus, when you run unit tests with an OWIN test host, you can expect there to not be an HttpContext.Current. It's decoupled from all that.
MVC can't run as OWIN-only because the libraries are tightly coupled to the legacy IIS/ASP.NET stack. Trying to test MVC stuff using an OWIN-only test server is going to give you trouble like this. That will change with the new ASP.NET 5.0 coming out with the new Visual Studio.
If you need to test MVC in an integrated way, there isn't a way to do that with OWIN right now. You have to fire up IIS Express.
Finally, I do see that you're missing the Web API middleware for OWIN (the actual Microsoft Web API middleware). That might give you other problems down the line.
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(_configuration);
app.UseAutofacMvc();
// You're missing this:
app.UseWebApi(config);