JWT RSA & HMAC + ASP.NET Core

Recently, I was struggling with the SSO authentication. At first I did pick up JSON Web Token which of course is a legitimate option, however, I was forced to share the secret key between different parties, as I decided to use HMAC. Not so long ago I decided to switch to the RSA instead and I’d like to present you both solutions using ASP.NET Core.


 

I will not dive into the details of how HMAC or RSA work as I’m not an expert in that matter, yet there’s at least this main difference that you should be aware of. If you stick to the HMAC, you’ll be forced to share the so-called secret key among different applications (e.g. services in a microservices architecture) given that you’d like to have the same valid token for different apps. And that’s not really the best idea, which is where RSA comes in handy – the service responsible for the token generation will use the private key for signing the JWT and all of the interested parties may use the public key to ensure the token’s validity. I did implement this solution for our open source platform Collectively that we’re building in a distributed way and we need sort of SSO mechanism in place.

Before we implement anything using C#, let’s prepare all of the stuff. In order to generate the secret key for HMAC, you could use e.g. this website. Just generate some random key, so it may look like this: GRQKzLUn9w59LpXEbsESa8gtJnN3hyspq7EV4J6Fz3FjBk994r.

Next, let’s create the RSA keys. In order to do that, we’ll use openssl tool. Open your terminal and type the following commands:

It will result in creating 2 files, where the private key may look like this:

And the public one like this:

There’s one more thing before we can actually use our RSA keys within .NET Core application. We need to convert them into XML. It can be done here and before we copy our shiny XML into private-rsa-key.xml and public-rsa-key.xml files, let’s format them a little bit by using this tool. Eventually, we should have the following 2 files that we will deploy with our application:

Just keep in mind, though, that the private key is required only for the service responsible for generating the token – the other parties should only use the public key.

Eventually, we can create a new Web API project and add the following packages into the .csproj file:

Next, let’s create a new section within the apssettings.json and the C# class.

We should also create the token model itself:

Now, let’s take care of the actual JWT handler, I’ll just paste the code here and it should be rather straightforward:

As you can see, such handler could be used by all of the services, as the private RSA key part is an optional one. Inside the payload you might notice a custom claim unique_name – this one is actually required if you want to get the current username using User.Identity.Name within ASP.NET Core application.

The FromXmlString() is an extension method defined in a following way:

Credits for this one go to that guy.

Let’s move forward as we’re about to finish the whole sample. We’ll start with creating a new controlle:

Where the SignIn is a typical reqeust:

We’re almost done. Let’s open the Startup class and firstly extend the ConfigureServices():

And move on to the Configure():

That’s it. Now you should be able to send the following requests in order to obtain the token and use it to access the secured endpoint. These are the samples using cURL:

You can switch between HMAC and RSA simply be setting useRsa boolean flag to either true or false. The actual server response for the successful sign in operation should be the following:

or

The tokens can be valited here and the expiration date can be checked here, simply by coping the expires property being represented as the EPOCH ticks.

The full application and all of the source code can be downloaded from the GitHub repository. I hope that this one will get you started with JWT and SSO that can be used to achieve the robust token based authentication mechanism in your application.

