Authenticating a user in a MVC application using JSON Web Tokens - c#

I have a legacy application from which I want to spawn a session in a new asp.net MVC application. I am attempting to pass a JSON Web Token to the MVC application to authenticate the user. I have it working for the most part, but it is requiring some extra code to get the user signed in (using Request.GetOwinContext().ctx.Authentication.SignIn) . It is making me question whether I am going down the correct path.
From what I have read I would think that I should be able to use the ClaimsPrincipal object created by the ValidateToken method to sign the user into the application.
My Issues:
The ClaimsIdentity instance created by ValidateToken sets the Authentication Type to Federated. To sign the user into the MVC application, I need to set the authentication type to Cookies (I think). To do this I am creating a new ClaimsIdentity instance based off the one created by ValidateToken and then passing ApplicationCookie as the authentication type to the constructor. Is it necessary to be doing this and is there a way to change the authentication type on the original ClaimsIdentity instance without creating a new one?
I cannot get the name property to be set automatically by the ValidateToken method. The SignIn method seems to require that to be set. To get around this I set it explicitly by pulling the name claim from the ClaimsIdentity instance created by ValidateToken (ident2.AddClaim(new Claim(ClaimTypes.Name, myIdentity.FindFirstValue("Name")));). Is there a claim I can pass in the payload of the JWT that will be mapped to this property automatically?
Or in general is this the wrong approach to accomplish the authentication?
The claims set of the payload for my JWT looks something like:
{
"iss": "http://oldapp.testing.com",
"aud": "http://newapp.testing.com",
"sub": "99239",
"iat": 1425507035,
"exp": 1425507065,
"name": "First Last",
"role": [
"Admin"
]
And the C# code to process it:
JwtSecurityToken tokenReceived = new JwtSecurityToken(token);
JwtSecurityTokenHandler recipientTokenHandler = new JwtSecurityTokenHandler();
byte[] keyBytes = Encoding.UTF8.GetBytes("someTestSecretKeyForTestingThis");
if (keyBytes.Length < 64 && tokenReceived.SignatureAlgorithm == "HS256")
{
Array.Resize(ref keyBytes, 64);
}
TokenValidationParameters validationParameters = new TokenValidationParameters()
{
ValidIssuer = "http://oldapp.testing.com",
ValidAudience = "http://newapp.testing.com",
IssuerSigningToken = new BinarySecretSecurityToken(keyBytes)
};
try
{
SecurityToken validatedToken;
var principal = recipientTokenHandler.ValidateToken(token, validationParameters, out validatedToken);
// Pull out the ClaimIdentity created by ValidateToken
var myIdentity = principal.Identities.FirstOrDefault();
//
// Copy ClaimIdentity created by the ValidateToken method and change the Authentication
// type from Federated to Cookie
//
// Is there a better way to do this???
//
var ident2 = new ClaimsIdentity(myIdentity.Claims, DefaultAuthenticationTypes.ApplicationCookie);
//
// Make sure the Name claim is set correctly so that the SignIn method will work
//
// Why isn't the Name claim set automatically???
//
ident2.AddClaim(new Claim(ClaimTypes.Name, myIdentity.FindFirstValue("Name")));
// Sign the user in
var ctx = Request.GetOwinContext();
var authManager = ctx.Authentication;
authManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
authManager.SignIn(ident2);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("Exception :" + ex.Message);

Related

Not able to read identity claims immediately after IAuthenticationService.SignInSync

I have a method of sign in like this, where I am adding three claims out of which one is custom claim
public async Task SignInAsync(IUser user)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString("N")),
new Claim(ClaimTypes.Name, $"{user.FirstName} {user.LastName}"),
new Claim(ClaimsPrincipalHelper.LoginSessionIdClaimsType, Guid.NewGuid().ToString())
};
var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
var principal = new ClaimsPrincipal(identity);
var properties = new AuthenticationProperties
{
AllowRefresh = true,
IsPersistent = true,
ExpiresUtc = DateTime.UtcNow.AddDays(7)
};
await _authenticationService.SignInAsync(_context, CookieAuthenticationDefaults.AuthenticationScheme, principal, properties);
}
What my understanding is that as soon as this method is called the claims are available to be fetched, so basically after this line
await SignInAsync(user);
When I will do this immediately
var claims = HttpContext.User.Claims.ToList();
all the three claims should be available. But this is not the case, it returns zero claims when I do it immediately, but when I try to access the claims after some time then I get all the three claims.
So is it that cookie writing takes time or what?
Why the claims are not available immediately or my understanding is wrong?
Your custom claim has to Implement IClaimsTransformation interface and the TransformAsync function, this gets called automatically by ASP. Then it needs to be added to application services collection in the Startup.cs class, now you will have access to it.
Step 1: Implement the interface IClaimsTransformation for your.. MyCustomClaim
Step 2: register in your services collection services.AddScoped<IClaimsTransformation, MyCustomClaim>();
You need them both or it will return null, here is a nice ref.

