Is it okay to embed username in BEARER Authorization Header? - c#

I have an API endpoint created using Web API to create a security Token. So the user passes username:password in a Basic Authorization header. The username/password is verified and a Json Web Token is returned.
Now all my other endpoints that do GETs or POSTs I will pass in the JWT in the Authorization header as a BEARER type. I know typically this is BEARER + (jwt) but would it be just as okay to pass
BEARER + username:(jwt)
The reason I want to pass the username:(jwt) is when I verify the jwt I also want to pull out the username from the jwt claim and compare it to the username passed in the auth header as an extra check.
Does this approach sound good or should I forget about doing the username check altogether because it doesn't add any extra security?

There is nothing preventing you from adding the username outside of the token, however
you can add as many claims to your token as you need and can include the username. These claims can be retrieved from your client application or Web API through the IHttpContextAccessor.
In your client/API you can then do something like this to retrieve the claims:
List<Claim> claims = _context.Request.HttpContext.User.Claims.ToList();
Where _context is of type IHttpContextAccessor.
This obtains the Subject property of your encoded token, which is a ClaimsIdentity consisting of a list of the Claims that have been added when creating your token.
You can add the username outside of the token, however everything you need can be stored more securely and retrieved from the token itself after the client/API has authenticated the bearer token.
In addition, you can use the claims to add extra authorization when accessing other classes within your client/API.
All the other claims types like sub, iat and so on are properties within the token after it is authenticated on your client/API end. What you need to do during token creation is to use the SecurityTokenDescriptor to create the token descriptor like this (abbreviated):
SecurityTokenDescriptor descriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[] {
new Claim(ClaimTypes.Name, username),
new Claim(JwtRegisteredClaimNames.Sub, username)}),
Expires = DateTime.Now.AddMinutes(expiration_in_minutes),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha256Signature)
};
You will need to ensure the namespaces are in your source:
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
When I run this to generate the token and then decode the token in https://jwt.io/ in get:
{
"unique_name": "usernam#xxxx.xxx",
"sub": "usernam#xxxx.xxx",
"nbf": 1622285741,
"exp": 1622287503,
"iat": 1622285741
}
Adding the claim using JwtRegisteredClaimNames.Sub exposes the sub in the token.
Try that and see if it works.

You can send the username/userid in the sub claim.
The claims inside of the token are signed. The username/userid(sub) is signed, no need to verify if the username/userid is ok/valid.
In your case the BEARER Username:Token
Username(No signed, can be changed, can't be validated):
Token(claims/header/signature - all content is signed/validated)>
Username/userid outside token don't add extra security.
System.IdentityModel.Tokens.Jwt.JwtRegisteredClaimNames.Sub to avoid write "sub" in the claim name. This namespace is located in System.IdentityModel.Tokens.Jwt nuget package.

Related

WebAPI making an extra trip for user claims using OIDC authentication handler

My Current Setup is:
I have an Identity server built using Duenede.IdentityServer package running at port 7025.
I have a WebApi which is Dotnet 6 based and below is its OIDC configuration.
AddOpenIdConnect("oidc", o =>
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
o.SaveTokens = true;
o.GetClaimsFromUserInfoEndpoint = true;
o.RequireHttpsMetadata = false;
o.ResponseType = "code";
o.Authority = "https://localhost:7025/";
o.ClientId = "some clientid";
o.ClientSecret = "some secret";
o.Scope.Clear();
o.Scope.Add("openid");
o.Scope.Add("profile");
o.Scope.Add("dotnetapi");
o.NonceCookie.SameSite = SameSiteMode.Unspecified;
o.CorrelationCookie.SameSite = SameSiteMode.Unspecified;
o.ClaimActions.MapUniqueJsonKey("role", "role");
o.ClaimActions.MapUniqueJsonKey("email", "email");
});
Now when web api request the token from the identityserver (OIDC is the challenge scheme and i have a cookie scheme set as default authentication scheme) it gets both id_token and access_token(verified using await HttpContext.GetTokenAsync("access_token"); await HttpContext.GetTokenAsync("id_token");). I can also find user claims in HttpContext.User.FindFirst("some claim");
But i have noticed that there is an extra call to the identity server from web api for the userinfo. I observed that it may be because of o.GetClaimsFromUserInfoEndpoint = true; when i omitted this line i found that user claims are not set, even though i am still getting both id and access token.
So my understanding is the OIDC client of dotnet is using userinfo endpoint to fetch the user claims. But my question is if i am already receiving the access_token why there is an extra call for the userinfo. Can this extra call be prevented?
is there any way so that i receive id_token at first and access_token is then fetched as it is doing now so that same information is not sent twice?
First, you can set this client config in IdentityServer to always include the user claims in the ID token
AlwaysIncludeUserClaimsInIdToken
When requesting both an id token and access token, should the user
claims always be added to the id token instead of requiring the client
to use the userinfo endpoint. Default is false.
The reason for not including it in the ID-token is that increases the size of the id-token and if you store the tokens in the asp.net session cookie, it also can become pretty big.
I wouldn't worry about the extra request that happens when the user authenticates.

