I am creating a Web Api application and I want to use bearer tokens for the user authentication. I implemented the token logic, following this post and everything seems to w
I have had a similar problem.
The problem here is that when you try to inject IUserService
in your provider, Autofac detects that it has been registered as InstancePerRequest
(that uses the well-known lifetime scope tag 'AutofacWebRequest'
) but the SimpleAuthorizationServerProvider
is registered in the 'root'
container scope where the 'AutofacWebRequest'
scope is not visible.
A proposed solution is to register dependencies as InstancePerLifetimeScope
. This apparently solved the problem but introduces new ones. All dependencies are registered in the 'root'
scope, that implies having the same DbContext
and services instances for all the requests. Steven explains very good in this answer why is not a good idea to share the DbContext
between requests.
After deeper investigation tasks, I've solved the problem getting the 'AutofacWebRequest'
from the OwinContext
in the OAuthAuthorizationServerProvider
class and resolving the services dependencies from it, instead of letting Autofac to inject them automatically. For this I've used the OwinContextExtensions.GetAutofacLifetimeScope()
extension method from Autofac.Integration.Owin
, see example below:
using Autofac.Integration.Owin;
...
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
...
// autofacLifetimeScope is 'AutofacWebRequest'
var autofacLifetimeScope = OwinContextExtensions.GetAutofacLifetimeScope(context.OwinContext);
var userService = autofacLifetimeScope.Resolve<IUserService>();
...
}
I've made OAuthAuthorizationServerProvider
registration and injection inside ConfigureOAuth
method in a similar way than proposed by Laurentiu Stamate in another response to this question, as SingleInstance()
.
I've implemented RefreshTokenProvider
in the same way.
EDIT
@BramVandenbussche, this is my Configuration
method in the Startup
class, where you can see the order of middlewares added to the OWIN pipeline:
public void Configuration(IAppBuilder app)
{
// Configure Autofac
var container = ConfigureAutofac(app);
// Configure CORS
ConfigureCors(app);
// Configure Auth
ConfigureAuth(app, container);
// Configure Web Api
ConfigureWebApi(app, container);
}
I also tried @jumuro answer using the OwinContextExtensions.GetAutofacLifetimeScope that saves my day. Instead of registering the IUserService at runtime, this answer gives an option on validation/creating the instance service after request.
I added some new answer because I can't comment yet because of my low reputations but added additional guide codes to help someone.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
try
{
if (service == null)
{
var scope = Autofac.Integration.Owin.OwinContextExtensions.GetAutofacLifetimeScope(context.OwinContext);
service = scope.Resolve<IUserService>();
}
var user = await service.FindUserAsync(context.UserName);
if (user?.HashedPassword != Helpers.CustomPasswordHasher.GetHashedPassword(context.Password, user?.Salt))
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
}
catch(Exception ex)
{
context.SetError("invalid_grant", ex.Message);
return;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
AuthenticationProperties properties = CreateProperties(context.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(identity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(identity);
}
To use dependency injection in SimpleAuthorizationServerProvider
you have to register IOAuthAuthorizationServerProvider
to the Autofac container just like any other type. You can do something like this:
builder
.RegisterType<SimpleAuthorizationServerProvider>()
.As<IOAuthAuthorizationServerProvider>()
.PropertiesAutowired() // to automatically resolve IUserService
.SingleInstance(); // you only need one instance of this provider
You also need to pass the container to the ConfigureOAuth
method and let Autofac resolve your instance like this:
var oAuthServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = container.Resolve<IOAuthAuthorizationServerProvider>()
};
You should always use single instances if your properties within the object don't change via external data (let's say you have a property which you set in the controller which dependents upon some information stored in the database - in this case you should use InstancePerRequest).