New to Dependency Injection, so this is probably a simple matter, but i have tried and cant figure it out, i am using Simple Injector.
I have a WebApi that uses SimpleInjector perfectly fine, now i would like to implement security using OAuth.
To do this i started to follow this tutorial, which is very helpful, but doesnt use Dependancy Injection
http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/
I have my global.asax file looking like this, to setup dependancy injection (working perfect)
protected void Application_Start()
{
SimpleInjectorConfig.Register();
GlobalConfiguration.Configure(WebApiConfig.Register);
}
I have created a Startup.Auth.cs file to configure OAuth
public class Startup
{
public void Configuration(IAppBuilder app)
{
var OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new MyAuthorizationServerProvider() // here is the problem
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
Now as i commented above, MyAuthorizationServerProvider is the problem. it takes a parameter of IUserService which i usually inject. I do not want to empty constructor because my IUserService also injects a repository. Here is the file
public class ApiAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
private IUserService _service;
public ApiAuthorizationServerProvider (IUserService service)
{
_service = service;
}
public override async Task ValidateClientAuthentication(
OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(
OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin",
new[] { "*" });
IUserService service = Startup.Container.GetInstance<IUserService>();
User user = _service.Query(e => e.Email.Equals(context.UserName) &&
e.Password.Equals(context.Password)).FirstOrDefault();
if (user == null)
{
context.SetError("invalid_grant",
"The user name or password is incorrect.");
return;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
context.Validated(identity);
}
}
How can i get this working with Dependency Injection? This must happen quite a lot and must be able to do something to handle it. I am sure its something simple, but i am still learning.
I took some time to find out if it would be possible to register OAuthAuthorizationServerOptions
in the Owin pipeling using the app.Use()
method directly, instead of app.UseOAuthAuthorizationServer()
which is just an extension method over app.Use()
. app.Use()
has an overload where you could register a delegate which you could use to construct the OAuthAuthorizationServerOptions
.
Unfortunately this effort hit a dead end, because it seems that even if we'd use a delegate for the construction, this will be most likely only called once by the Owin pipeline which leads to the same result, namely a singleton instance of the OAuthAuthorizationServerOptions
and thus all dependencies of this class will be singleton as well.
So the only solution to keep things working as they should be, is to pull a new instance of your UserService
every time the GrantResourceOwnerCredentials()
method is called.
But to follow the Simple Injector design principles it would be bad design to keep a dependency on the container in the ApiAuthorizationServerProvider
class, like the original code shows.
A better way to do this would be to use a factory for the UserService
class instead of directly pulling it from the container. The next code shows an example of how you could do this:
First of all, clean out the Application_Start()
method in your global.asax file and place all your startup code in the Owin Startup()
method. The code of the Startup()
method:
public class Startup
{
public void Configuration(IAppBuilder app)
{
var container = SimpleInjectorConfig.Register();
GlobalConfiguration.Configure(WebApiConfig.Register);
Func<IUserService> userServiceFactory = () =>
container.GetInstance<IUserService>();
var OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new ApiAuthorizationServerProvider(userServiceFactory)
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
Notice how I changed the signature of the SimpleInjectorConfig.Register()
function by returning the completly configured Simple Injector container to the caller so it can be used directly.
Now change the constructor of your ApiAuthorizationServerProvider
class, so the factory method can be injected:
public class ApiAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
private Func<IUserService> userServiceFactory;
public ApiAuthorizationServerProvider(Func<IUserService> userServiceFactory)
{
this.userServiceFactory = userServiceFactory;
}
// other code deleted for brevity...
private IUserService userService
{
get
{
return this.userServiceFactory.Invoke();
}
}
public override async Task GrantResourceOwnerCredentials(
OAuthGrantResourceOwnerCredentialsContext context)
{
// other code deleted for brevity...
// Just use the service like this
User user = this.userService.Query(e => e.Email.Equals(context.UserName) &&
e.Password.Equals(context.Password)).FirstOrDefault();
// other code deleted for brevity...
}
}
This way you get a new UserService
everytime the GrantResourceOwnerCredentials()
method is called and the complete dependency graph behind the UserService
class will follow the lifetimes you defined in your Simple Injector configuration, while you depend on the container only in the composition root of the application.