In my asp.net core 2.0 web app, I've got a custom ISecurityTokenValidator
which validates tokens.
It depends on a repository to do a db lookup - the repository itself is setup as a scoped dependency:
services.AddScoped<IMyRepository>(MyRepository);
Now the funkiness comes about because of the way the ISecurityTokenValidator is setup.
It's added in ConfigureServices
:
.AddJwtBearer(options =>
{
options.SecurityTokenValidators.Clear();
options.SecurityTokenValidators.Add(new MyTokenValidator(services.BuildServiceProvider()));
})
This is how it looks:
public class MyTokenValidator : ISecurityTokenValidator
{
private readonly IServiceProvider _serviceProvider;
public MyTokenValidator(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public bool CanReadToken(string securityToken) => true;
public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters,
out SecurityToken validatedToken)
{
var serviceScopeFactory = _serviceProvider.GetRequiredService<IServiceScopeFactory>();
using (var scope = serviceScopeFactory.CreateScope())
{
var myRepository = scope.ServiceProvider.GetService<IMyRepository>();
var principalFactory = scope.ServiceProvider.GetService<IUserClaimsPrincipalFactory<User>>();
// Use the repo....
}
}
}
Now, because the IsecurityTokenProvider
is only instantiated once, it's effectively a singleton. When I use the service provider to ask for a IMyRepository
I was finding that I was always received the same object - there is no new scope as far as it was concerned, because it's in a singleton class.
To get round that, you'll see in the code above Ive had to manually force a new scope every time the token validator is called. Is this really the only way to resolve this, it seems like I'm hacking around to make it work here...
In a scoped service, with every HTTP request, we get a new instance. However, within the same HTTP request, if the service is required in multiple places, like in the view and in the controller, then the same instance is provided for the entire scope of that HTTP request.
Scoped: A new instance of a service is created in each scope. It will act as if it is singleton within that scope. If the service is disposable it will be disposed when service scope is disposed.
The JwtBearer middleware looks for tokens (JSON Web Tokens or JWTs) in the HTTP Authorization header of incoming requests. If a valid token is found, the request is authorized.
JSON Web Tokens (commonly known as JWT) is an open standard to pass data between client and server, and enables you to transmit data back and forth between the server and the consumers in a secure manner. This article talks about how you can take advantage of JWTs to protect APIs.
Old question but the best way I have found to solve this problem is to use IPostConfigureOptions<JwtBearerOptions>
to configure SecurityTokenValidators.
First register the JWT bearer and options
services.AddAuthentication(options =>
{
...
}).AddJwtBearer(AuthenticateScheme, options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
...
};
});
Then register a custom implementation of IPostConfigureOptions<JwtBearerOptions>
services.AddSingleton<IPostConfigureOptions<JwtBearerOptions>, CustomJwtBearerOptionsPostConfigureOptions>();
And register a custom implementation of ISecurityTokenValidator
services.AddSingleton<MyCustomSecurityTokenValidator>();
CustomJwtBearerOptionsPostConfigureOptions could look something like:
public class CustomJwtBearerOptionsPostConfigureOptions : IPostConfigureOptions<JwtBearerOptions>
{
private readonly MyCustomSecurityTokenValidator _tokenValidator; //example dependancy
public CustomJwtBearerOptionsPostConfigureOptions(MyCustomSecurityTokenValidator tokenValidator)
{
_tokenValidator = tokenValidator;
}
public void PostConfigure(string name, JwtBearerOptions options)
{
options.SecurityTokenValidators.Clear();
options.SecurityTokenValidators.Add(_tokenValidator);
}
}
Now options.SecurityTokenValidators is configured by CustomJwtBearerOptionsPostConfigureOptions
which is instantiated by dependency injection and can pass on the relevant decencies.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With