Home > Net >  Using JWT with a secret key to handle Single Sign On
Using JWT with a secret key to handle Single Sign On

Time:11-21

I have a running members only website that works as it should with login etc. I now need to support Single Sign On where I will recieve the username of the profile in a JWT token, and then log the user in. The external system that will send this request, does NOT know any other information than the Username - so the password is not shared in the token, nor is it possible.

JWT has been chosen as the method of choice to enable this. We are going to use a secret key to exchange the information, the secret key is ofcourse only know by the sender and reciever.

What I am looking for is a simple example of how this could look code-wise, both in the sender end and in the recieving end. The reciever end will be a WebAPI endpoint that will handle the login and then redirect to the frontpage of the members-only website if the login is successfull.

It is developed in C#.

CodePudding user response:

Elementary tips

  • Usually and commonly, the user/password are exchanged for a new JWT

  • JWT should not store anything sensible like a password. But some companies like Microsoft store not a sensible values but a lot of information enter image description here

  • The security platform returns a response with data in a syntax that client (c# in your case) understand if token is legit, valid, not expired, user exist, user is allowed to perform the operation, etc. I used this in some implementation in which I said: "User is allowed to perform this operation and this is its username"

    endpoint: acme-security.com/v1/oauth2/token/validate

    request: eyJ0eXAiOiJKV1Qi...

    response:

    {
    "isAllowed": true,
    "subject": "[email protected]"
    }
    
  • Also you can do this with an api gateway

    enter image description here

  • On a more real scenario, we need more than a simple jwt validation. We need to ensure that user admin/guest are allowed to execute specific endpoints. We don't want that a employee invoke directly the payment endpoint set its own salary :/

  • Follow the oauth2 spec if you will implement your own security platform

  • Jwt should be sent as Authorization Header

  • IMPORTANT: If you will implement your own jwt generation and validation, use ENV variables to hide it the secret and protect it as much as you can for your production environment

JWT handling with c# netcore 5

If the previous steps are understood, you only need one of these approaches:

  • Use some IAM or security platforms like auth0, okta, keycloack, etc
  • A simple c# web/microservice/api (that also functions as security platform) to receive token and return if user is allowed
  • A class to to the validation inside of the target microservice

JWT Generation

public string GenerateToken(User user)
{
    // generate token that is valid for 7 days
    var tokenHandler = new JwtSecurityTokenHandler();
    var key = Encoding.ASCII.GetBytes(SUPER_SECRET_KEY);
    var tokenDescriptor = new SecurityTokenDescriptor
    {
        Subject = new ClaimsIdentity(new[] { new Claim("id", user.Id.ToString()) }),
        Expires = DateTime.UtcNow.AddDays(7),
        SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
    };
    var token = tokenHandler.CreateToken(tokenDescriptor);
    return tokenHandler.WriteToken(token);
}

JWT Decode or validation

public int? ValidateToken(string token)
{
    if (token == null) 
        return null;

    var tokenHandler = new JwtSecurityTokenHandler();
    var key = Encoding.ASCII.GetBytes(SUPER_SECRET_KEY);
    try
    {
        tokenHandler.ValidateToken(token, new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(key),
            ValidateIssuer = false,
            ValidateAudience = false,
            // set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
            ClockSkew = TimeSpan.Zero
        }, out SecurityToken validatedToken);

        var jwtToken = (JwtSecurityToken)validatedToken;
        var userId = int.Parse(jwtToken.Claims.First(x => x.Type == "id").Value);

        // return user id from JWT token if validation successful
        return userId;
    }
    catch
    {
        // return null if validation fails
        return null;
    }
}

Source: https://jasonwatmore.com/post/2021/06/02/net-5-create-and-validate-jwt-tokens-use-custom-jwt-middleware

This code throws errors when token is expired or some buddy tries to send a jwt without your SUPER_SECRET_KEY

You could use these snippets directly in your token generation and inside of your endpoints

[HttpGet]
public ActionResult<List<Dictionary<string, object>>> findAllEmployees()
{
    //get jwt from http received headers
    validationResponse = ValidateToken(jwt)
    //if you have a security platform, callhere instead
    //ValidateToken(jwt)
    //if validationResponse.isAllowed ....
    var list = this.employeeService.findAllEmployees();
    return Ok(list);
}

or with a middleware like a pro, to keep clean your final controllers

Lectures

  • Related