User Authentication in ASP.NET WEB API 2 with RSA-signed JWT Tokens (part 3)

published in Android Development, iOS Development, Tutorials
by Michał Zawadzki

By now we should understand the structure and process of how JWT Tokens works. Today in our example of user authentication in ASP.NET API 2 we will deal with AuthService, which is responsible for creating, signing and verifying JWT tokens.

In the previous part we covered MembershipProvider (which downloads claims and validates the user) and RSAKeyProvider (which provides the RSA key to encrypt/decrypt our JWT token).

AuthService

AuthServiceis the most important element of our application. It’s responsible for creating JWT tokens as well as for signing and verifying an incoming ones. It has two methods (GenerateJwtToken() and ValidateToken()) and it uses both MembershipProvider (login validation, downloading claims) and RSAKeyProvider (for providing the signing key).


using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Zaven.Practices.Auth.JWT.Providers.Interfaces;
using Zaven.Practices.Auth.JWT.Services.Interfaces;
using Microsoft.IdentityModel.Tokens;

...
public class AuthService : IAuthService
    {
	...
        private readonly IMembershipProvider _membershipProvider;
        private readonly IRSAKeyProvider _rsaProvider;

        public AuthService(IMembershipProvider membershipProvider, IRSAKeyProvider rsaProvider)
        {
            _membershipProvider = membershipProvider;
            _rsaProvider = rsaProvider;
        }
	…
}

Generating the JWT Token

Here is one of the two main AuthService methods:


public async Task GenerateJwtTokenAsync(string username, string password)
        {
            if (!_membershipProvider.VerifyUserPassword(username, password))
                return "Wrong access";

            List claims = _membershipProvider.GetUserClaims(username);

            string publicAndPrivateKey = await _rsaProvider.GetPrivateAndPublicKeyAsync();
            if(publicAndPrivateKey == null)
                return "RSA key error";

            RSACryptoServiceProvider publicAndPrivate = new RSACryptoServiceProvider();            
            publicAndPrivate.FromXmlString(publicAndPrivateKey);

            JwtSecurityToken jwtToken = new JwtSecurityToken
            (
                issuer: "http://issuer.com",
                audience: "http://mysite.com",
                claims: claims,
                signingCredentials: new SigningCredentials(new RsaSecurityKey(publicAndPrivate), SecurityAlgorithms.RsaSha256Signature),
                expires: DateTime.Now.AddDays(30)
            );

            JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
            string tokenString = tokenHandler.WriteToken(jwtToken);

            return tokenString;
        }

VerifyUserPassword() (part of MembershipProvider) valides data provided by the user. If he doesn’t exist, a feedback message gets send. If the user exists, his claims are downloaded to be placed in the token later.

Next, we create the RSA key, which is delivered through RSAKeyProvider by the GetPrivateAndPublicKey() method. In this example, the key is being kept in a regular file. Note: in real-case scenarios keys should be kept in a safe place!

The object of the initiated RSACryptoServiceProvider class is a previously created key (publicAndPrivateKey).


RSACryptoServiceProvider publicAndPrivate = new RSACryptoServiceProvider();            
publicAndPrivate.FromXmlString(publicAndPrivateKey);

When we have all data, we can create the JWT token (in JSON format) as an object of JwtSecurityToken class with following properties:

  • issuer: the token sender
  • audience: defines the token receiver
  • claims: defines claims
  • expires: defines the expiration date of a token
  • signingCredentials: defines the method of data validation

Next, our JWT token has to be converted from JSON into a compact string (a character string separated by dots: xxx.yyy.zzz). To do this, create an object of JwtSecurityTokenHandlerclass and call it with WriteToken().
A such prepared Token is then sent to the user.

JWT Token validation

The second most important method of AuthService is ValidateTokenAsync(), which tries to decrypt a sent JWT token and checks its validity. If one of these two operations does not succeed, a Boolean status gets returned.


public async Task ValidateTokenAsync(string TokenString)
        {
            Boolean result = false;

            try
            {
                SecurityToken securityToken = new JwtSecurityToken(TokenString);
                JwtSecurityTokenHandler securityTokenHandler = new JwtSecurityTokenHandler();
                RSACryptoServiceProvider publicAndPrivate = new RSACryptoServiceProvider();

                string publicAndPrivateKey = await _rsaProvider.GetPrivateAndPublicKeyAsync();
                if (publicAndPrivateKey == null)
                    return result;

                publicAndPrivate.FromXmlString(publicAndPrivateKey);

                TokenValidationParameters validationParameters = new TokenValidationParameters()
                {
                    ValidIssuer = "http://issuer.com",
                    ValidAudience = "http://mysite.com",
                    IssuerSigningKey = new RsaSecurityKey(publicAndPrivate)
                };
                
                ClaimsPrincipal claimsPrincipal = securityTokenHandler.ValidateToken(TokenString, validationParameters, out securityToken);

                result = true;
            }
            catch (Exception ex)
            {
                result = false;
            }

            return result;
        }

Similarly, when signing, we create an object of type RSACryptoServiceProvider that is initiated by a key stored in a global variable. Our token in the format of a compact string has to be converted back to JSON.

A TokenValidationParameters object includes a list of parameters which will be used by a SecurityTokenHandler object during the attempt of reading the JWT token. In this case the given properties are: issuer, audience and an object of RsaSecurityKey which is responsible for decrypting.

ValidateToken() from JwtsecurityTokenHandler will attempt to read any claims. If the token is invalid or claims are unreadable, the exception will be caught and the method will return false.

user authentication in ASP.NET API 2

 

In the next part, we can move on to programming the login controller and test our application.

Sources:

Michał Zawadzki Back-end Developer

Michał is at the Back-end site of our software team, specializing in .NET web apps. Apart from being an excellent table tennis player, he’s very much into sci-fi literature and computer games.

Popular posts

Clutch Announces Zaven as Top Polish Development in  2019 Eastern European Leaders Award

Clutch Announces Zaven as Top Polish Development in 2019 Eastern European Leaders Award

Since 2011, Zaven has been providing digital solutions for global clients. Our team is composed of experts in custom development, UX/UI design, data analytics, backend integration and more. We aim to build long-term business relationships with our clients in order to create the highest quality and most innovative solutions. Clutch, a B2B market research platform, […]

Read more
How to overcame the fear of hiring outsourced developers?

How to overcame the fear of hiring outsourced developers?

There is many a legend about outsourcing which successfully keeps away entrepreneurs. Stories about software which never even existed, despite the fact that the developer had promised that everything was fine - even ahead of deadline. No communication with the developer for a long, long time and then it turns out that nobody has been working because it is Chinese New Year! These are telling examples and they are not isolated. I can only imagine the fear that grips some people when the subject of outsourcing steps out of the shadows! Let’s just deal with the most common fears of hiring outsourced developers.

Read more
Why we moved to Kotlin for Android development (and maybe you should too)

Why we moved to Kotlin for Android development (and maybe you should too)

Recently Kotlin programming is really endorsed by Google and most of the developers. We are not an exception. We moved to Kotlin for Android development and it was worth it. Here are 8 reasons why we did it.

Read more
Mobile Apps

Get your mobile app in 3 easy steps!

1

Spec out

with the help of our
business analyst

2

Develop

design, implement
and test, repeat!

3

Publish

get your app out
to the stores


back to top