Part Four: Security in React and WebApi in ASP.NET

Part Four: Security in React and WebApi in ASP.NET

Part Four: Calling the protected Web API from the React SPA with the access JWT Token Bearer Authorization

Version 1.0
Date 2022/06/15
By Nicolas Barlatier

If you missed Part One:
Part One: Installing Keycloak with Docker and Administration

If you missed Part Two:
Part Two: Securing a front-end React application

If you missed Part Three:
Part Three: Securing the ASP.NET Core C# REST Web API

To use the front-end React application within Docker, you can read:

Part One Dockerizing the TypeScript React App with NodeJS Vs NGINX with WSL2 Alpine Linux on Windows 10

GitHub Repository with React and Web API projects

  • React Version 18.1.0 with TypeScript
  • Web API with ASP.NET Core 5.0

[GitHub - nicoclau/reactwebapiaspnetcorekeycloak: React and REST Web API protected by Keycloak with…
React and REST Web API protected by Keycloak with Authorization Code Flow and JWT Token - GitHub …github.com](https://github.com/nicoclau/reactwebapiaspnetcorekeycloak "github.com/nicoclau/reactwebapiaspnetcoreke..")

You have two commits:

The first commit contains:

  • React SPA secured by the keycloak server
  • Web API secured by the Access Token with the public key from the keycloak server used to validate it

The two applications don’t communicate yet, they are only secured.

The second commit contains:

  • React SPA communicates with the Web API with the JWT Token and the CORS policy is handled

Introduction

In this 4rd and final part we will see in React how to use the access JWT token to call our protected resource: the REST Web API.

The codebase will be very simple and will not use middleware, redux often used in more production oriented application.

Here we want to see only what we need and no more to reach for our services with our JWT access Tokens.

This blog will be divided in three parts:

  • creating the HTTP Service which will help sending our request with the JWT token by HTTP
  • using the Service
  • updating the REST Web API service to handle the CORS policy

1- Creating the HTTP Service

Let’s open our React solution with our Visual Code IDE.

Remember we have the following solution structure:

We added a directory called security with the Keycloak Service.
Now we will create a new directory called services.

We get

Now let’s add the file HttpService.ts which will be responsible to send and receive our calls to the protected Web Rest API.

You notice I used the extension .ts and not .tsx because we use “pure” typescript and not a mix of typescript and JSX like we do in the React Components.

Now we have the choice, as a matter of fact, React is not very opinionated.
When it is about using HTTP we have the choice of library/framework:

We have in 2022: 10 different HTTP Libraries we can use easily in React:

[10 Best React HTTP Request Libraries in 2022 | Openbase
A comparison of the 10 Best React HTTP Request Libraries in 2022: beccaccino, hermes-js, react-http-request…openbase.com](https://openbase.com/categories/js/best-react-http-request-libraries "openbase.com/categories/js/best-react-http-..")

And also we have the Fetch API is a tool that’s built into most modern browsers on the window object (window.fetch) and enables us to make HTTP requests very easily using JavaScript promises.

In the libraries the most used one is axios. Moreover it has the Typescript definition built-in

[axios: Docs, Tutorials, Reviews | Openbase
Promise based HTTP client for the browser and node.js New axios docs website: click here Make XMLHttpRequests from the…openbase.com](https://openbase.com/js/axios "openbase.com/js/axios")

So we can hesitate between axios and Fetch API

We can find in the good article

[Consuming REST APIs In React With Fetch And Axios - Smashing Magazine
If you're a React developer who'd like to learn how you can start consuming APIs in your React applications, then this…smashingmagazine.com](https://www.smashingmagazine.com/2020/06/rest-api-react-fetch-axios/ "smashingmagazine.com/2020/06/rest-api-react..")

shows us how to use:

  • Axios
  • Fetch API

To know what is the best solution another article can help:

[Axios vs. fetch(): Which is best for making HTTP requests? - LogRocket Blog
Axios is not always an ideal solution; depending on your needs, there are sometimes better options for making HTTP…blog.logrocket.com](https://blog.logrocket.com/axios-vs-fetch-best-http-requests/#:~:text=To%20send%20data%2C%20fetch%28%29,stringify%20method "blog.logrocket.com/axios-vs-fetch-best-http..")

  • Axios is more compatible to a lot of browser than Fetch API. Axios uses XMLHttpRequest behind the scenes. It wraps it to help us.
  • Axios in the basic syntax we will use is better to post data and to receive the response already parsed in Javascript Object. With Fetch API we need to call Response.json() to parse the response and return the JavaScript object.
  • With Fetch API we have more things to set up for the same task.
  • Axios is far easier to use to handle the timeout problem.
  • Axios gives use the possibility of interception the HTTP request to add our JWT Token
  • Axios can provide a Download Progression
  • Axios can provide several request in //

We need to add easily our JWT Token, so axios is better.

So let’s install axios with our usual npm!

In the Visual Code terminal

Use the command:

npm i axios

Now we have axios:

But before we need to update our KeyCloakService by adding :

  • IsLoggedIn
  • GetToken
  • UpdateToken

GetToken is pretty obvious, we need to get the access JWT Token.
Then we can use it to add it in our HTTP request in its header : Bearer so we can use the Bearer Authorization.

For the other methods we will see why what they are for in the source code of our HttpService.

HttpService which will configure axios to add the Access JWT Token in each HTTP request

We have in the HttpService three main methods:

  • configure : we will use it as soon as our React app is loading to set up our axios instance to add the Token returned by the other service KeyCloakService. We use the notion of Interceptors of Axios at the Request level which will automatically add, for any request, our JWT Token anywhere we call this axio instance in our codebase
  • getAxiosClient: returns the axios instance anywhere in the code base
  • HttpMethods: just a helper method to send the HTTP method

You can see more info about the axio Interceptors:

[Interceptors
You can intercept requests or responses before they are handled by then or catch. If you need to remove an interceptor…axios-http.com](https://axios-http.com/docs/interceptors "axios-http.com/docs/interceptors")

Let’s explain now more the method configure

It’s divided in two main parts:

  • We create a function called cb (configure bearer) which checks if the user is authenticated with keycloak : if true we set the axios to send the header “Bearer”.
    This function is not called yet!
  • It will be called only if we get a success in return from the UpdateToken call. In UpdateToken, the token is updated only when the token is about to expire within 5 seconds: see the code of Updatetoken of the other Service KeyCloakService, otherwise the token is not updated and we return success.
    So when the token is not updated ==> success, when token expires and update is ok ==> success, otherwise ==> ko

2- Using the HTTP Service

Let’s use our HttpService in the Index.tsx

We get the following code :

We call HttpService after the KeyCloak Service because HttpService depends on the Keycloak to check if the user is authenticated and to get the Access JWT Token.

Let’s run and check if our SPA react is running smoothly.

We are ready now to add our new feature “WeatherCast” in our App component in the App.tsx file:

  • To call our protected Rest Web Api to get the weather by sending the Token returned by our keycloak service.

Let’s add a simple button called “WeatherCast” which will call our Web Api.

We just included our HttpService then called the method getAxiosClient() to get the axios instance with the request interceptor adding the JWT Token if our user is properly authenticated by our Keycloak Server of course.

We will make sure to use the IP of our machine.

So we have two applications running on the same machine as we are on dev but we have different protocol (http vs https) and different ports.

So that way we get close to the real situation where we have services on different machines. We will see that is enough to get the CORS protection which will block us ! :-)

We will see what to do to solve the problem.
By the way on Keycloak server we already solved this problem by setting up the CORS with

Where we tell our Keycloak Server that our client react can reach it with XmlHttpRequests.

Remember the CORS checks 3 parts:

  • scheme
  • domain/subdomain
  • port

Please look at this article to get a good understanding of this protection.

[Understanding CORS
If you ever worked with an AJAX call, you are probably familiar with the following error displayed in browser console:medium.com](https://medium.com/@baphemot/understanding-cors-18ad6b478e2b "medium.com/@baphemot/understanding-cors-18a..")

And a very detailed documentation from Developer Mozilla which is brillant!

[Cross-Origin Resource Sharing (CORS) - HTTP | MDN
Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins…developer.mozilla.org](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#preflighted_requests "developer.mozilla.org/en-US/docs/Web/HTTP/C..")

By the way I help other authors on medium with my links ! :)

When we try to click on the button we get the error:

Not cool, what happened?

When we check the console of our chrome dev tool we see:

Access to XMLHttpRequest at ‘https://10.7.7.11:5001/WeatherForecast' from origin ‘http://10.7.7.11:3000' has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

ok basically the request to our webapi from our react app has been blocked by the CORS policy of our chrome browser.

Why so ? Because the web api server did not send back in the response the expected header “Allow-Control-Allow-Origin” with the value the URI of the client : our react SPA app.

Ok before we solve the CORS problem let’s have a look at the network exchange between our chrome, keycloak server and our web api app:

Access JWT Token in the Bearer Authorization

We can check that our service HttpService in react did the job correctly by sending the Access JWT Token for the Bearer Authentication 😃👍

So for the CORS we have again a dance of request/response(s). Let’s check with Fiddler

  • First chrome detects we need to send our ajax request to another scheme protocol (https) and different port, same IP. So it sends the OPTIONS method HTTP request to the server to ask for it if the SPA react app is allowed to do this ajax request.
    It is the Preflight request for any “not simple” request (please see the links about CORS for more information below)

[Preflight request - MDN Web Docs Glossary: Definitions of Web-related terms | MDN
A CORS preflight request is a CORS request that checks to see if the CORS protocol is understood and a server is aware…developer.mozilla.org](https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request "developer.mozilla.org/en-US/docs/Glossary/P..")

OPTIONS 10.7.7.11:5001/WeatherForecast HTTP/1.1
Host: 10.7.7.11:5001
Connection: keep-alive
Accept: */*
Access-Control-Request-Method: GET
Access-Control-Request-Headers: authorization
Origin: http://10.7.7.11:3000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
Sec-Fetch-Dest: empty
Referer: http://10.7.7.11:3000/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,es;q=0.8,fr;q=0.7,zh-CN;q=0.6,zh;q=0.5,nl;q=0.4

Basically the chrome ask the server if the app is allowed to use the Request Method : GET at the URI. Chrome ask this by using the Request Method: OPTIONS.

The server is our Web Api and for now it doesn’t have a clue about this CORS thing. So it will answer :

HTTP/1.1 405 Method Not Allowed
Date: Thu, 16 Jun 2022 13:28:41 GMT
Server: Kestrel
Content-Length: 0
Allow: GET

It answered : “The computer says no!”

Very famous TV show “Little Britain” 😆

So why no ?

Well basically we have to tell our server Web Api application to allow our React SPA to ask for our resource with the GET method or any other method like POST.

So we have to go back to our Web Api project.

3- Updating the REST Web API service to handle the CORS policy

To enable the CORS middleware so we can handle the Options HTTP request it is very simple.

We just configure the CORS service and tell it that we allow our React SPA application (identified by its URI) to use any method and header for reaching our endpoints in the Web API.

Once the CORS service is set up, we enable the CORS middleware in the pipeline. We tell the middleware to use the settings by referencing them by its name here “MyPolicy”

We can find all the details on the microsoft documentation for the ASP.NET Core 5.0:

[Enable Cross-Origin Requests (CORS) in ASP.NET Core
Learn how CORS as a standard for allowing or rejecting cross-origin requests in an ASP.NET Core app.docs.microsoft.com](https://docs.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-5.0 "docs.microsoft.com/en-us/aspnet/core/securi..")

Once we did that, first make sure the chrome browser allow the access to our Web Api by accepting the HTTPS self-signed certificate. Otherwise our React app will get an error if the certificate is not trusted by the Chrome browser.

If you see:

Click on “Advanced” then on “Proceed to ….”

Then you will see

That way you are sure our React App will be able to reach for the Web Api with our Chrome browser by using Ajax.

We can see that we turned off the security warning, we can rollback anytime if we need to.

Let’s try again our React app:

We made it!

Let’s have a look at Fiddler:

Again you need to go to the WebApi with Chrome and tell it to ignore the warning, because Fiddler use another certificate.

Let’s go to React app and click on the button “WeatherCast”

We get the two calls:

First Request/Response

OPTIONS 10.7.7.11:5001/WeatherForecast HTTP/1.1
Host: 10.7.7.11:5001
Connection: keep-alive
Accept: */*
Access-Control-Request-Method: GET
Access-Control-Request-Headers: authorization
Origin: http://10.7.7.11:3000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
Sec-Fetch-Dest: empty
Referer: http://10.7.7.11:3000/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,es;q=0.8,fr;q=0.7,zh-CN;q=0.6,zh;q=0.5,nl;q=0.4

HTTP/1.1 204 No Content
Date: Thu, 16 Jun 2022 13:53:23 GMT
Server: Kestrel
Access-Control-Allow-Headers: authorization
Access-Control-Allow-Methods: GET
Access-Control-Allow-Origin: http://10.7.7.11:3000

This time our Web Api app on the Kestrel server returns the expecter CORS header to say: yeah it is allowed to use the GET.

In the second call we get the following Request/Response

GET 10.7.7.11:5001/WeatherForecast HTTP/1.1
Host: 10.7.7.11:5001
Connection: keep-alive
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="102", "Google Chrome";v="102"
Accept: application/json, text/plain, */*
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ1RXJacF9SUjJsRVRiN205a2tmMTJOel8wWjJiaVAyVVZBSDNiRTRfNG5VIn0.eyJleHAiOjE2NTUzODc4NDYsImlhdCI6MTY1NTM4NzU0NiwiYXV0aF90aW1lIjoxNjU1MzgzOTU4LCJqdGkiOiI2YmJhYjVjYy0wMzI5LTRmMWMtYTMxZS05Y2NmMjdkNjg3MDYiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvcmVhbG1zL015UmVhbG0iLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiMWVmM2UwMmItNTUwMy00YmIwLWI3NDgtNzM4NGZkM2ExOTc1IiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiTXlBcHAiLCJub25jZSI6IjYwZDRhOWJmLWVjNTAtNDNkOS05MTI5LTk3YWRlZDFlMmQ5ZiIsInNlc3Npb25fc3RhdGUiOiI1YTEyMjRiYy0yNzk4LTRhMjEtOTAwNC1lNDFlODNiZGYyMjIiLCJhY3IiOiIwIiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHA6Ly8xMC43LjcuMTE6MzAwMCJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1teXJlYWxtIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7Ik15QXBwIjp7InJvbGVzIjpbIkFkbWluIl19LCJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwic2lkIjoiNWExMjI0YmMtMjc5OC00YTIxLTkwMDQtZTQxZTgzYmRmMjIyIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJuaWNvbGFzIn0.e_QiJXIdzQicLpn_wCG0c3idaXokfAu75vpcUd91dRep7YZyPX1el0W4BqC7pm_fKHdwtvYT71RkVs9WmQIoT-JrwmhLzkM_fbjZt0ISK5jan7tS5qJ6gBeKj7wscV1qtXT1BaZ0RWppzm1e-Pfd0QA3NYivdTnqEeSdAUM-hE1Z1_w-COtipVhPeH_K27TEOT-opsgbn9kjOpTeauJ1sX7egAXZzRQrposRO3YGF9cE_92cwuFMzY1qlkdaB8HIYqqXxuL3LhExnwXPyrEAMVBz2k9NUzniboefFnsL36k6uqtmWC_x6ZrcLCi6zz0no9oPqeaXJXA1cwaS72QIRg
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36
sec-ch-ua-platform: "Windows"
Origin: http://10.7.7.11:3000
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://10.7.7.11:3000/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,es;q=0.8,fr;q=0.7,zh-CN;q=0.6,zh;q=0.5,nl;q=0.4

HTTP/1.1 200 OK
Date: Thu, 16 Jun 2022 13:53:23 GMT
Content-Type: application/json; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked
Access-Control-Allow-Origin: http://10.7.7.11:3000

1f4
[{"date":"2022-06-17T15:53:24.5701743+02:00","temperatureC":-8,"temperatureF":18,"summary":"Chilly"},{"date":"2022-06-18T15:53:24.5701806+02:00","temperatureC":49,"temperatureF":120,"summary":"Warm"},{"date":"2022-06-19T15:53:24.5701811+02:00","temperatureC":19,"temperatureF":66,"summary":"Scorching"},{"date":"2022-06-20T15:53:24.5701814+02:00","temperatureC":-1,"temperatureF":31,"summary":"Balmy"},{"date":"2022-06-21T15:53:24.5701818+02:00","temperatureC":17,"temperatureF":62,"summary":"Warm"}]
0

We sent sent our request with the GET Http method, the JWT Token.
Our Web Api validated our JWT Token and returned the response as CORS was validated too!

Conclusion

Phew ! We made at last the pretty long travel with Keycloak, Code Authorization Flow, React application and ASP.NET 5.0 REST Web Api application with the Authentification and Authorization.

I hope these 4 parts help you to go faster in your own production project and make secure your services more easily.

Thank you for having read my articles till this point! I hope they were clear and easy to follow and reproduce in code.

Please if any part of the articles was incorrect or confusing, don’t hesitate to ask me in the comment part! 😃

References

[10 Best React HTTP Request Libraries in 2022 | Openbase
A comparison of the 10 Best React HTTP Request Libraries in 2022: beccaccino, hermes-js, react-http-request…openbase.com](https://openbase.com/categories/js/best-react-http-request-libraries "openbase.com/categories/js/best-react-http-..")

[axios: Docs, Tutorials, Reviews | Openbase
Promise based HTTP client for the browser and node.js New axios docs website: click here Make XMLHttpRequests from the…openbase.com](https://openbase.com/js/axios "openbase.com/js/axios")

[Consuming REST APIs In React With Fetch And Axios - Smashing Magazine
If you're a React developer who'd like to learn how you can start consuming APIs in your React applications, then this…smashingmagazine.com](https://www.smashingmagazine.com/2020/06/rest-api-react-fetch-axios/ "smashingmagazine.com/2020/06/rest-api-react..")

[Understanding CORS
If you ever worked with an AJAX call, you are probably familiar with the following error displayed in browser console:medium.com](https://medium.com/@baphemot/understanding-cors-18ad6b478e2b "medium.com/@baphemot/understanding-cors-18a..")

[Preflight request - MDN Web Docs Glossary: Definitions of Web-related terms | MDN
A CORS preflight request is a CORS request that checks to see if the CORS protocol is understood and a server is aware…developer.mozilla.org](https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request "developer.mozilla.org/en-US/docs/Glossary/P..")

[Cross-Origin Resource Sharing (CORS) - HTTP | MDN
Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins…developer.mozilla.org](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#preflighted_requests "developer.mozilla.org/en-US/docs/Web/HTTP/C..")