I have a web api 2 project and I am using Asp.net Identity and Entity framework.
I have discovered that when I change the password, the old one still works but the new one give me error (invalid grant).
I am sure the change password works correctly... Infact in the database the value of the (hashed) password changes correctly.
UPDATE
I have understood why. The authorizationServerProvider (or the user manager I use inside it) is not instanciated per http request. It seems it is a singleton. The problem is that it is instanciated on startup and stop, so it's like it continue to use data of the first login, that are cached (by Entity framework).
Here how I configure my authorizationServerProvider class:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
OAuthAuthorizationServerOptions oauthServerOptions = new OAuthAuthorizationServerOptions
{
//...,
Provider = new TokenBasedAuthorizationServerProvider(),
RefreshTokenProvider = new RefreshTokenProvider()
};
app.UseOAuthAuthorizationServer(oauthServerOptions);
}
How can avoid the login use the cached data?
Thank you
You can try to:
Disable Tracking using AsNoTracking()
Throw away the DbContext and create a new one
Call GetDatabaseValues() to get updated values for single entity
I had the same problem with UseOAuthAuthorizationServer and DI. Last time I faced with this when using Autofac but this is relevant to other DI frameworks. The problem is we have static instance of OAuthAuthorizationServerProvider stored by middleware so no DI involved.
My solution was to get dependencies in OAuthAuthorizationServerProvider in each methods like this:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var scope = context.OwinContext.GetAutofacLifetimeScope();
var userManager = scope.Resolve<ApplicationUserManager>();
.........
}
My container initialization:
public static IContainer Configure(IAppBuilder appBuilder, HttpConfiguration config)
{
var containerBuilder = new ContainerBuilder();
.... registrations here ....
var container = containerBuilder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
appBuilder.UseAutofacMiddleware(container);
appBuilder.UseAutofacWebApi(config);
return container;
}
You can do something like this using other DI containers as well.
Related
I have secrets.json and Azure KeyVault setup in my application exactly as the MS tutorial suggest. I also have an authorization filter. All is working fine.
In a controller, I pass IConfiguration in and do:
_config["KEY"]
to access a value from my secrets.
How do I access these values in the custom AuthorizationFilter? It will not let me pass in attributes as I typically would.
In fact, if I could actually get my DbContext in the filter, I wouldn't need to access the config, but I can't pass that in either.
I want to either replace the string with a value from my vault or pull in the DbContext I created in Program.cs:
// Program.cs:
builder.Services.AddDbContext<DBContext>(options => Options.UseSqlServer(connectionString));
// In my filter...
public void OnAuthorization(AuthorizationFilterContext context)
{
var optionsBuilder = new DbContextOptionsBuilder<DBContext>();
optionsBuilder.UseSqlServer("{STRING}");
var dbContext = new DBContext(optionsBuilder.Options);
//...
}
If I understand you correctly, you have to use httpContext services
public class TestFilter : AuthorizeFilter
{
public override Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
var config = context.HttpContext.RequestServices.GetService<IConfiguration>();
var db = context.HttpContext.RequestServices.GetService<DBContext>();
return base.OnAuthorizationAsync(context);
}
}
So you will use DI on Authorization request. I would assume that .net core would create singleton instance of filter this is why constructor injections wont work.
I have recently been working on a .NET Core web API. I have just attempted authentication using JWT, by following the guide on https://stormpath.com/blog/token-authentication-asp-net-core.
All was going well until I had to replace the hard-coded username and passwords in the GetIdentity method with a DB query and realized I do not know how to access the DB from within this file!
The method I am referring to is shown in the link below on line 70.
https://github.com/nbarbettini/SimpleTokenProvider/blob/master/test/SimpleTokenProvider.Test/Startup.Auth.cs
My questions are as follows.
Can I access the database here? If so how?
Should this be where the GetIdentity method is, or is there a better way?
Yes, you can access the database! Code that runs in the Configure method can access any services that are added in the ConfigureServices method, including things like database contexts.
For example, if you have a simple Entity Framework context:
using Microsoft.EntityFrameworkCore;
using SimpleTokenProvider.Test.Models;
namespace SimpleTokenProvider.Test
{
public class SimpleContext : DbContext
{
public SimpleContext(DbContextOptions<SimpleContext> options)
: base(options)
{
}
public DbSet<User> Users { get; set; }
}
}
And you add it in ConfigureServices:
services.AddDbContext<SimpleContext>(opt => opt.UseInMemoryDatabase());
Then, you can access it when you are setting up the middleware:
var context = app.ApplicationServices.GetService<SimpleContext>();
app.UseSimpleTokenProvider(new TokenProviderOptions
{
Path = "/api/token",
Audience = "ExampleAudience",
Issuer = "ExampleIssuer",
SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256),
IdentityResolver = (username, password) => GetIdentity(context, username, password)
});
And rewrite the GetIdentity method a little:
private Task<ClaimsIdentity> GetIdentity(SimpleContext context, string username, string password)
{
// Access the database using the context
// Here you'd need to do things like hash the password
// and do a lookup to see if the user + password hash exists
}
I'm the author of the original sample. Sorry it wasn't clear initially! I tried to write the IdentityResolver delegate in a way that makes it easy to provide your own functionality -- like integrating with your own database (as above), or hooking it up to ASP.NET Core Identity. Of course, you're free to throw away my code and do something better, too. :)
On .NET CORE 2.1, just pass the context as an argument to the Configure method:
public void Configure(IApplicationBuilder app, YourDbContext context, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//do whatever you want with the context here...
}
Adding services to the service container makes them available within
the app and in the Configure method. The services are resolved via
dependency injection or from ApplicationServices.
The accepted answer does not work for scoped services (scoped services are created per request, if you are using Entity Framework and adding the context with AddDbContext then this is the case).
You can use scoped services in startup as follows (source):
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
using (var serviceScope = app.ApplicationServices.CreateScope())
{
var services = serviceScope.ServiceProvider;
var myDbContext = services.GetService<MyDbContext>();
}
}
or pass it in the argument of the Configure method as shown in the answer of
juanora
I might be wrong on some other level but the solution I found is to create a scope.
I passed the app instead of the ctx in GetIdentity, then in GetIdentity using a scope:
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope()) {
if (serviceScope.ServiceProvider.GetService<YourAppDbContext>() != null)
{
var ctx = serviceScope.ServiceProvider.GetService<YourAppDbContext>();
if (AnAuthenticateMethodHereMaybe(ctx, username, password)) {
return Task.FromResult(new ClaimsIdentity(new
GenericIdentity(username, "Token"), new Claim[] { }));
}
}
}
I cannot figure out a correct way to resolve a service via Autofac that is used while constructing the Owin context and also disposed at the request end.
Since the OwinContext is still under construction at this point, the LifetimeScope cannot be found by calling HttpContext.Current.GetOwinContext().GetAutofacLifetimeScope(). The OwinContext is not available here yet.
In my code the IAdfsAuthorizationProvider service is resolved directly at the Container, but isn't disposed after a request and lives much longer.
I could create a new LifeTimeScope by calling container.BeginLifetimeScope(), but now the LifeTime is separated of the request and will possibly resolve different instances in the same request. And there is no way to dispose the lifeTimeScope myself at the correct time.
public void Configure(IAppBuilder app)
{
var builder = new ContainerBuilder();
builder.RegisterType<AdfsAuthorizationProvider>().As<IAdfsAuthorizationProvider>().InstancePerLifetimeScope();
var container = builder.Build();
app.UseAutofacMiddleware(container);
// **Service that's not binding to the request lifetime.**
var service = container.Resolve<IAdfsAuthorizationProvider>();
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
Provider = new AuthorizationServerProvider(service),
});
}
Does anyone has a suggestion?
The OAuthAuthorizationServerProvider is a singleton that call methods for all requests.
This class has not been designed to be injected. If you want to resolve a dependency for one request you will have to resolve it manually using the owinContext provided with each method
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override Task GrantAuthorizationCode(
OAuthGrantAuthorizationCodeContext context)
{
IAdfsAuthorizationProvider authorizationProvider =
context.OwinContext
.GetAutofacLifetimeScope()
.Resolve<IAdfsAuthorizationProvider>();
return base.GrantAuthorizationCode(context);
}
}
What I', trying to do is have a UserContext instantiated (single instance per user ideally). The UserContext depends on the current logged-in user.
MyUser is created by calling GlobalContext.User(username)
The following snippet is called from my Startup.Configuration(IAppBuilder app):
private static IContainer RegisterServices()
{
var builder = new ContainerBuilder();
builder.RegisterApiControllers(System.Reflection.Assembly.GetAssembly(typeof(MyWeb.API.Startup)));
builder.RegisterType<GlobalContext>().As<IGlobalContext>().SingleInstance();
builder.Register(c => c.Resolve<IGlobalContext>().User(
HttpContext.Current.User.Identity.Name)
).As<MyUser>();
builder.RegisterType<UserContext>().As<IUserContext>().InstancePerLifetimeScope();
return builder.Build();
}
Everything seems to work fine when ran in IIS Express.
When I run this in an integration test using the Owin WebApp.Start<Startup>(url: BaseAddress) HttpContext.Current is null. This is expected from all the reading that I've done so far, but what's the alternative?
I think I need to access the owinContext or the autofac lifetime scope to get the current user during resolution, but how do I do that?
Is there a better way to do this? or am I missing something trivial?
All help is appreciated.
EDITED TO ADD INFO:
In case it helps I'm including my Configuration() method here:
using System.Web;
using System.Web.Http;
using Autofac;
using Autofac.Integration.WebApi;
using Owin;
using System.Reflection;
namespace SomeNameSpace
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var configuration = new HttpConfiguration{ IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always };
WebApiConfig.Register(configuration);
BreezeConfig.Register(configuration);
CorsConfig.Register(configuration);
// Autofac IoC
var container = RegisterServices();
app.UseAutofacMiddleware(container);
app.UseWindowsAuthentication();
var urt = new UserRolesTranformer(new GlobalContext());
app.UseClaimsTranformation(urt.Transformation);
app.UseAutofacWebApi(configuration);
configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
//app.Use(typeof(TestMiddleware));
app.UseAutofacWebApi(configuration);
app.UseWebApi(configuration);
}
}
Ultimate Goal:
create a test that simulates multiple concurrent users logging in and ensuring that each user get's their own UserContext instance.
In the case of a Unit Test, you shouldn't really need access to the HttpContext or the OwinContext. As I imagine you have already discovered, a Unit Test has no concept of a HttpContext.
I am assuming that your IGlobalContext has a method User which takes a Username and returns you a User, perhaps from your database based on the Username passed in from the Current Context?
What you should do is have different Autofac registrations for your website and for your unit test so that you can have different dependencies in each environment. Then in your Unit Tests Autofac registration you could register either a completely different dependency to be resolved in your Unit Test, or you could pass into your User method, a mocked username that you know exists.
So you would keep this as your Autofac Registrations in your Website.
private static IContainer RegisterServices()
{
var builder = new ContainerBuilder();
builder.RegisterApiControllers(System.Reflection.Assembly.GetAssembly(typeof(MyWeb.API.Startup)));
builder.RegisterType<GlobalContext>().As<IGlobalContext>().SingleInstance();
builder.Register(c => c.Resolve<IGlobalContext>().User(
HttpContext.Current.User.Identity.Name)
).As<MyUser>();
builder.RegisterType<UserContext>().As<IUserContext>().InstancePerLifetimeScope();
return builder.Build();
}
Then in your Unit Test, when you register your Autofac registrations with something like this..
private static IContainer RegisterServices()
{
var builder = new ContainerBuilder();
builder.RegisterApiControllers(System.Reflection.Assembly.GetAssembly(typeof(MyWeb.API.Startup)));
builder.RegisterType<GlobalContext>().As<IGlobalContext>().SingleInstance();
builder.Register(c => c.Resolve<IGlobalContext>().User(
"MyTestUser")
).As<MyUser>();
builder.RegisterType<UserContext>().As<IUserContext>().InstancePerLifetimeScope();
return builder.Build();
}
This way you know in your Unit Test, exactly what User you are passing in and what User you should be getting back.
Or by using your own entirely mocked user like this ...
public class TestUser : MyUser
{
//Known Test Scenario Properties and Methods as required
}
private static IContainer RegisterServices()
{
var builder = new ContainerBuilder();
builder.RegisterApiControllers(System.Reflection.Assembly.GetAssembly(typeof(MyWeb.API.Startup)));
builder.RegisterType<GlobalContext>().As<IGlobalContext>().SingleInstance();
builder.RegisterType<TestUser>().As<MyUser>();
builder.RegisterType<UserContext>().As<IUserContext>().InstancePerLifetimeScope();
return builder.Build();
}
I hope this helps?
I am trying to understand the UserManagerFactory middleware explained here per request lifetime management for usermanager.
I created this class which I am calling from the Startup Configuration method
public class CustomUserManagerProvider
{
public static CustomUserStore<CustomUser> CreateCustomUserStore()
{
return new CustomUserStore<CustomUser>(/*Need to inject dependencies here*/);
}
public static CustomUserManager CreateCustomUserManager(IdentityFactoryOptions<CustomUserManager> options,
IOwinContext context)
{
return new CustomUserManager(context.Get<CustomUserStore<CustomUser>>());
}
}
And the Startup Configuration
public void Configuration(IAppBuilder app)
{
app.CreatePerOwinContext(CustomUserManagerProvider.CreateCustomUserStore);
app.CreatePerOwinContext<IngramUserManager>(CustomUserManagerProvider.CreateCustomUserManager);
////....Other things
}
Now, My CustomUserStore has some dependencies which I want to inject in the constructor.
The composition root of the IOC container knows how to resolve these dependencies.
How do I make the CustomUserManagerProvider DI container aware(If that makes sense)...
Although this works
public static CustomUserStore<CustomUser> CreateCustomUserStore()
{
var dependency = DependencyResolver.Current.GetService<ISomeDependency>();
return new CustomUserStore<CustomUser>(dependency);
}
But, I was trying to avoid the service locator (anti)pattern. Is this my only option, Is this even right??
I am using Ninject.
Cant I just create a UserManager in requestScope in the composition root and inject into the controllers, wont that be the same thing?
In a web app, is CreatePerOwinContext same as creating InRequestScope?
Yes, you can inject the UserManager into your controllers a different way if you like, there's nothing that requires using the CreatePerOwinContext/IdentityFactoryMiddleware.