Reputation: 5946
I'm writing a web-application in .net core that uses an API and a Website.
The web-service builds a JWT-token. This is the service-configuration (removed unnecessary parts)
public void ConfigureServices(IServiceCollection services)
{
//...
var tokenValidationParameters = new TokenValidationParameters
{
// The signing key must match!
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
// Validate the JWT Issuer (iss) claim
ValidateIssuer = true,
ValidIssuer = "ExampleIssuer",
// Validate the JWT Audience (aud) claim
ValidateAudience = true,
ValidAudience = "ExampleAudience",
// Validate the token expiry
ValidateLifetime = true,
// If you want to allow a certain amount of clock drift, set that here:
ClockSkew = TimeSpan.Zero,
};
var serialiser = services.BuildServiceProvider().GetService<IDataSerializer<AuthenticationTicket>>();
var dataProtector = services.BuildServiceProvider().GetDataProtector(new string[] {$"IronSphere.Web.Site-Auth"});
services
.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = tokenValidationParameters;
})
.AddCookie(cookie =>
{
cookie.Cookie.Name = "access_token";
cookie.TicketDataFormat = new JwtTokenValidator(
SecurityAlgorithms.HmacSha256,
tokenValidationParameters, serialiser, dataProtector);
});
//...
}
So far, so good. The login works, my website-side authorization works, I can work with the [Authorize]
attribute.
The problem is now, I'm logged in on the website, but not on the API.
I can't use the [Authorize]
-attribute for my API methods (and, yes, for sure, it makes sense).
So when I'm logged in, everytime I call the API, I also send the token in the header (this works, I can read it in the API-controller). I guess I'd need to deserialize it.
I tried with dependency injection to get that IDataSerializer<AuthenticationTicket>
into my controllers with:
services.AddSingleton<IDataSerializer<AuthenticationTicket>>(services.BuildServiceProvider().GetService<IDataSerializer<AuthenticationTicket>>());
then take the key from header, deserialize and I'd have my user-object with claims. But when I try to inject in any controller it makes my application crash (just a message, that dotnet stopped working)
Any idea how I can also validate the user when the api is getting called? (I could post more code if you need, just didn't want to fill here with too much code)
Upvotes: 1
Views: 953
Reputation: 5946
So after @Tseng helped me a lot with his input, here is my result (more input on how to do it better would be nice):
Added the un-protector for the token as a service with adding in Startup.cs
services.AddTransient<IJwtTokenService, JwtTokenService>();
the IJwtTokenService
public interface IJwtTokenService
{
string UnprotectToken(string protectedText);
}
the implemented JwtTokenService
:
public class JwtTokenService:IJwtTokenService
{
private readonly IDataSerializer<AuthenticationTicket> _ticketSerializer;
private readonly IDataProtector _dataProtector;
public JwtTokenService(IDataSerializer<AuthenticationTicket> serializer, IDataProtector protector)
{
_ticketSerializer = serializer;
_dataProtector = protector;
}
public string UnprotectToken(string protectedText)
{
SecurityKey signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(" ......... "));
TokenValidationParameters tokenValidationParameters =
_getTokenValidationParameters();
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
AuthenticationTicket authTicket;
string embeddedJwt;
try
{
// logic to deserialize token
// logic to validate token
// more logic... (algorithm,..)
}
catch (Exception)
{
return null;
}
return embeddedJwt;
}
}
So in the services I also had to add the IDataProtector
. It threw an exception before.
services.AddTransient(x => x.GetDataProtector(new[] {$"auth"}));
Then I could add the IJwtTokenService
into the constructor for dependency injection, unprotect it and sent it with the header:
protected ServiceBase(
IHttpContextAccessor contextAccessor,
IJwtTokenService jwtTokenService,
IMemoryCache memoryCache = null)
{
MemoryCache = memoryCache ?? new MemoryCache(new MemoryCacheOptions());
CachingFunctionalty = new CachingFunctionality();
HttpContextAccessor = contextAccessor;
JwtTokenService = jwtTokenService;
}
protected RestClient CreateClient()
{
RestClient restClient = new RestClient(ServiceAdress);
var token = HttpContextAccessor.HttpContext.Request.Cookies["access_token"];
if (string.IsNullOrWhiteSpace(token)) return restClient;
var unprotected = JwtTokenService.UnprotectToken(token);
restClient.AuthenticationHeaderValue = new AuthenticationHeaderValue("Bearer", unprotected);
return restClient;
}
Now my API works together with the AuthorizeAttribute
Upvotes: 1