How to logout or expire JWT token for ASP.NET Core Web API?

I have created a token with:
IdentityOptions identityOptions = new IdentityOptions();
var claims = new Claim[]
{
new Claim(identityOptions.ClaimsIdentity.UserIdClaimType, token.User.FirstOrDefault().ID.ToString()),
new Claim(identityOptions.ClaimsIdentity.UserNameClaimType,request.Local.UserName),
};
var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("this-is-my-secret-key"));
var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
var jwt = new JwtSecurityToken(signingCredentials: signingCredentials, claims: claims, expires: DateTime.Now.AddHours(12));
Can you help me how to logout or expire this token for ASP.NET Core Web API? So the user will never use this token again.
Thanks
Once JWT is generated and sent to the client. It cannot be change already (not by client itself and must go through back-end to get a new token).
In order to invalidate/revoke a JWT, you may have a Redis (recommended) or database to store those invalidated JTI (Token ID) that is associated with each JWT issued.
If you do not wish to have Redis/database, then you must keep your JWT lifetime as short as possible like 5 minutes. Then, when logout, remove the token from client side (local storage or cookie). However, this approach does not invalidate JWT immediately, clients are still able to access to the API if they keep their token before we remove it.

Why IdentityModel JWT library generates the same token when the same Expires is provided?

I have the following code in my application to generate a JWT for authentication purposes in a REST API:
private string GenerateToken(List<Claim> identityFields)
{
var key = Convert.FromBase64String(__SECRET);
var securityKey = new SymmetricSecurityKey(key);
var descriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(identityFields.ToArray()),
Expires = DateTime.UtcNow.AddDays(1),
SigningCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature)
};
var handler = new JwtSecurityTokenHandler();
var token = handler.CreateJwtSecurityToken(descriptor);
return handler.WriteToken(token);
}
The "identityFields" parameter is an object with values from the user (which are not relevant here).
My main problem is that, when I sent two request to that method in the same second (I mean, for example, 2019-01-23 14:57:59.827 and 2019-01-23 14:57:59.350), it generates the same JWT for both session (taking in mind that the "identityFields are the same, of course), because the "Expires" property doesn't concern about the milliseconds.
How can solve that issue?
I'm using the official JWT library (System.IdentityModel.Tokens.Jwt), is not custom code.
Thank you!
I recommend a look at https://blogs.msdn.microsoft.com/webdev/2016/10/27/bearer-token-authentication-in-asp-net-core/
Depending on your specific use case you should be able to add custom claims to your token so that you can extrac the needed information directly from the token.
If you need to keep the information secret (since anyone can decode a JWT token) you could add a unique identifier to as a custom claim and use that to look up information in your database.

What is the best practice for fetching User data after validating JWT in .NET Core 2 Web Api?

I am moving from MVC to a SPA + API-approach on a new application im making and am facing some questions I cant seem to find the answer for.
I have a Web API in .Net Core 2 that is using Identity for its user-store. I am protecting the API by issuing JWT-tokens which my SPA is sending in as a bearer-token on every request to my controllers. The issuing code is:
private async Task<object> GenerateJwtTokenAsync(ApplicationUser user)
{
var roles = await _userManager.GetRolesAsync(user);
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Sub, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(ClaimTypes.NameIdentifier, user.Id)
};
claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role)));
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtKey"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var expires = DateTime.Now.AddDays(Convert.ToDouble(_configuration["JwtExpireDays"]));
var token = new JwtSecurityToken(
_configuration["JwtIssuer"],
_configuration["JwtIssuer"],
claims,
expires: expires,
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
My question is: What is the best practice for getting user information needed to serve the request in the controller?
As Im getting the JWT-token I have the user-information, but the extended information on the user, such as which company they work for is in my SQL-database.
// GET: api/Agreements
[HttpGet]
public async Task<IEnumerable<Agreement>> GetAgreementsAsync()
{
var user = await _userManager.GetUserAsync(HttpContext.User);
return _context.Agreements.Where(x => x.CompanyId == user.CompanyId);
}
Do I need to make another turn to the DB to get this information on each request? Should this information be put in the JWT-token, and in that case, in which field to you put "custom"-information?
Preferably when authorizing you would like to stay stateless, which means that when client passes authentication and gets JWT token, then server can authorize requests with the JWT token on the fly. Which means that server won't look for JWT token anywhere, not in database or memory. So you don't have overhead of looking in database or elsewhere.
To answer your question you should also know the reason behind Claims: "The set of claims associated with a given entity can be thought of as a key. The particular claims define the shape of that key; much like a physical key is used to open a lock in a door. In this way, claims are used to gain access to resources." from MSDN
You don't need to make another request to DB, but keep in mind that Claims are best used for authorization purposes, so mainly add extra claims in JWT so you can later authorize for API resources without going to the database. You can also add your custom claims to then token which then get encrypted into JWT Token and sent to the client.
After authentication you can store the companyId and/or userId in the claims and you can name any string you would like, because that's how claim's constructor is implemented. And when the request arrives you can get the companyId from the claims. For example name it simply "companyId".
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Sub, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(ClaimTypes.NameIdentifier, user.Id),
new Claim("companyId", user.companyId.ToString()) // Like this
};
And then write getter for the company id claim
HttpContext.User.FindFirst("companyId").Value
Also keep in mind that, for complex user data you shouldn't use claims like this because this data pass in network and you don't want huge JWT Tokens. Also it is not good practice, for that you can use HttpContext.Session where you can store data and get it when the request comes. This is not place to write in detail about Session storage.