18 Comments JWT RSA & HMAC + ASP.NET Core

  1. Pingback: JWT RSA & HMAC + ASP.NET Core - How to Code .NET

  2. Damian

    Masz w planach rozszerzyć samą walidację JWT o refresh_token i skrócić czas tokena uwierzytelniającego ?
    Gdy obecnie zmienimy jakieś dane w DB np zablokujemy usera to mimo wszystko będzie miał dostęp przez 3 dni zgodnie z powyższym przykładem ?

    Myślałem o użyciu Identity Server 4 obecnie aczkolwiek jeszcze nie ma pełnego wsparcia na .NET Core 2.0 ;/ I mam tutaj zgrzyt o ile rozumiem cały schemat generowania tokena, wystawienie nowego tokena na podstawie refresh_token to mimo wszystko magia dla mnie, byłbyś w stanie jakoś to opisać jakie czynności należało by wykonać aby wystawić nowy token na podstawie refresh_tokena ? W jaką strukturę tutaj uderzyć ? Lub ewentualnie może masz w planach jakiś post na blogu ?

    Reply
    1. Piotr Gankiewicz

      Identity Server 4 jest dla mnie zbyt duży i zrobienie czegoś niestandardowego wymaga masę pracy. Zastanawiałem się ostatnio nad mechanizem odświeżania tokena – na pewno jeszcze się rozpatrzę w temacie jak to inni implementują ale wstępny pomysł mam już gotowy. W bazie można trzymać prostą strukturę na zasadzie np. {token_hash, expires, refreshed}, czyli unikalny hash generowany na bazie tokena, data ekspiracji oraz flaga czy został odświeżony. Następnie pojedynczy endpoint typu POST gdzie użytkownik przekazuje token, następuje walidacja i dostaje w zwrotce nowy token. Jak to zaimplementuję to na pewno pomyślę nad postem na bloga :).

      Reply
      1. Damian

        Czyli taki schemat:
        1 Aplikacja loguje się przez API
        2 API generuje token + refresh token
        oraz zapisuje do db jako:
        – token_hash (refresh_token),
        – expires ( czas wygasniecia tokena )
        – refreshed – czy token został już odswierzony

        zwracamy token userowi
        3. User wysyła żadanie o dostęp do danych
        4. Następuje sprawdzenie czasu głównego tokena jeśli nie wygasł zwracamy dane
        – jeśli wygasł sprawdzamy czy refresh token jeszcze nie wygasł w DB jeśli tak to zwracamy użytkownikowi brak autoryzacji
        5. Jeśli token usera wygasł Aplikacja wysyła żądanie aby otrzymać nowy token
        6. API sprawdza czy refresh token nie wygasł i zwraca nowy token ?

        A co w przypadku jeśli ktoś fizycznie z przeglądarki wykradnie nasz token autoryzacyjny ? W takim przypadku dane użytkownika są narażone ?

        Czy troszkę się pogubiłem ? Mógłbyś poprawny schemat taki rozpisać jak Ty to widzisz ?

        Pozdrawiam 🙂

        Reply
        1. Piotr Gankiewicz

          1. Logowanie przez API -> zwrócenie tokena i zapisanie jakiegoś unikalnego hasha w bazie (aby nie trzymać faktycznego tokena).
          2. Prośba o odświeżenie – walidacja przez API czy token jest poprawny -> przekazanie do bazy, sprawdzenie czy refreshed == false i jeśli tak to zwrócenie nowego tokena (+ zapisanie w bazie jak w punkcie 1). W sumie w bazie tutaj już nie trzeba trzymać expires, bo to zweryfikuje API.
          3. Jeśli token wygasł to trzeba zalogować się ponownie – wygasa po prostu na bazie “expires”, nie ma różnych dat do tego co jest w tokenie, a tego co w bazie.

          Do tego stosuje się SSL, żeby nikt niczego nie wykradł.

          Reply
  3. Pingback: Dew Drop - July 24, 2017 (#2526) - Morning Dew

  4. Andrew de Rozario

    Great post! In my opinion RSA works best in scenarios where not all parties using the identity provider are going to be internal i.e third parties.

    I think everyone having the same key is less of a concern if the correct dev ops/deployment practices are in place.

    Reply
    1. Piotr Gankiewicz

      Thank you, Andrew. For sure, sharing a secret key is not a big deal if it’s used internally by the whole system, however, since e.g. only a single service should be responsible for the authentication, there’s no need to even give a chance to the other parties to be able to do the same just because they own the same secret key. And like you said RSA solves this issue and even more importantly you are able to share the public key with anyone else outside of your environment as well.

      Reply
  5. John

    Hello Piotr,

    Do you have any plans on doing tutorial how to do JWT authentication with refresh tokens on .NET Core 2.0 ?

    Greetings John

    Reply
    1. Piotr Gankiewicz

      Hi John,

      I think it should be pretty much the same with .NET Core 2.0. Speaking of refreshing the tokens, it’s something that I’m going to implement and share on my blog in a near future.

      Reply
      1. Damian

        W zasadzie w .net core 2.0 trochę zmieniła się struktura startup.cs jak i również zmieniła się trochę struktura własnych atrybutów autoryzujących.
        Tutaj 1 z wątków odnośnie autoryzacji (breaking change)
        https://github.com/aspnet/Announcements/issues/262
        Tak samo zmieniła się kwestia jak wyżej napisałem własnych atrybutów autoryzujących. Nie mogłem niestety wątku na githubie znaleźć. Ale tam jest troszkę bardziej zawiła sprawa, gdyż trzeba teraz dodać własną politykę bezpieczeństwa.

        Pozdrawiam Damian.

        Reply
          1. Piotr Gankiewicz

            Policy omawiałem w jednym z odcinków kursu na YT, natomiast cokolwiek jest związane z .NET Core 2.0 na ten moment odpada, nie ufam niczemu co jest w wersji preview i pochodzi od MS. Odświeżanie tokenów jeśli już to zrobię dla 1.1, bo logika pod spodem będzie identyczna.

  6. Damian

    Mogę zapytać dlaczego ” natomiast cokolwiek jest związane z .NET Core 2.0 na ten moment odpada, nie ufam niczemu co jest w wersji preview i pochodzi od MS” ?
    W kwartale 3 ma być pełna publikacja i w sumie niewiele czasu już zostało i zbliżamy się tak na prawdę do wersji finalnej. 2.0 Więc skąd taki dystans ? Kwestia bezpieczeństwa na produkcji, czy w samym momencie developmentu problemy i jeszcze niewielkie wsparcie community ?

    Reply
    1. Piotr Gankiewicz

      Już niejednokrotnie się przejechałem na ich wersjach alpha/beta/preview i nie zamierzam powtarzać tego błędu ponownie, więc dopóki nie wyjdzie oficjalny release to nawet nie ruszam 2.0.

      Reply
      1. Tomasz

        .NET Core 2.0 już wyszedł oficjalnie – https://blogs.msdn.microsoft.com/dotnet/2017/08/14/announcing-net-core-2-0/

        Przymierzam się do przejścia na .NET Core, jednak na razie jestem na etapie prototypownia. Trochę informacji na temat refresh token’a znalazłem tu: http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/ jednak w oparciu o WebAPI 2. Implementacja w .NET Core 2.0 na pewno by pomogła 🙂

        Reply
        1. Piotr Gankiewicz

          Tak, wiem ale jeszcze trochę czasu upłynie zanim w pełni zmigruję moje projekty na 2.0 żeby zacząć go w pełni używać, chociaż postaram się to zrobić możliwie szybko.

          Reply

Leave A Comment

Your email address will not be published. Required fields are marked *