Differences between AspNetUserToken and custom made JWT token in .NET Core 3.0

I'm working on a small side-project API and I wanted to implement JWT authentication. I already made some API with JWT and always made custom implementation and validation.
This time, I wanted to use Identity/Entity Framework to go faster and to use the tools that are already made for me.
While doing the GenerateToken method and looking through the internet, I noticed that a lot of the tables created by IdentityFramework are not used. And I got interested in AspNetUserToken.
I noticed that instead of
private object GenerateToken(IdentityUser user)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.UTF8.GetBytes(ApiConfig.JwtSecretKey);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.Email, user.Email),
}),
Expires = DateTime.UtcNow.AddSeconds(double.Parse(ApiConfig.JwtExp)), //TODO: Try parse
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
Audience = ApiConfig.JwtAudience,
Issuer = ApiConfig.JwtIssuer
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
Which I used to generate a new JWT token, add the claims, issuer, audience, etc... Could maybe be replaced by this :
//Removes existing token
_userManager.RemoveAuthenticationTokenAsync(user, "lboard", "login");
//Creates a new one
var newToken = await _userManager.GenerateUserTokenAsync(user, "lboard", "login");
//Set the new token for the user
await _userManager.SetAuthenticationTokenAsync(user, "lboard", "login", newToken);
I would like to know what are the differences between the two methods, and if there are any benefits of using a custom implementation or if I'm better off with the IdentityFramework one.
The GenerateUserTokenAsync methods is used internally by other UserManager methods like GenerateChangeEmailTokenAsync, GenerateChangePhoneNumberTokenAsync and so on. REF
In order to use more abstract GenerateUserTokenAsync, you must provide a token provider that actually generates the token. Since you don't have any default token providers for a JWT access token, you would still have to write the logic yourself and register your custom token provider and then you could use the GenerateUserTokenAsync method.
You would still need to write the JWT logic by yourself, incude claims etc, but with an added overhead.

get signature from TokenValidatedContext