How to issue and consume JWT using ServiceStack's JwtAuthProvider

Looking at the JwtAuthProvider documentation for ServiceStack, it seems that a lot of JWT functionality is given out-of-the-box. However I really need to look at some working example. I could not find any in the example directory for ServiceStack.
What I'd like to see, is an example code that shows:
How to issue a token with some claims.
How to decode the token and inspect the claims.
Just using some "Hello world" service. Does anyone have some code that shows this or know where to look?
Ideally, the signing would use RSA, but right now this is not that important...
Thanks.
The JWT AuthProvider is what Issues the JWT token which it populates based on the User Session. You can add your own metadata in the tokens and inspect it with the CreatePayloadFilter and PopulateSessionFilter.
JWT is enabled in both the AngularJS http://techstacks.io Example by just making a call to /session-to-token after the user successfully authenticates with their OAuth Provider, e.g:
$http.post("/session-to-token");
This converts their currently authenticated session into a JWT Token which it uses for future subsequent requests.
Likewise JWT is also used in http://gistlyn.com which uses a Customized JwtAuthProvider to embed the Github OAuth Access Token Secret into the JWT Token then uses the PopulateSessionFilter to extract it from the JWT Token and populate it back in the Users Session:
appHost.Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new GithubAuthProvider(appHost.AppSettings),
//Use JWT so sessions survive across AppDomain restarts, redeployments, etc
new JwtAuthProvider(appHost.AppSettings)
{
CreatePayloadFilter = (payload, session) =>
{
var githubAuth = session.ProviderOAuthAccess.Safe()
.FirstOrDefault(x => x.Provider == "github");
payload["ats"] = githubAuth != null
? githubAuth.AccessTokenSecret : null;
},
PopulateSessionFilter = (session, obj, req) =>
{
session.ProviderOAuthAccess = new List<IAuthTokens>
{
new AuthTokens { Provider = "github", AccessTokenSecret = obj["ats"] }
};
}
},
}));
Gistlyn uses a similar approach to TechStacks to using JWT Tokens by calling /session-to-token after the User has authenticated with Github OAuth using JavaScript's new fetch API
fetch("/session-to-token", { method:"POST", credentials:"include" });
JWT Stateless Auth Tests
For other examples you can look at JWT RSA Tests which uses CreateJwtPayload which shows examples of manually creating JWT Tokens in code.

Categories