Generating a JWT token using AuthenticateAsync - jwt

I am trying to login using ClaimsPrincipal and then fetch a JWT in .net core 2.0. With my current code, I get the error from the result of the SignInAsync function:
"No IAuthenticationSignInHandler is configured to handle sign in for the scheme: Bearer"
Here is the controller I am currently using:
[Route("Login/{username}")]
public async Task<IActionResult> Login(string username)
{
var userClaims = new List<Claim>
{
new Claim(ClaimTypes.Name, username)
};
var principal = new ClaimsPrincipal(new ClaimsIdentity(userClaims));
var sign = HttpContext.SignInAsync(principal);
await sign;
var res = await HttpContext.AuthenticateAsync();
var token = await HttpContext.GetTokenAsync("access_token");
return Json(token);
}
The login portion was tested and works well with cookies. However when I use the following code with JwtBearerDefaults.AuthenticationScheme in my startup.cs:
services.AddAuthentication(config => {
config.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
config.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(config =>
{
config.TokenValidationParameters = Token.tokenValidationParameters;
config.RequireHttpsMetadata = false;
config.SaveToken = true;
});
I get the error from the result of the SignInAsync function:
"No IAuthenticationSignInHandler is configured to handle sign in for the scheme: Bearer"
My Token class was created with the help of a code I found online (at JWT on .NET Core 2.0) and is defined as follows:
public static class Token
{
public static TokenValidationParameters tokenValidationParameters {
get
{
return new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = GetSignInKey(),
ValidateIssuer = true,
ValidIssuer = GetIssuer(),
ValidateAudience = true,
ValidAudience = GetAudience(),
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
}
}
static private SymmetricSecurityKey GetSignInKey()
{
const string secretKey = "very_long_very_secret_secret";
var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
return signingKey;
}
static private string GetIssuer()
{
return "issuer";
}
static private string GetAudience()
{
return "audience";
}
}

If I understand it correctly from looking at the source code for JwtBearerHandler, it does not implement IAuthenticationSignInHandler, which is why you are getting this error. Call to SignInAsync is designed to persist authentication information, such as created auth cookie which, for instance, is exactly what CookieAuthenticationHandler does. But for JWT there is no single well-known place to store the token, hence no reason to call SignInAsync at all. Instead of that, grab the token and pass it back to the browser. Assuming you are redirecting, you can tuck it into a query string. Assuming browser application is an SPA (i.e. Angular-based) and you need tokens for AJAX calls, you should store token in the SPA and send it with every API request. There are some good tutorials on how to use JWT with SPAs of different types, such as this: https://medium.com/beautiful-angular/angular-2-and-jwt-authentication-d30c21a2f24f
Keep in mind that JwtBearerHandler expects to find Authentication header with Bearer in it, so if your AJAX calls are placing token in query string, you will need to supply JwtBearerEvents.OnMessageReceived implementation that will take token from query string and put it in the header.

A signed token can be created using the JwtSecurityTokenHandler.
var handler = new JwtSecurityTokenHandler();
var jwt = handler.CreateJwtSecurityToken(new SecurityTokenDescriptor
{
Expires = DateTime.UtcNow.Add(Expiary),
Subject = new ClaimsIdentity(claims, "local"),
SigningCredentials = new SigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256)
});
return handler.WriteToken(jwt);

Related

Authorize attribute authorizing any JWT token for the controller in asp.net core?

I am trying to use JWT authentication in my ASP.NET CORE project.
Step-1: I have added the JWT service in ConfigureServices method of Starup.cs file.
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:SecretKey"])),
RequireExpirationTime = true,
ValidateLifetime = true,
ValidateAudience = false,
ValidateActor = false,
ValidateIssuer = false
};
});
And added below code in the Configure method:
app.UseAuthentication();
Step-2: Sending the jwt token while login.
public class LoginRepository
{
public LoginRepository()
{
//TODO: Dependency to MongoDB will be initialized here
}
public LoginStatus Authenticate(string username, string password)
{
LoginStatus loginStatus = new LoginStatus();
string secretKey = ConfigurationManager.AppSetting["Jwt:SecretKey"];
int tokenExpirationHours = int.Parse(ConfigurationManager.AppSetting["Jwt:TokenExpirationHours"]);
//TODO: Need to add the userID in the payload. UserID will come from Database
Dictionary<string, string> payload = new Dictionary<string, string>() {
{ "UserName", username}
};
//TODO: Need to check the username and password in Database and then generate the token
loginStatus.Token = JwtTokenHelper.GenerateJwtToken(secretKey, payload, tokenExpirationHours);
return loginStatus;
}
}
Here is the JwtTokenHelper:
public class JwtTokenHelper
{
public static string GenerateJwtToken(string secretKey, IReadOnlyDictionary<string, string> payloadContents, int tokenExpirationHours)
{
JwtSecurityTokenHandler jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
var payloadClaims = payloadContents.Select(c => new Claim(c.Key, c.Value));
var payload = new JwtPayload("", "", payloadClaims, DateTime.Now, DateTime.Now.AddHours(tokenExpirationHours));
var header = new JwtHeader(signingCredentials);
var securityToken = new JwtSecurityToken(header, payload);
return jwtSecurityTokenHandler.WriteToken(securityToken);
}
}
Here, I am getting the JWT token successfully.
Step-3: Now, I tried to authorize a controller and it is working nice when I have given the token in the Authorization header from Postman.
namespace SampleAPI.Controllers
{
[Authorize]
[Produces("application/json")]
[Route("api/Test")]
public class TestController : Controller
{
[HttpGet]
[Route("Testing")]
public IActionResult Testing()
{
return Ok("Yes");
}
}
}
But, if I change something in the JWT token and again hit this endpoint, it is returning "Yes" means it is saying that the jwt token is valid. But I have changed some parts of that token before sending in the Authorization header.
What am I missing here? Can you please point me out what more steps should I follow?

