0

I have implemented Jwt as a way to authenticate my user. However, I am stuck on how I can do certain things on my application with regards to roles. Currently my Jwt Token contains the users email, phone , id and a list of roles that they have.

What I do with that token is like this:

[TypeFilter(typeof(ValidateRolesFilter), Arguments = new object[] {
        ApplicationGlobals.ApplicationSecretKey, RoleGlobals.SystemAdministrator
})]
public IActionResult Index()
{
    return View();
}

My Typefilter contains a rest request that sends the token to another application to verify if my user can access that Function. However, I am stuck when it comes to the view. I want to segment certain containers to be allowed to be viewed by certain users with certain roles.

I have an idea that if I were to add my users claims to the signinmanager just like a non jwt application, i would be able to get the claims from the httpcontext. However, I don't know if what I have can work with an application that uses jwt.

public async Task SignInUserAsync(TIdentityUser user, bool isPersistent, IEnumerable<Claim> customClaims)
{
    var claimsPrincipal = await _signInManager.CreateUserPrincipalAsync(user);
    var identity = claimsPrincipal.Identity as ClaimsIdentity;
    var claims = (from c in claimsPrincipal.Claims select c).ToList();
    var savedClaims = claims;
    foreach (var item in claims)
    {
        identity.RemoveClaim(item);
    }
    if (customClaims != null)
    {
        identity.AddClaim(savedClaims[0]);
        identity.AddClaim(savedClaims[1]);
        identity.AddClaim(savedClaims[2]);
        identity.AddClaims(customClaims);
    }
    await _signInManager.Context.SignInAsync(IdentityConstants.ApplicationScheme,
        claimsPrincipal,
        new AuthenticationProperties { IsPersistent = isPersistent });
}
Tetsuya Yamamoto
  • 24,297
  • 8
  • 39
  • 61
JianYA
  • 2,750
  • 8
  • 60
  • 136
  • have you checked this out? https://stackoverflow.com/questions/18677837/decoding-and-verifying-jwt-token-using-system-identitymodel-tokens-jwt – Sujit.Warrier Nov 12 '18 at 06:40

2 Answers2

2

I am recently doing a cooperative project on JWT. I wrote a middlware, when ever the user request to the api, It is checked by the Authentication middleware. I read the userRole from db and put it in the identity priciple I am sharing the middleware codes.

In here I read the JWT middle part to extract the user information

public class AuthenticationMiddleware
{
    private readonly RequestDelegate _next;

    // Dependency Injection
    public AuthenticationMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        string authHeader = context.Request.Headers["Authorization"];

        if (authHeader != null)
        {              
            int startPoint = authHeader.IndexOf(".") + 1;               
            int endPoint = authHeader.LastIndexOf(".");

            var tokenString = authHeader.Substring(startPoint, endPoint - startPoint).Split(".");
            var token = tokenString[0].ToString()+"==";

            var credentialString = Encoding.UTF8
                .GetString(Convert.FromBase64String(token));

            // Splitting the data from Jwt
            var credentials = credentialString.Split(new char[] { ':',',' });

            // Trim this string.
            var userRule = credentials[5].Replace("\"", ""); 
            var userName = credentials[3].Replace("\"", "");

             // Identity Principal
            var claims = new[]
            {
                new Claim("name", userName),
                new Claim(ClaimTypes.Role, userRule),

            };
            var identity = new ClaimsIdentity(claims, "basic");
            context.User = new ClaimsPrincipal(identity);
        }
        await _next(context);
    }


}

In startup.cs you need to call this middleware in the configure method

 app.UseMiddleware<AuthenticationMiddleware>();

In the controller

 [HttpGet("GetUsers")]
  [Authorize(Roles = "admin")]
    public ActionResult GetUsers()
    {
        var users = _authRepository.GetUsers();
        return Ok(users);
    }

if You need any help please give a comment. This implementation really worked for me. Check my repositories on the subject: https://github.com/hidayatarg/Asp.net-Core-2.1-Jwt-Authentication-Middleware https://github.com/hidayatarg/Decode-JWT-Token

  • Hello, thank you for answering. I tried that method but then I needed a more customised function thats why I used the typefilter instead. – JianYA Nov 12 '18 at 22:14
  • Since Jwt sign the headers of our request. Here i am reading the header specifically the middle part jwt which is called the JWT pay load if you want to see the full exampl echeck my repo https://github.com/hidayatarg/Asp.net-Core-2.1-Jwt-Authentication-Middleware – Hidayat Arghandabi Nov 13 '18 at 07:36
0

JSON Web Tokens consist of three parts separated by dots (.), which are: Header,Payload,Signature .Therefore, a JWT typically looks like xxxxx.yyyyy.zzzzz .The second part of the token is the payload, which contains the claims.

You can decode the access token to get the claims which related to your roles :

How to decode JWT Token? .

Decoding and verifying JWT token using System.IdentityModel.Tokens.Jwt

If you are using Owin OpenID Connect middlerware to autheticate user from identity provider like Azure AD , Idenity server 4.... You can add additional claims to principal under OnTokenValidated event .

Edit :

You can also add the claims(decode and get the claims) to user context before sign- in :

 var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
 identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, loginData.Username));
 identity.AddClaim(new Claim(ClaimTypes.Name, loginData.Username));
 //add your custom claims 
 ....

 var principal = new ClaimsPrincipal(identity);
 await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties { IsPersistent = loginData.RememberMe });

Reference : http://future-shock.net/blog/post/creating-a-simple-login-in-asp.net-core-2-using-authentication-and-authorization-not-identity

Then you can access the claims in view like :

@foreach (var item in Context.User.Claims)
{
    <p>@item.Value</p> 
}; 
Nan Yu
  • 26,101
  • 9
  • 68
  • 148
  • Once I decode the token on the client side how can I store it in the httpcontext to pass it to the view? – JianYA Nov 13 '18 at 08:53
  • I’d prefer to not use cookies if possible – JianYA Nov 13 '18 at 08:53
  • @JianYA ,just store the role claim to viewbag , add show/hide the content base on that value in view . – Nan Yu Nov 13 '18 at 08:55
  • But doesn't that mean if I store the role claim to a viewbag, if i have something like a navigation menu that has links for a certain role, won't i have to add it for the entire application? – JianYA Nov 13 '18 at 11:39
  • @JianYA ,see edited reply , you could store in user claim or server side storage. – Nan Yu Nov 14 '18 at 05:13
  • Thank you for your help! I'll try this and mark it if it works. Hope it does! – JianYA Nov 14 '18 at 05:19
  • Sorry but I get this error when I implement this code: InvalidOperationException: No sign-in authentication handlers are registered. Did you forget to call AddAuthentication().AddCookies("Cookies",...)? – JianYA Nov 14 '18 at 05:25
  • ConfigureServices function : services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(); – Nan Yu Nov 14 '18 at 05:28
  • app.UseAuthentication(); in config function – Nan Yu Nov 14 '18 at 05:29
  • This is exactly what I needed. Thank you so much. I was struggling with this for days. – JianYA Nov 14 '18 at 05:34