I'm using the Microsoft.AspNetCore.Authentication.JwtBearer and System.IdentityModel.Tokens.Jwt packages for my .NET Core project.
When configuring the services I'm adding logic to the OnTokenValidated event.
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(jwtBearerOptions =>
{
// ... set TokenValidationParameters ...
jwtBearerOptions.Events = new JwtBearerEvents()
{
OnTokenValidated = (tokenValidatedContext) =>
{
JwtSecurityTokenHandler jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
string tokenWithoutSignature = jwtSecurityTokenHandler.WriteToken(tokenValidatedContext.SecurityToken);
// ... full token from request? ...
}
};
});
Since I know the context only returns me the token without the signature I would like to know how I can
either get the full token with signature
or the signature additionally to add it to the tokenWithoutSignature string
If this is not possible:
I'm generating new tokens this way
public string GenerateAccessToken(IDictionary<string, object> payload)
{
SymmetricSecurityKey symmetricSecurityKey = new SymmetricSecurityKey(Convert.FromBase64String("secret from config"));
SecurityTokenDescriptor tokenDescriptor = new SecurityTokenDescriptor
{
Claims = payload,
Expires = DateTime.Now, // value from config
SigningCredentials = new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha256Signature)
};
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
SecurityToken securityToken = tokenHandler.CreateToken(tokenDescriptor);
string token = tokenHandler.WriteToken(securityToken);
return token;
}
Maybe I can either retrieve
the token string without the signature
only the signature
within this method?
If nothing works:
Since I kow a bearer token always contains three parts like
header.payload.signature
I could split the string segments to an array, take the first and second element from the array and create a new string of
firstString + . + secondString
That should give me the token without the signature. Are there any better ideas to cut off the signature from a full token?
Why do I want to achieve this?
This question is based on this one
Security token from TokenValidatedContext from the OnTokenValidated event listener is missing last string segment
I'm working with access and refresh tokens. During validation, I have to compare the token from the request with the token from the database. The token in the database contains the signature too. So I'm facing the same problem as linked above.
That's why I thought about multiple solutions and wrote them down here. If the TokenValidatedContext is not able to return me the signature it seems I have to store the JWT to the database without the signature. And also for this case, I need to separate the signature from the generated JWT.
Without using refresh tokens I only store the maximum session lifetime of a user to the database. The flow is based on this idea
Only store the time of the JWT with the highest lifetime to the database instead of the whole JWT
With using refresh tokens I came up with the following flow. Since you know that the OnTokenValidated handles the validation logic the following logic is additional. I have a database table with
username | access_token | refresh_token | refresh_token_expires_at
and the primary key is a composite key of username + access_token. Refresh tokens are just some random strings generated like so
public string GenerateRefreshToken()
{
var randomNumber = new byte[32];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(randomNumber);
return Convert.ToBase64String(randomNumber);
}
}
and that's why I'm storing an additional expiration date to it. It should be able to expire after some time.
Signing in
Store the generated access and refresh token and its expiration time for a user to the database. Either store the full access token or the access token without signature to the database (depends on the solution of this question).
Hitting a protected endpoint
Check if that access token for that user exists in the database.
Hitting the refresh endpoint
Check if the database refresh token has expired. If not, compare this one with the refresh token from the request. If everything is fine, remove the old access and refresh token from the database and store the new generated access and refresh token to the database.
Signing out
Remove that access and its connected refresh token from the database.
I don't quite understand why you do all this, but if all you need is the original token, you can use one of these:
o.Events = new JwtBearerEvents
{
OnTokenValidated = (context) =>
{
var securityToken = (System.IdentityModel.Tokens.Jwt.JwtSecurityToken)context.SecurityToken;
var token = securityToken.RawData; // "ey...."
var tokenHeader = securityToken.RawHeader; // "ey...."
var tokenPayload = securityToken.RawPayload; // "ey...."
var tokenSignatur = securityToken.RawSignature; // "ey...."
var fullBearerHeader = context.Request.Headers["Authorization"]; // "Bearer ey..."
return Task.CompletedTask;
}
};
You probably want to make the code a bit more safe with regards to type casting etc., but it should give you the token.
Why do you want to manipulate the token? If it is just to Validate the token you can use below code.
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
//ValidIssuer = Configuration["Issuer"],
//ValidAudience = Configuration["Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Key"]))
};
});
and
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAuthentication();
app.UseMvc();
}

ASP.net Core 2 custom authentication (IsAuthenticated = false)

I'm looking for a minimal example for a custom authentication writen in C# for asp.net core 2 based on for example API keys.
Mircosoft has a pretty good documentation about doing this with cookies, however this is not what I want. Since I want to use API keys (given by http-header, GET or Cookie, ...) I never make a call to HttpContext.SignInAsync and this is maybe the issue I can't find/google my way around.
I built an simple AuthenticationHandler (based on this - since I read that custom middlewares are not the way to go anymore) which looks something like this:
internal class CustomAuthHandler : AuthenticationHandler<CustomAuthOptions>
{
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
// parse cookies to find APIKEY
if(Context.Request.Cookies.ContainsKey("APIKEY"))
{
string APIKEY = Request.Cookies["APIKEY"];
// ... checking DB for APIKEY ...
// creating claims
var claims = new[]
{
new Claim( /* ... */ ),
// ...
};
var claimsIdentity = new ClaimsIdentity(claims);
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
var ticket = new AuthenticationTicket(claimsPrincipal, new AuthenticationProperties(), "Custom Scheme");
return AuthenticateResult.Success(ticket); // this line gets called
}
return AuthenticateResult.NoResult();
}
}
But when I have an API endpoint with just the [Authorize] attribute the DenyAnonymousAuthorizationRequirement denies the request cause the user is not allowed (cause IsAuthenticated == false which is readonly, all claims are shown properly)
Change var claimsIdentity = new ClaimsIdentity(claims); into something like var claimsIdentity = new ClaimsIdentity(claims, "Password"); (of course, instead of "Password" use the AuthenticationType that best fits your case).
Similar question here: Why is my ClaimsIdentity IsAuthenticated always false (for web api Authorize filter)?

How to C# Core OAuth without external login panel

I'm making an API for Exact Online for a website with a form. The visitor will fill in his information and after that the visitor sends it. It need to be send to the Exact online account from my client. But before that I need a accesstoken. The problem is that I don't want to give the user the login page that Exact gives me. I'm searching already for days to find a way to skip the login or to enter the login information by backend (there is always 1 login, and that is the login from my client).
Now this authorization thing is something new for me. So far I know I can call my authorization settings from the startup with this:
HttpContext.Authentication.GetAuthenticateInfoAsync("ExactOnline");
But then I get that loginscreen that I don't want. The only thing that Exact is telling me to do:
Create an app registration that supports an automated connection wizard (your provisioning process).
Is there a way to send them the login information and the visitor doesn't see a loginpage.
In my Startup.cs
var s = new OAuthOptions{
AuthenticationScheme = "ExactOnline",
ClientId = "CLIENTID",
ClientSecret = "CLIENTSECRET",
CallbackPath = new PathString("/callback"),
AutomaticAuthenticate = true,
AutomaticChallenge = true,
AuthorizationEndpoint = new Uri(string.Format("{0}/api/oauth2/auth", "https://start.exactonline.nl")).ToString(),
TokenEndpoint = new Uri(string.Format("{0}/api/oauth2/token", "https://start.exactonline.nl")).ToString(),
//Scope = { "identity", "roles" },
Events = new OAuthEvents
{
OnCreatingTicket = context =>
{
context.Identity.AddClaim(new Claim("urn:token:exactonline", context.AccessToken));
return Task.FromResult(true);
}
}
};
app.UseOAuthAuthentication(s);
First i had this code, but that gives me a null exception when i put the identity in the claimprincipal, probably because my claimsprincipal is null and i don't know why.
HttpContext.Authentication.AuthenticateAsync("ExactOnline");
var identity = new ClaimsIdentity("ExactOnline",ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);
identity.Label = "Authentication";
identity.AddClaim(new Claim(ClaimTypes.Name, "USERNAME?"));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "PASSWORD?"));
claimsPrincipal.AddIdentity(identity);
var test = HttpContext.Authentication.SignInAsync("ExactOnline", claimsPrincipal, new AuthenticationProperties() { IsPersistent = false }));
After that i tried following code, but that also didn't work. My code will continue, but the test variable will be filled with this message: The name 'InnerExceptionCount' does not exist in the current context.
var identity = new ClaimsIdentity("ExactOnline", ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);
identity.Label = "Authentication";
identity.AddClaim(new Claim("username", "USERNAME"));
identity.AddClaim(new Claim("password", "PASSWORD"));
ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(identity);
var test = HttpContext.Authentication.SignInAsync("ExactOnline", claimsPrincipal, new AuthenticationProperties() { IsPersistent = false });
Someone know how to solve this problem?

Categories