Blazor jwt from client to server

I already have the token in local storage and ready to send to the web api where the controller or the method has en Authorize attribute this es the
Blazor client, How do I send the token ?
var token = Storage["token"];
await http.GetJsonAsync<string[]>("/api/authorizedController");
And how do I recive the token on the api ?
Does it happens automatically or do I have to do somehting ?
[Authorize]
[Route("api/[controller]")]
I found the answer here on stackoverflow in several places I just did not know how to look for it all I needed to do was to add this line of code
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
and it looked like this all together
var token = Storage["token"];
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
await http.GetJsonAsync<string[]>("/api/AutorizedController");
Mate, you also need code on the server to validate the bearer token in your request header on each request.
Try this:
[Route("api/[controller]")]
[Authorize]
public class AutorizedController: Controller
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityCore<IdentityUser>()
.AddEntityFrameworkStores<StoreContext>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(cfg =>
{
cfg.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidIssuer = _config["Security:Tokens:Issuer"],
ValidateAudience = true,
ValidAudience = _config["Security:Tokens:Audience"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Security:Tokens:Key"])),
};
});
services.AddDbContext<StoreContext>();
services.AddMvc();
}

Good algorithm for downloading and caching public signing key for token validation

In a katana web api, I'm using:
appBuilder.UseIdentityServerBearerTokenAuthentication(
new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "https://...",
ValidationMode = ValidationMode.Local,
RequiredScopes = new[] { "..." },
});
This appears to nicely find the public signing key(s) from the authority and (hopefully?) cache them, etc. Although I haven't tried it, I understand there's an equivalent for ASP.NET Core.
Now I need to do the same thing but not in a web api middleware. So I'm trying to find the code that IdentityServer3.AccessTokenValidation.IdentityServerBearerTokenValidationMiddleware uses to do this. All I can see is that it calls UseOAuthBearerAuthentication, which seems to be in Microsoft.Owin.Security.OAuth. I haven't been able to find a version of that source code that seems to match the signature.
It seems to me that under the covers, somebody is probably using System.IdentityModel.Tokens.JwtSecurityTokenHandler and putting a nice little snippet of code into the IssuerSigningKeyResolver of the TokenValidationParameters. That nice little snippet is getting the signing keys from the metadata address. Anybody know what that code is, or have a piece that works well? Obviously, I could write it but I hate to re-invent the wheel, plus mine would be un-tested.
We are using this class that plugs into the JWT handler:
https://github.com/IdentityServer/IdentityServer3.AccessTokenValidation/blob/master/source/AccessTokenValidation/Plumbing/DiscoveryDocumentIssuerSecurityTokenProvider.cs
Thanks, leastprivilege. Looking deeper at your DiscoverydocumentIssuerSecurityTokenProvider class, I found ConfigurationManager<OpenIdConnectConfiguration>. Using that, I have come up with the following helper class for access token validation outside of OWIN middleware.
Feedback solicited!
using Microsoft.IdentityModel.Protocols;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Security.Claims;
namespace WpfClient
{
public class AccessTokenValidator
{
protected TokenValidationParameters _accessTokenValidationParameters;
public AccessTokenValidator(string stsRoot)
{
stsRoot = stsRoot.TrimEnd('/');
var discoveryEndpoint = stsRoot + "/.well-known/openid-configuration";
var webHandler = new WebRequestHandler();
var httpClient = new HttpClient(webHandler);
var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(discoveryEndpoint, httpClient);
_accessTokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = stsRoot,
RequireSignedTokens = true,
ValidateIssuerSigningKey = true,
IssuerSigningKeyResolver = (string token, SecurityToken securityToken, SecurityKeyIdentifier keyIdentifier, TokenValidationParameters validationParameters) => {
var signingTokens = configurationManager.GetConfigurationAsync().Result.JsonWebKeySet.GetSigningTokens();
foreach (var signingToken in signingTokens)
{
foreach (var clause in keyIdentifier)
{
var key = signingToken.ResolveKeyIdentifierClause(clause);
if (key != null)
{
return key;
}
}
}
return null;
},
RequireExpirationTime = true,
ValidateAudience = false, // See https://github.com/IdentityServer/IdentityServer3/issues/1365: "OAuth2 does not use the term 'audience' ... it instead uses the term 'scope' ... 'audience' and the 'aud' claim are JWT specific concepts."
ValidateLifetime = true,
};
}
public void ValidateAccessToken(string accessToken, IEnumerable<string> requiredScopes, IEnumerable<string> requiredRoles)
{
ClaimsPrincipal claimsPrincipal;
SecurityToken securityToken;
var handler = new JwtSecurityTokenHandler();
claimsPrincipal = handler.ValidateToken(accessToken, _accessTokenValidationParameters, out securityToken);
if (claimsPrincipal == null)
{
throw new NullReferenceException("ClaimsPrincipal object returned is null");
}
RequireClaims("scope", requiredScopes, claimsPrincipal);
RequireClaims("role", requiredRoles, claimsPrincipal);
}
private static void RequireClaims(string type, IEnumerable<string> requiredValues, ClaimsPrincipal claimsPrincipal)
{
if (requiredValues != null)
{
var haveClaims = claimsPrincipal.FindAll(type);
var missingRequiredValues = requiredValues.Where(s => !haveClaims.Any(c => c.Value.Equals(s, StringComparison.InvariantCultureIgnoreCase))).ToArray();
if (missingRequiredValues.Any())
{
var list = string.Join(", ", missingRequiredValues);
throw new InvalidDataException($"Missing required {type} claims: {list}");
}
}
}
}
}
I liked the idea of the helper class, but jon-brichoux's implementation still has a lot of code (e.g. iterating over signingTokens) you'd think would be handled by libraries. Microsoft's libraries also have a lot of solid code including refreshing the keys regularly. That implementation looks to be similar to what's recommended by Microsoft at Obtaining SecurityKeys for Validation Dynamically.
A blog post, Manually validating a JWT using .NET, is good but it hits the server and gets the metadata on every validation. Here's what I came up with that seems to work as expected.
You will need to reference the Microsoft.IdentityModel.Protocols.OpenIdConnect and Microsoft.IdentityModel.Protocols NuGet packages.
public class JwtTokenValidator
{
protected static TokenValidationParameters _validationParameters;
public JwtTokenValidator(string metadataAddress, string audience)
{
_validationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = audience,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ConfigurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(metadataAddress, new OpenIdConnectConfigurationRetriever()),
};
}
public void ValidateToken(string token)
{
var jsonWebTokenHandler = new JsonWebTokenHandler();
var tokenValidationResult = jsonWebTokenHandler.ValidateToken(token, _validationParameters);
if (!tokenValidationResult.IsValid)
{
// Handle each exception which tokenValidationResult can contain as appropriate for your service
// Your service might need to respond with a http response instead of an exception.
if (tokenValidationResult.Exception != null)
throw tokenValidationResult.Exception;
throw new InvalidOperationException("invalid token"); // TODO: throw an application-appropriate exception
}
}
}
Usage:
// cache single instance at application initialization
JwtTokenValidator jwtTokenValidator = new JwtTokenValidator($"https:.../.well-known/openid-configuration", "[audience]");
// live check in controller or wherever
jwtTokenValidator.ValidateToken("eyJ0eX...");

ServiceStack Authenticates both iOS Apps when one is logged in

I'm using the awesome ServiceStack to implement my REST backend which serves two iPhone apps written in Xamarin. Everything works great but i'm struggling in getting sessions to work correctly when the two apps are installed on the same device !
The issue is that if I login in one of the apps the second app gets authenticated and doesn't require me to login as a result of 'isCurrentUserAuthenticated()' method below.
I pass cookies with my requests to mimic the browser and to make sure user doesn't have to pass his credentials every time but I guess the problem is that maybe ServiceStack sees two authentication requests from the same IP so it authenticated them both using the first authentication requests succeeds.
Note : The two apps accesses the same database and UserAuth table but every app supports a user role different than the other.
The only way to fix it is to logout from the second app so the user can login again with his credentials to make everything work.
Can you please help with this ?
Here is the code so far :
public static class BLL
{
public static JsonServiceClient ServiceClient { get; set; }
public static string HostUri = "http://test.elasticbeanstalk.com";
public static string HostDomain = "test.elasticbeanstalk.com";
static BLL ()
{
string ss_id = ConfigRepository.GetConfigString ("ss-id");
string ss_pid = ConfigRepository.GetConfigString ("ss-pid");
ServiceClient = new JsonServiceClient (HostUri);
ServiceClient.CookieContainer.Add (new Cookie ("ss-id", ss_id, "/", HostDomain));
ServiceClient.CookieContainer.Add (new Cookie ("ss-pid", ss_pid, "/", HostDomain));
}
public static async Task<bool> isCurrentUserAuthenticated ()
{
bool result = false;
try {
Authenticate authRequest = new Authenticate ();
// Restore the cookie
var response = await ServiceClient.PostAsync<AuthenticateResponse> (authRequest);
NSUserDefaults.StandardUserDefaults.SetString (response.UserId, "UserId");
NSUserDefaults.StandardUserDefaults.Synchronize ();
result = true;
} catch (Exception Ex) {
result = false;
}
return result;
}
public static async Task<AuthenticateResponse> Login (string userName, string password)
{
Authenticate authRequest = new Authenticate () {
provider = "credentials",
UserName = userName,
Password = password,
RememberMe = true,
};
var response = await ServiceClient.PostAsync<AuthenticateResponse> (authRequest);
var cookies = ServiceClient.CookieContainer.GetCookies (new Uri (HostUri));
if (cookies != null) {
var ss_id = cookies ["ss-id"].Value;
var ss_pid = cookies ["ss-pid"].Value;
if (!ss_id.IsNullOrEmpty ()) {
int r = ConfigRepository.AddConfigKey ("ss-id", ss_id);
System.Diagnostics.Debug.WriteLine ("ss-id " + ss_id.ToString ());
}
if (!ss_pid.IsNullOrEmpty ()) {
int r = ConfigRepository.AddConfigKey ("ss-pid", ss_pid);
System.Diagnostics.Debug.WriteLine ("ss-pid " + ss_pid.ToString ());
}
}
NSUserDefaults.StandardUserDefaults.SetString (response.UserId, "UserId");
NSUserDefaults.StandardUserDefaults.Synchronize ();
return response;
}
public static async Task<AuthenticateResponse> Logout ()
{
Authenticate authRequest = new Authenticate () {
provider = "logout"
};
var response = await ServiceClient.PostAsync<AuthenticateResponse> (authRequest);
return response;
}
}
The issue is because you're using the same Session Cookies with a shared ServiceClient instance which ends up referencing the same Authenticated Users Session.
ServiceStack Sessions are only based on the session identifiers (ss-id/ss-pid) specified by the clients cookies, if you use the same cookies you will be referencing the same Authenticated Users Session, they're not affected by IP Address or anything else.
If you want to authenticate as another user, use a new instance of the ServiceClient (so it's not using an existing Sessions Cookies).

DotNetOpenAuth Claimed Identifier from Facebook is never the same

I'm using DotNetOpenAuth v3.5.0.10357 and each time a user authenticates against Facebook I get a different claimed identifier back. The token looks to be encrypted so I assume DNOA is somehow encrypting the token along with the expiry. Can anyone confirm this? Or am I using it wrong:
public ActionResult FacebookLogOn(string returnUrl)
{
IAuthorizationState authorization = m_FacebookClient.ProcessUserAuthorization();
if (authorization == null)
{
// Kick off authorization request
return new FacebookAuthenticationResult(m_FacebookClient, returnUrl);
}
else
{
// TODO: can we check response status codes to see if request was successful?
var baseTokenUrl = "https://graph.facebook.com/me?access_token=";
var requestUrl = String.Format("{0}{1}", baseTokenUrl, Uri.EscapeDataString(authorization.AccessToken));
var claimedIdentifier = String.Format("{0}{1}", baseTokenUrl, authorization.AccessToken.Split('|')[0]);
var request = WebRequest.Create(requestUrl);
using (var response = request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
var graph = FacebookGraph.Deserialize(responseStream);
var token = RelyingPartyLogic.User.ProcessUserLogin(graph, claimedIdentifier);
this.FormsAuth.SignIn(token.ClaimedIdentifier, false);
}
}
return RedirectAfterLogin(returnUrl);
}
}
Here's the code for FacebookAuthenticationResult:
public class FacebookAuthenticationResult : ActionResult
{
private FacebookClient m_Client;
private OutgoingWebResponse m_Response;
public FacebookAuthenticationResult(FacebookClient client, string returnUrl)
{
m_Client = client;
var authorizationState = new AuthorizationState(new String[] { "email" });
if (!String.IsNullOrEmpty(returnUrl))
{
var currentUri = HttpContext.Current.Request.Url;
var path = HttpUtility.UrlDecode(returnUrl);
authorizationState.Callback = new Uri(String.Format("{0}?returnUrl={1}", currentUri.AbsoluteUri, path));
}
m_Response = m_Client.PrepareRequestUserAuthorization(authorizationState);
}
public FacebookAuthenticationResult(FacebookClient client) : this(client, null) { }
public override void ExecuteResult(ControllerContext context)
{
m_Response.Send();
}
}
Also, I am using the RelyingPartyLogic project included in the DNOA samples, but I added an overload for ProcessUserLogin that's specific to facebook:
public static AuthenticationToken ProcessUserLogin(FacebookGraph claim, string claimedIdentifier)
{
string name = claim.Name;
string email = claim.Email;
if (String.IsNullOrEmpty(name))
name = String.Format("{0} {1}", claim.FirstName, claim.LastName).TrimEnd();
return ProcessUserLogin(claimedIdentifier, "http://facebook.com", email, name, claim.Verified);
}
It looks as though FacebookClient inherits from WebServerClient but I looked for the source on GitHub and I don't see a branch or a tag related (or at least not labeled) with the corresponding v3.5 version.
Facebook does not support OpenID. Claimed Identifier is an OpenID term. Facebook uses OAuth 2.0, so you're mixing up OpenID and OAuth.
Facebook sends a different access token every time, which is normal for the OAuth protocol. You have to use the access token to query Facebook for the user id that is consistent on every visit.
I think you need to add the offline_access permission in the token request as well, see https://developers.facebook.com/docs/reference/api/permissions/