I encrypt a jwt token in the server and send it to client, Then store it in two places 1- Authorization header and 2- in a cookie, The cookie is used when the user refreshes the page and then again one copy is placed on an authorization header to go for server validation, and the other send to an open service to decrypt and get the principal object for use by the client webassembly app authorization system.
Is this design flawed? and is this open service to decrypt the token and get the principal unsafe?
[HttpPost("GetUserFromEncryptedToken")]
//[Authorize(Roles = "Administrators")]
public async Task<Client.Infrastructure.Auth.User> GetUserFromEncryptedToken(TokenDTO model)
{
var handler = new JwtSecurityTokenHandler();
var validations = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = _config["Jwt:Issuer"],
ValidAudience = _config["Jwt:Audience"],
RequireExpirationTime = true,
IssuerSigningKey = new SymmetricSecurityKey
(Encoding.UTF8.GetBytes(_config["Jwt:Key"])),
TokenDecryptionKey = new SymmetricSecurityKey
(Encoding.UTF8.GetBytes(_config["Jwt:DecryptionKey"])),
ClockSkew = TimeSpan.FromMinutes(0),
};
ClaimsPrincipal claims = null;
try
{
claims = handler.ValidateToken(model.Token, validations, out var tokenSecure);
var user = Client.Infrastructure.Auth.User.FromClaimsPrincipal(claims);
return user;
}
catch (Exception ex) { return new Client.Infrastructure.Auth.User(); }
}
First of all, the answer to this kind of question is always "it depends". Security is not an absolute thing and secure designs fail if not implemented properly.
Sending your JWT to an "open" service means two things: either that service is authenticated or its not. I probably isn't because if it was, you would have the same authentication problem other there.
At least, that open service is not a wholesale token decryption service, it is an oracle that returns the username of an encrypted JWT. But that extra network trip provides no value. You will have to trust the service to return the right username (there is no way for the client to validate that). I also requires you to make the encryption key available to another system in your infrastructure, increasing key exposure.
That is bad because the encryption key is symmetric. A compromise of that open service woud allow someone to generate valid JWTs.
So the open service as I understand it is not a good idea. I would say it increases the complexity and reduces security. Get rid of it.
Why you need to access the username is not clear. Since there is a JWT, the user is likely already authenticated, and you want to say "Welcome back {USER}" in your homepage.
Maybe you have claims in the JWT you don't want the client to see, and maybe you can't store them server side for scalability/stateless reasons. Depending on that, I would either:
I prefer not to use cookies if possible. To acheive that, send the Authorization
header back with every response, and use client-side javascript to put it back in the request's Authorization
header. This can also help with key rotation, as new JWTs will be signed or encrypted with the new key must sooner than if you have to wait for the user to authenticate again.
+If you use cookies for anything, your cookies should have the secure
and http_only
attributes set.
Finally, your design should support multiple keys whose validity period overlap enough to keep most of your users logged in when the key is changed (aka Key rotation).That means the server will have to implement a logic like: