Reputation: 189
So I'm just having a bit of trouble getting my head round the .NET Core [Authorize] attribute.
I have an authentication service running (let's say authapi.com
) which when provided with valid authentication details will return a JWT. When this JWT is given back to it, it will validate the JWT and return a message indicating such.
So, I'm now building another WebAPI (let's say genericapi.com
)which will require authorization for some of the actions/controllers. The idea being, the JWT will be passed in the headers of the request to genericapi
which then needs to pass those on to authapi.com
to validate them.
I tried adding a Policy but it got convoluted really quick, and I had to write [Authorize(Policy="TokenValid")]
on everything, when I'd rather just the default [Authorize]
did this, since ALL authorization will have to hit authapi
.
How would I go about getting that JWT from the header and passing it to the authapi
as standard behaviour for [Authorize]
?
Bear in mind: I don't want to do anything with the JWTs on genericapi
, all authentication is to be handled by authapi
.
Upvotes: 2
Views: 3622
Reputation: 1
I know this thread is over 7 years old, but i had the same problem found this and wasnt quite happy with writing my custom middleware. I finally found what i was looking for an it was this:
builder.Services.AddAuthentication()
.AddCookie()
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme,
options =>
{
options.Authority = builder.Configuration["Jwt:authority"];
options.Audience = builder.Configuration["Jwt:audience"];
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
};
options.RequireHttpsMetadata = false;
options.IncludeErrorDetails = true;
})
By setting the authority u can let another service validate the token (or not) and can use [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
on your controllers.
Upvotes: 0
Reputation: 24123
As far as i understand you don't want to use JwtBearerAuthentication
in genericapi
. In this case you can write custom authentication middleware(send jwt to authapi
and validate it, then set current user) for genericapi
. Then just use [Authorize]
attribute.
To write custom authentication middleware take a look at https://stackoverflow.com/a/37415902/5426333
However if possible, i wouldn't go with your way. I would use JwtBearerAuthentication
for genericapi
. Then i would use OnTokenValidated
event to handle other validations.
app.UseJwtBearerAuthentication(new JwtBearerOptions()
{
Events = new JwtBearerEvents()
{
OnTokenValidated = (context) =>
{
// send jwt to auth api
// validate it
if (!valid)
{
context.SkipToNextMiddleware();
}
return Task.FromResult(0);
}
}
});
Upvotes: 1
Reputation: 18295
You may try to customize the default JwtBearerAuthenticationMiddleware
providing to it a custom ISecurityTokenValidator
. Your user identity will be automatically set by the middleware, and you may continue using the Authorize
attribute inside MVC:
class MyTokenValidator : ISecurityTokenValidator
{
public string AuthenticationScheme { get; }
public MyTokenValidator(string authenticationScheme)
{
AuthenticationScheme = authenticationScheme;
}
public bool CanValidateToken => true;
public int MaximumTokenSizeInBytes
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public bool CanReadToken(string securityToken) => true;
public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
{
validatedToken = null;
//your logic here
var response = GetResponseFromMyAuthServer(securityToken);
//assuming response will contain info about the user
if(response == null || response.IsError)
throw new SecurityTokenException("invalid");
//create your identity by generating its claims
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, response.UserId),
new Claim(ClaimTypes.Email, response.Email),
new Claim(ClaimsIdentity.DefaultNameClaimType, response.UserName),
};
return new ClaimsPrincipal(new ClaimsIdentity(claims, AuthenticationScheme));
}
}
And in your startup class:
var options = new JwtBearerOptions();
options.SecurityTokenValidators.Clear();
options.SecurityTokenValidators.Add(new MyTokenValidator(options.AuthenticationScheme));
app.UseJwtBearerAuthentication(options);
//the rest of your code here
app.UseMvc();
You may need to further refine this approach, but this way you can achieve what you need by delegating all the validation to the remote endpoint.
Upvotes: 2