Part Two: Security in React and WebApi in ASP.NET Core C# with authentification and authorization by KeyCloak

Part Two: Security in React and WebApi in ASP.NET Core C# with authentification and authorization by KeyCloak

Part two: Securing a front-end React application

Version 1.0
Date 2022/05/29
By Nicolas Barlatier

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

The next part is available:
Part three: Securing the ASP.NET Core C# REST Web

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

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

The article will show you how to secure a front-end React single page application with authentication and authorization.

It will be divided in three main sections:

  • Understanding the Flows of the OAuth 2.0 and OpenID Connect protocols and what Flow is the best for our SPA React Application
  • Securing our SPA React Application by adding the Keycloak Javascript adapter in the React
  • How to use properly the Keycloak Javascript adapter in our application with a custom KeyCloakService

The first step was to register a “client” in keycloak which represents our application to secure (see the first part to remember how : Part One: Installing Keycloak with Docker and Administration)

We can see below a detailed section about the notion of “client” in keycloak :

[Server Administration Guide
users Users are entities that are able to log into your system. They can have attributes associated with themselves…keycloak.org](https://www.keycloak.org/docs/latest/server_admin/index.html#assembly-managing-clients_server_administration_guide "keycloak.org/docs/latest/server_admin/index..")

All we need to remember is :

Clients are entities that can request authentication of a user.

When we created our client, we have to realise that Keycloak automatically associated the client with a flow by default which will be detailed later on.

Remember when we created the client the protocol OpendId Connect was selected.

OAuth 2.0 and OpendId Connect Flows

So we need understand better the different flows available with:

  • OAuth 2.0
  • OpenId Connect.

We need to get what are their purpose so we can choose the proper flow for securing our front-end React application and why it is the best choice !

OAuth Flows

Flows in the OAuth 2.0 protocol are actually called “grant types”

  • Authorization Code : The Authorization Code grant type is used by Confidential Clients and Public Clients to exchange an authorization code for an access token. After the user returns to the client via the redirect URL, the application will get the authorization code from the URL and use it to request an access token.
  • PKCE : is an extension to the Authorization Code flow to prevent CSRF and authorization code injection attacks.
  • Implicit Flow with Form Post : intended for Public Clients, or applications which are unable to securely store Client Secrets.
  • Client Credentials : used by clients to obtain an access token outside of the context of a user. This is typically used by clients to access resources about themselves rather than to access a user’s resources. ==> we will explain this better later on what that means.
  • Device Code : used by browserless or input-constrained devices in the device flow to exchange a previously obtained device code for an access token.
  • Refresh Token : used by clients to exchange a refresh token for an access token when the access token has expired. This allows clients to continue to have a valid access token without further interaction with the user.

Now if we remember well we actually use the procotol OpenId Connect which is based on OAuth 2.0.

So we need to see the OpenId Connect flows which are available.

OpenId Connect Flows

OpenId Connect defines four main flows that can be used to authenticate a user:

  • Authorization Code Flow for browser-based applications like SPAs (Single Page Applications) or server-side application ==> we will use this option
  • Implicit Flow for browser-based application, less secure than the previous one, not recommended and deprecated in OAuth 2.1;
  • Client Credentials Grant for REST clients like web services, it involves storing a secret, so the client is supposed to be trustworthy;
  • Resource Owner Password Credentials Grant for REST clients like interfaces to mainframes and other legacy systems which cannot support modern authentication protocols, it involves sharing credentials with another service, caution here.

Remember when we created our client we

  • set the name : MyApp
  • the Client Protocol : openid-connect so we must reason with the protocol Flows
  • the root UR: locahost:3000

Creating a new client

We get the following page, we will highlight the 6 parts which are important to know.

Client Administration and Flows

The 6 main parts that keycloak automatically set up. We must master them so we know what is going on behind the scenes.

The parts will show you that keycloak by default:

  • Part 1°) : set the client to Enabled : so the client can initiate or not a login and get back the Access Token
  • Part 2°): set the Access Type to “public”, it is used for Front-end public clients which can’t safely store the secret to initiate a login.
    For example our SPA react application is a public application because the javascript code is directly delivered to the browser of the user. We can hardly think it safe to store the secret in the js file!

What secret are we talking about here ? The secret is used in the Authorization Code Flow where the client exchanges the Autorization Code for the Access Token by sending this Authorization Code with the secret in the request. In our case, the secret will not used but we will use Valide Redirect URIs to make sure not any SPA reaches for the Keycloak server.

Access Type: public

If we changed the Access Type to “confidential” we would get the new following fields in red

New fields : Service Accounts, OIDC CIBA Grant, Authorization Enabled

Only Service Accounts is important: if enabled we actually use the “Client Credentials Grant”.
Service Accounts are not available to public clients as there is no secret available.

it allows us to use the “Client Credentials Grant”

So to understand the differences between the “Authorization Code Grant Type Flow” and the “Client Credentials Flow” see the diagrams:

Authorization Code Grant

The ressource owner accesses the application and we have 2 steps:

  • step 1: Authentication and Grant Authorization which returns the Authorization Code to the application
  • step 2: The application sends the Authorization Code to get the Access Token
  • It is used when we have a Resource Owner (user)

Client Credentials Flow

Here we don’t have the Resource Owner, it is a direct communication between machines so we only have one step here : we send the client ID and secret and we directly get back the Access Token from the Authorization Server (Keycloak here)

This Client Credentials flow is for Apps that can request an access token and access resources on its own. These apps often use services that call APIs without users.

When we used “Confidential” client type and clicked on “save” we would get a new tab available called “Credentials” in our Client MyApp page

We get here the secret

So when we have on application which can save the clientid and secret safely on the server, we can use the “confidential” client and use the “client id” and “secret” in the Authorization Code Flow.

The secret is known only to the application and the authorization server. It is essential the application’s own password.

Basically when we have users needing authentication and authorization with a public application : we use the Authorization Code Flow with the login/password. This flow is enabled by the “Standard Flow” of the Part 3°).

  • Part 3°): enabled the “Standard Flow” : it is the standard OpenID Connect authentication based on redirection with authorization code.
    It is to enable the “Authorization Code Flow” which will be explained later.
    It will be this Authorization Flow which will be used to secure our front-end React SPA. We will explain later on why.

Standard Flow : Enables the Authorization Flow with OpenID Connect protocol

  • Part 4°): enabled the “Direct Access Grants” where the client (our application) can get and use the user login/pwd to get directly from the Keycloak the Access Token. In OAuth 2.0 it is like enabling “Resource Owner Password Credentials Grant

We will see what happens when we disable this Flow after we finish this section.

  • Part 5°): added our SPA url with the wildcard *
    http://localhost:3000/* as Valide Redirect URIs
    We will see that it is very important indeed to protect our public client.
    The more precise are the links, the safer. Because public client can’t store the secret used in the Authorization Code flow, we need this protection to avoid another website to access the Access Token! If the url doesn’t match keycloak will returns the following error:

When an unauthorized website url tries to connect to the keycloak server

We can see in the following link complementary explanations:

[Keycloak Authentication Flows, SSO Protocols and Client Configuration
Keycloak In this article, I’m going to introduce the concept of authentication flows. Then, I’ll briefly mention the…thomasvitale.com](https://www.thomasvitale.com/keycloak-authentication-flow-sso-client/ "thomasvitale.com/keycloak-authentication-fl..")

Since the access to the client will be public, for security reasons, we must restrict it by setting the redirect URIs correctly. Keycloak automatically generates them from the Root URL, but we could add more of them. The more specific we are, the better.

  • Part 6°): added the URI of our application.

This option handles Cross-Origin Resource Sharing (CORS). If browser JavaScript attempts an AJAX HTTP request to a server whose domain is different from the one that the JavaScript code came from, the request must use CORS. The server must handle CORS requests, otherwise the browser will not display or allow the request to be processed. This protocol protects against XSS, CSRF, and other JavaScript-based attacks.

So basically keycloak enabled for us the two following Flows:

  • Authorization Code Flow without the use of the secret (public client)
    We will use the Login and Password of the user.
  • Resource Owner Password Credentials Grant

Hopefully this section was not confusing, please let’s me know be email if you need further details.

Let’s see what happens if we disable the Resource Owner Password Credentials Grant.

We can see that the access token is directly issued once the login/pwd are validated by the Keycloak server.

Resource Owner Password Credentials Grant Diagram

Let’s open our Insomia API client application and send our request:

Resource Owner Password Credentials Grant

We sent in the request

We send :

  • grant_type : Application grant types (or flows) are methods through which applications can gain Access Tokens

Below is the list of possible values:

We have to be aware that only highly-trusted applications can use the Resource Owner Password Flow.

Let’s disable on the server this flow:

Turn off Direct Access Grants

Disable and Save

If we try again we get the following error:

Client not allowed for direct access grants

The client MyApp is not allowed anymore to authenticate and authorize itselfs with this grant. It will have to use the other grant : Authorization Code grant.

We will use the Authorization Code Grant for securing our React SPA application

Securing a React SPA application with the OpenID Connect procotol

Basically what is a React SPA application?

It is a front-end application with HTML + CSS + Javascript with special modulation to make its development faster, easier, more efficient and more scalable.

So our code is Javascript on the client side.

Let’s look what is available on the Keycloak website to secure it with the OpenID Connect protocol.

[Securing Applications and Services Guide
Edit descriptionkeycloak.org](https://www.keycloak.org/docs/latest/securing_apps/index.html "keycloak.org/docs/latest/securing_apps/inde..")

Here we can find a complete guide about securing applications and services.

We will look at the Client-Side application so we click on

[Securing Applications and Services Guide
Edit descriptionkeycloak.org](https://www.keycloak.org/docs/latest/securing_apps/index.html#client-adapters "keycloak.org/docs/latest/securing_apps/inde..")

OpenID Connect Client-Side Javascript Adapter

[Securing Applications and Services Guide
Edit descriptionkeycloak.org](https://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter "keycloak.org/docs/latest/securing_apps/inde..")

Keycloak comes with a client-side JavaScript library that can be used to secure HTML5/JavaScript applications.

the library can be retrieved directly from the Keycloak server at /js/keycloak.js

Let’s check:

We send a GET Http method request to :

http://localhost:8080/js/keycloak.js

Javascript Keycloak Adapter

it is a big js file of 2405 lines for our server keycloak of the lastest version 18.0.0 when I wrote this blog:

Cool now we know we can use this file to help us securing any front-end web application using javascript.

But how do we use it in our React application?

First we need one file:

  • download the keycloak.json by following the instructions:

Click on the Installation tab select Keycloak OIDC JSON for Format Option then click Download. The downloaded keycloak.json file should be hosted on your web server at the same location as your HTML pages.

The “Installation” tab helps us to get the configuration files.

We select Keycloak OIDC JSON

We download our configuration file

We add the keycloak.json at the root of the public directory of our React project.

At the end we get the following project structure:

Keycloak.json is in the public directory

Now we are ready to code!

We will use Visual Code but you are free to use any IDE:

Let’s open the terminal in Visual Code

Adding the Keycloak Javascript adapter in the React

We need to add the Javascript Keycloak adapter in our React project.

For this, we will use the npm.

A simple search on google gives:

https://www.npmjs.com/package/keycloak-js

Here we can find it, good news it also contains the built-in typescript declaration, very convenient!

[keycloak-js
Keycloak Adapter. Latest version: 18.0.0, last published: a month ago. Start using keycloak-js in your project by…npmjs.com](https://www.npmjs.com/package/keycloak-js "npmjs.com/package/keycloak-js")

Moreover, we can check it matches with the Keycloak Server version.

Let’s install it with the following command:

C:\DevRoot\myapp>npm i keycloak-js@18.0.0
npm WARN [@apideck/better-ajv-errors](twitter.com/apideck/better-ajv-errors "Twitter profile for @apideck/better-ajv-errors")@0.3.3 requires a peer of ajv@>=8 but none is installed. You must install peer dependencies yourself.
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.3.2 (node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.3.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

+ keycloak-js@18.0.0
added 3 packages from 3 contributors and audited 1434 packages in 13.281s

182 packages are looking for funding
run `npm fund` for details

found 1 high severity vulnerability
run `npm audit fix` to fix them, or `npm audit` for details

keycloak-js

Now we can import the keycloak js adapter features in our application.
For this, we will enjoy Typescript by creating a Service with its interface.

It will make it easier to use it anywhere and change its implementation.

First let’s create a folder that we can call “security” for our keycloak service, that service instance will make it easier to use it anywhere in our React project.

Let’s add the tsx file called “KeycloakService.tsx”

KeyCloakService

We need to know the Keycloak JS API, please go to the link

[keycloak-documentation/javascript-adapter.adoc at main · keycloak/keycloak-documentation
Contribute to keycloak/keycloak-documentation development by creating an account on GitHub.github.com](https://github.com/keycloak/keycloak-documentation/blob/main/securing_apps/topics/oidc/javascript-adapter.adoc "github.com/keycloak/keycloak-documentation/..")

We add the following code:

Very important to remember, the documentation says at https://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_implicit_flow

By default, the JavaScript adapter uses the Authorization Code flow.

So we made sure that the client side can communicate to the server side by using the Authorization Code flow, the Keycloak Javascript Adapter by default uses the Authorization Code flow that we enabled for the client “MyApp” on the Keycloak server configuration

We don’t have to do anything about the Flow in our code.

Now we are ready to make sure it works :) in our index.tsx file before our application is rendered we use

Securing the React App with Keycloak Service

We import our KeyCloakService and we create a function called renderApp.
This renderApp will be called only if the user is authenticated!

So before the React application can be rendered the keycloak service will trigger the CallLogin and we will see what happens.

Let’s run our React Application in VS Code.

Let’s open our package.json

Debug

We see a part called “Debug” with all the scripts we need.

We will use scripts/start

Click on “Debug”

Click on “start” to run react-scripts start

It will open the terminal and run

At the end the brower opens and the terminal returns:

And what do you see in the browser? :)

Redirection from our React App to the Keycloak login form

Our react application is secured by keycloak.

Let’s see the url

http://localhost:8080/realms/MyRealm/protocol/openid-connect/auth?client_id\=MyApp&redirect_uri\=http%3A%2F%2Flocalhost%3A3000%2F&state=39ebab78-b871-43ce-a30a-b01cce35b83d&response_mode=fragment&response_type=code&scope=openid&nonce=e8f025e1-c1fc-4551-8bb6-2c1239b01a79

And you can see the realm “My Realm” and the client_id is MyApp we set up in our previous blog.

In the url we see the main parts:

  • Keycloak js adapter calls the auth endpoint to authenticate the user http://localhost:8080/realms/MyRealm/protocol/openid-connect/auth : Endpoint to authenticate the user with a form
  • protocol is openid-connect
  • client_id is MyApp
  • redirect_uri is our URI: http://locahost:3000
  • response_type = code we say to the server we will use the Authorization Code grant
  • scope=openid it is to say keycloak will return ID Token and Access Token. ID Token is the extension of the protocol OpenID Connect over the OAuth 2.0 protocol.

At the end of the flow we get in response of the ultimate request of the Authorization Code Flow POST: http://localhost:8080/realms/MyRealm/protocol/openid-connect/token where we request for the token(s) by exchanging with the Authorization Code.

ID Token and Access Token

We sent in the POST body:

  • code: the value of Authorization Code
  • grant_type: Authorization_Code Flow
  • client_id: MyApp
  • redirect_uri: to redirect to our application after the user is authenticated

Let’s see with Fiddler the complete dance

Step 1: User not Authenticated, redirection to Keycloak to log in

The important request/response here is on the line 13

Below the complete request with the expected query paramters already studied earlier

GET localhost:8080/realms/MyRealm/protocol/open.. HTTP/1.1

Host: localhost:8080

Connection: keep-alive

sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101"

sec-ch-ua-mobile: ?0

sec-ch-ua-platform: "Windows"

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9

Sec-Fetch-Site: same-site

Sec-Fetch-Mode: navigate

Sec-Fetch-Dest: document

Referer: localhost: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

Cookie: AUTH_SESSION_ID=d1945748-e67b-448f-b204-a0b804da5740; AUTH_SESSION_ID_LEGACY=d1945748-e67b-448f-b204-a0b804da5740; KC_RESTART=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIyMTllYzkwNi1hYjNlLTQ4YWItOTM2OS0yZDdjYTZhM2QwOWQifQ.eyJjaWQiOiJNeUFwcCIsInB0eSI6Im9wZW5pZC1jb25uZWN0IiwicnVyaSI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzAwMC8iLCJhY3QiOiJBVVRIRU5USUNBVEUiLCJub3RlcyI6eyJzY29wZSI6Im9wZW5pZCIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9yZWFsbXMvTXlSZWFsbSIsInJlc3BvbnNlX3R5cGUiOiJjb2RlIiwicmVkaXJlY3RfdXJpIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwLyIsInN0YXRlIjoiMWUxZGVhZjItMmI0NC00ZWY5LTk1YTktMzcxYWNlNmVkNGNjIiwibm9uY2UiOiIyYjc3YjVlYy05MTNkLTQwMjEtODZkZi0zYTRmNzc5Mjg5MjgiLCJyZXNwb25zZV9tb2RlIjoiZnJhZ21lbnQifX0.0VclmSPhERNprcUZ9oAaknvuvW5UNFM9clJkO9fOZsA

We get in response the form, here a primary preview in the fiddler

In our brower we get:

Let’s log in and follow the dance with fiddler:

Step 2 and 3: Authorization Code and Tokens

Step 2: Let’s look at the line 26 where we logged and the endpoint authenticate is called. We sent in the POST http call the username and password

POST localhost:8080/realms/MyRealm/login-actions.. HTTP/1.1

Host: localhost:8080

Connection: keep-alive

Content-Length: 45

Cache-Control: max-age=0

sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101"

sec-ch-ua-mobile: ?0

sec-ch-ua-platform: "Windows"

Upgrade-Insecure-Requests: 1

Origin: null

Content-Type: application/x-www-form-urlencoded

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9

Sec-Fetch-Site: same-origin

Sec-Fetch-Mode: navigate

Sec-Fetch-User: ?1

Sec-Fetch-Dest: document

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

Cookie: AUTH_SESSION_ID=d1945748-e67b-448f-b204-a0b804da5740; AUTH_SESSION_ID_LEGACY=d1945748-e67b-448f-b204-a0b804da5740; KC_RESTART=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIyMTllYzkwNi1hYjNlLTQ4YWItOTM2OS0yZDdjYTZhM2QwOWQifQ.eyJjaWQiOiJNeUFwcCIsInB0eSI6Im9wZW5pZC1jb25uZWN0IiwicnVyaSI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzAwMC8iLCJhY3QiOiJBVVRIRU5USUNBVEUiLCJub3RlcyI6eyJzY29wZSI6Im9wZW5pZCIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9yZWFsbXMvTXlSZWFsbSIsInJlc3BvbnNlX3R5cGUiOiJjb2RlIiwicmVkaXJlY3RfdXJpIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwLyIsInN0YXRlIjoiYmUzMzJkYmUtZTQ5Mi00MTMzLWIzOTctNDJmYjM4NWEwZDlmIiwibm9uY2UiOiJjOGZjMWFlNi04NGViLTQzMzItOTFlMS05ZWJkZjIzNDQ3YzkiLCJyZXNwb25zZV9tb2RlIjoiZnJhZ21lbnQifX0.IwNPJ-OaVGtX-PgLxr1u849Q84LD4RPI87Bt4TzgcL0

username=myuser&password=myuser&credentialId=

We get in response once the login/password validated by keycloak

HTTP/1.1 302 Found

Referrer-Policy: no-referrer

X-Frame-Options: SAMEORIGIN

Strict-Transport-Security: max-age=31536000; includeSubDomains

X-Robots-Tag: none

Cache-Control: no-store, must-revalidate, max-age=0

X-Content-Type-Options: nosniff

Content-Security-Policy: frame-src 'self'; frame-ancestors 'self'; object-src 'none';

Set-Cookie: KEYCLOAK_LOCALE=; Version=1; Comment=Expiring cookie; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Max-Age=0; Path=/realms/MyRealm/; HttpOnly

Set-Cookie: KC_RESTART=; Version=1; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Max-Age=0; Path=/realms/MyRealm/; HttpOnly

Set-Cookie: KEYCLOAK_IDENTITY=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIyMTllYzkwNi1hYjNlLTQ4YWItOTM2OS0yZDdjYTZhM2QwOWQifQ.eyJleHAiOjE2NTM4NzkyODEsImlhdCI6MTY1Mzg0MzI4MSwianRpIjoiMTEyMjVmMjAtYWFjMC00NjJhLWFmODgtNjNiNDQ4ZWIxZTVjIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9NeVJlYWxtIiwic3ViIjoiOGI1YTc4NjYtZTk2OC00YjIwLTkwMDEtNWZmNWFmNzVmZGNlIiwidHlwIjoiU2VyaWFsaXplZC1JRCIsInNlc3Npb25fc3RhdGUiOiJkMTk0NTc0OC1lNjdiLTQ0OGYtYjIwNC1hMGI4MDRkYTU3NDAiLCJzaWQiOiJkMTk0NTc0OC1lNjdiLTQ0OGYtYjIwNC1hMGI4MDRkYTU3NDAiLCJzdGF0ZV9jaGVja2VyIjoiZWJmbWZGbzFldHRnVU5ybDhjVjhKZjNsVko2SnQ0Tk9TbC1JWlg2YjRrcyJ9.tqPAjEiivavLO5I8JPl-62Sn66Dvvpd-95Ymsp7vHo0; Version=1; Path=/realms/MyRealm/; SameSite=None; Secure; HttpOnly

Set-Cookie: KEYCLOAK_IDENTITY_LEGACY=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIyMTllYzkwNi1hYjNlLTQ4YWItOTM2OS0yZDdjYTZhM2QwOWQifQ.eyJleHAiOjE2NTM4NzkyODEsImlhdCI6MTY1Mzg0MzI4MSwianRpIjoiMTEyMjVmMjAtYWFjMC00NjJhLWFmODgtNjNiNDQ4ZWIxZTVjIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9NeVJlYWxtIiwic3ViIjoiOGI1YTc4NjYtZTk2OC00YjIwLTkwMDEtNWZmNWFmNzVmZGNlIiwidHlwIjoiU2VyaWFsaXplZC1JRCIsInNlc3Npb25fc3RhdGUiOiJkMTk0NTc0OC1lNjdiLTQ0OGYtYjIwNC1hMGI4MDRkYTU3NDAiLCJzaWQiOiJkMTk0NTc0OC1lNjdiLTQ0OGYtYjIwNC1hMGI4MDRkYTU3NDAiLCJzdGF0ZV9jaGVja2VyIjoiZWJmbWZGbzFldHRnVU5ybDhjVjhKZjNsVko2SnQ0Tk9TbC1JWlg2YjRrcyJ9.tqPAjEiivavLO5I8JPl-62Sn66Dvvpd-95Ymsp7vHo0; Version=1; Path=/realms/MyRealm/; HttpOnly

Set-Cookie: KEYCLOAK_SESSION=MyRealm/8b5a7866-e968-4b20-9001-5ff5af75fdce/d1945748-e67b-448f-b204-a0b804da5740; Version=1; Expires=Mon, 30-May-2022 02:54:41 GMT; Max-Age=36000; Path=/realms/MyRealm/; SameSite=None; Secure

Set-Cookie: KEYCLOAK_SESSION_LEGACY=MyRealm/8b5a7866-e968-4b20-9001-5ff5af75fdce/d1945748-e67b-448f-b204-a0b804da5740; Version=1; Expires=Mon, 30-May-2022 02:54:41 GMT; Max-Age=36000; Path=/realms/MyRealm/

Set-Cookie: KEYCLOAK_REMEMBER_ME=; Version=1; Comment=Expiring cookie; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Max-Age=0; Path=/realms/MyRealm/; HttpOnly

P3P: CP="This is not a P3P policy!"

X-XSS-Protection: 1; mode=block

Location: localhost:3000/#state=be332dbe-e492-4133-b3..

content-length: 0

It returns the HTTP code 302 to redirect to http://localhost:3000/#state=be332dbe-e492-4133-b397-42fb385a0d9f&session_state=d1945748-e67b-448f-b204-a0b804da5740&code=618cefce-9582-4e5a-bd98-207b22e06a75.d1945748-e67b-448f-b204-a0b804da5740.c6015bba-a545-4114-841c-3a3425c001b2

We can see the authorization code in the url:
code=618cefce-9582–4e5a-bd98–207b22e06a75.d1945748-e67b-448f-b204-a0b804da5740.c6015bba-a545–4114–841c-3a3425c001b2

Step 3: Once we come back to our application the Keycloak JS keeps sending/receiving other request/responses till the line 45 where we get the final step 3 of the Authorization Code Flow: exchanging the Authorization Code to get back the Tokens in response if Keycloak could validate.

POST localhost:8080/realms/MyRealm/protocol/open.. HTTP/1.1

Host: localhost:8080

Connection: keep-alive

Content-Length: 207

sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101"

sec-ch-ua-mobile: ?0

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36

sec-ch-ua-platform: "Windows"

Content-type: application/x-www-form-urlencoded

Accept: */*

Origin: localhost:3000

Sec-Fetch-Site: same-site

Sec-Fetch-Mode: cors

Sec-Fetch-Dest: empty

Referer: localhost: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

Cookie: AUTH_SESSION_ID=d1945748-e67b-448f-b204-a0b804da5740; AUTH_SESSION_ID_LEGACY=d1945748-e67b-448f-b204-a0b804da5740; KEYCLOAK_IDENTITY=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIyMTllYzkwNi1hYjNlLTQ4YWItOTM2OS0yZDdjYTZhM2QwOWQifQ.eyJleHAiOjE2NTM4NzkyODEsImlhdCI6MTY1Mzg0MzI4MSwianRpIjoiMTEyMjVmMjAtYWFjMC00NjJhLWFmODgtNjNiNDQ4ZWIxZTVjIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9NeVJlYWxtIiwic3ViIjoiOGI1YTc4NjYtZTk2OC00YjIwLTkwMDEtNWZmNWFmNzVmZGNlIiwidHlwIjoiU2VyaWFsaXplZC1JRCIsInNlc3Npb25fc3RhdGUiOiJkMTk0NTc0OC1lNjdiLTQ0OGYtYjIwNC1hMGI4MDRkYTU3NDAiLCJzaWQiOiJkMTk0NTc0OC1lNjdiLTQ0OGYtYjIwNC1hMGI4MDRkYTU3NDAiLCJzdGF0ZV9jaGVja2VyIjoiZWJmbWZGbzFldHRnVU5ybDhjVjhKZjNsVko2SnQ0Tk9TbC1JWlg2YjRrcyJ9.tqPAjEiivavLO5I8JPl-62Sn66Dvvpd-95Ymsp7vHo0; KEYCLOAK_IDENTITY_LEGACY=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIyMTllYzkwNi1hYjNlLTQ4YWItOTM2OS0yZDdjYTZhM2QwOWQifQ.eyJleHAiOjE2NTM4NzkyODEsImlhdCI6MTY1Mzg0MzI4MSwianRpIjoiMTEyMjVmMjAtYWFjMC00NjJhLWFmODgtNjNiNDQ4ZWIxZTVjIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9NeVJlYWxtIiwic3ViIjoiOGI1YTc4NjYtZTk2OC00YjIwLTkwMDEtNWZmNWFmNzVmZGNlIiwidHlwIjoiU2VyaWFsaXplZC1JRCIsInNlc3Npb25fc3RhdGUiOiJkMTk0NTc0OC1lNjdiLTQ0OGYtYjIwNC1hMGI4MDRkYTU3NDAiLCJzaWQiOiJkMTk0NTc0OC1lNjdiLTQ0OGYtYjIwNC1hMGI4MDRkYTU3NDAiLCJzdGF0ZV9jaGVja2VyIjoiZWJmbWZGbzFldHRnVU5ybDhjVjhKZjNsVko2SnQ0Tk9TbC1JWlg2YjRrcyJ9.tqPAjEiivavLO5I8JPl-62Sn66Dvvpd-95Ymsp7vHo0; KEYCLOAK_SESSION=MyRealm/8b5a7866-e968-4b20-9001-5ff5af75fdce/d1945748-e67b-448f-b204-a0b804da5740; KEYCLOAK_SESSION_LEGACY=MyRealm/8b5a7866-e968-4b20-9001-5ff5af75fdce/d1945748-e67b-448f-b204-a0b804da5740

code=618cefce-9582-4e5a-bd98-207b22e06a75.d1945748-e67b-448f-b204-a0b804da5740.c6015bba-a545-4114-841c-3a3425c001b2&grant_type=authorization_code&client_id=MyApp&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2F

We call the endpoint token to exchange the Authorization code to the Tokens

We send in the POST HTTP request

  • the Authorization code code=618cefce-9582–4e5a-bd98–207b22e06a75.d1945748-e67b-448f-b204-a0b804da5740.c6015bba-a545–4114–841c-3a3425c001b
  • the grant_type =authorization_code
  • the client_id=MyApp
  • the redirect_uri our React Application URI from where we were redirected from before getting on the keycloak server

In response we get:

HTTP/1.1 200 OK

Referrer-Policy: no-referrer

X-Frame-Options: SAMEORIGIN

Access-Control-Expose-Headers: Access-Control-Allow-Methods

Strict-Transport-Security: max-age=31536000; includeSubDomains

Cache-Control: no-store

Access-Control-Allow-Origin: localhost:3000

Access-Control-Allow-Credentials: true

X-Content-Type-Options: nosniff

Pragma: no-cache

X-XSS-Protection: 1; mode=block

Content-Type: application/json

content-length: 3426

{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJFMUk0RHpMWHUzUTRqMm80ZHdSRFBSOVBGUzd6bEw2MjdOaGtiSUl5WkQ0In0.eyJleHAiOjE2NTM4NDM1ODYsImlhdCI6MTY1Mzg0MzI4NiwiYXV0aF90aW1lIjoxNjUzODQzMjgxLCJqdGkiOiI4MjI4NjM1OC1kZmVhLTQwMGMtOGVkNy05Yzg3NjFkMWU1YmQiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvcmVhbG1zL015UmVhbG0iLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiOGI1YTc4NjYtZTk2OC00YjIwLTkwMDEtNWZmNWFmNzVmZGNlIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiTXlBcHAiLCJub25jZSI6ImM4ZmMxYWU2LTg0ZWItNDMzMi05MWUxLTllYmRmMjM0NDdjOSIsInNlc3Npb25fc3RhdGUiOiJkMTk0NTc0OC1lNjdiLTQ0OGYtYjIwNC1hMGI4MDRkYTU3NDAiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHA6Ly9sb2NhbGhvc3Q6MzAwMCJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1teXJlYWxtIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwiLCJzaWQiOiJkMTk0NTc0OC1lNjdiLTQ0OGYtYjIwNC1hMGI4MDRkYTU3NDAiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6Im15dXNlciJ9.B2F_-pSTh6phS7TZJeS6-ZUgT31uEYQPbtV9oxjVdIKVeQWadWCyV-P2H0gY5oo7VWUhvtcqib_IK3fm41TxDxatlHkysFX5FVsvUoilkIeRp1vQDEhz745NddspVIZzH5u87TdFVuKH5Abv3Vr410sYAQeugzuCi28sbcgIsXzleX_LuUpOmaewz71j5zrkDGTz2qM9xo3D1g74ZuUegXsm_XKjDLqDEKHkJTa8YJvUBaf_iMfM-ZXI91Z41l9zP4rFuYqwFgLxvu-FdgN7jsW4rUGohsqyaNBJ_GCb0F8HYZRGCKuIkgZj-Zn6zkgbQetB7oMoB0ueEAtJd8aO2A","expires_in":300,"refresh_expires_in":1800,"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIyMTllYzkwNi1hYjNlLTQ4YWItOTM2OS0yZDdjYTZhM2QwOWQifQ.eyJleHAiOjE2NTM4NDUwODYsImlhdCI6MTY1Mzg0MzI4NiwianRpIjoiMDg1ZTJmZWQtZDgxZi00OTcxLWE0ZWEtODQxMmJiNWY4MDgzIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9NeVJlYWxtIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9NeVJlYWxtIiwic3ViIjoiOGI1YTc4NjYtZTk2OC00YjIwLTkwMDEtNWZmNWFmNzVmZGNlIiwidHlwIjoiUmVmcmVzaCIsImF6cCI6Ik15QXBwIiwibm9uY2UiOiJjOGZjMWFlNi04NGViLTQzMzItOTFlMS05ZWJkZjIzNDQ3YzkiLCJzZXNzaW9uX3N0YXRlIjoiZDE5NDU3NDgtZTY3Yi00NDhmLWIyMDQtYTBiODA0ZGE1NzQwIiwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBlbWFpbCIsInNpZCI6ImQxOTQ1NzQ4LWU2N2ItNDQ4Zi1iMjA0LWEwYjgwNGRhNTc0MCJ9.i1WadD6KDRmAcJAPV2s8aYLd6VXR6YWO0l-a-TVGP9c","token_type":"Bearer","id_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJFMUk0RHpMWHUzUTRqMm80ZHdSRFBSOVBGUzd6bEw2MjdOaGtiSUl5WkQ0In0.eyJleHAiOjE2NTM4NDM1ODYsImlhdCI6MTY1Mzg0MzI4NiwiYXV0aF90aW1lIjoxNjUzODQzMjgxLCJqdGkiOiI0OTRkZDc4YS04YTY5LTQzYzItOWQ3OS05MWFkNzU1ZmU5NjkiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvcmVhbG1zL015UmVhbG0iLCJhdWQiOiJNeUFwcCIsInN1YiI6IjhiNWE3ODY2LWU5NjgtNGIyMC05MDAxLTVmZjVhZjc1ZmRjZSIsInR5cCI6IklEIiwiYXpwIjoiTXlBcHAiLCJub25jZSI6ImM4ZmMxYWU2LTg0ZWItNDMzMi05MWUxLTllYmRmMjM0NDdjOSIsInNlc3Npb25fc3RhdGUiOiJkMTk0NTc0OC1lNjdiLTQ0OGYtYjIwNC1hMGI4MDRkYTU3NDAiLCJhdF9oYXNoIjoiemlUM0NqeDI4WEQtSjVjbWFVV1k1ZyIsImFjciI6IjEiLCJzaWQiOiJkMTk0NTc0OC1lNjdiLTQ0OGYtYjIwNC1hMGI4MDRkYTU3NDAiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6Im15dXNlciJ9.V6ZJKLECBOlKF0qisF19Y8xX9kTf7CZgHLz0efxmr63ZOSRsyZQwLBnoWH00E-pFIGHkdK5ugRGNU36N7xX1tzDvf6sOUIAAj7IUokaAxeYZOtjQL5ZTFgS1qZqEH7kUi27NaCc-z2mOrxF3nD6fSVqZcJ2jx-Wd5-nNvRCMpyEx0j5UtrB4aQP9XJddaJI4P19QjWicNTf1XBeProRKSoFsZ8W3wF0PCaCTV0hsF5rHyyvEP2csAAn1PZ7_Sv9TS0dI2_KjcxiiDCsMuBQnJI53vjAQkpm289dT20BUq6DXnJwLjeYsvO3EGAmpieqUIRTGx6JZ6WvZ-XVQp5_ctA","not-before-policy":0,"session_state":"d1945748-e67b-448f-b204-a0b804da5740","scope":"openid profile email"}

We can see the ID and Access tokens

Below a visual view :

We can also see the expiration of the tokens.

So with fiddler we could make sure that the Authorization Code flow is used.

At the end we can go to our React App page !

Reaching our React App after authenticating our User with Keycloak

Getting User Information on our React Application, Logging out and SSO

In the final part of the part 2 we will show you how to display:

  • the user name
  • the user role(s)

on our React App.

We will show you how to log out from our React App.

Finally we will prove you that SSO works by logging out outside our React App.

On our welcome page we will :

  • add the user name and role(s)
  • add a link to log out

Remember in our Keycloak Service we added only one method: CallLogin

We will add a new method GetUserName to get our User Login:

Now let’s use it in our welcome page within the App component:

We get the following message

Let’s see the Role(s) now

Let’s use it

We get the result

So we see how easy it is to get all the users information.
The roles are returned by the Access Token which is parsed and returned by the Keycloak js adapter.

Finally we will add a button to log out the user, but fist we need to add the method in our Keycloak Service:

Let’s add the button to call the logout:

We get the screen:

When we click on Log Out we return to

Finally let’s prove that we use a SSO (Single Sign On).

We log in again by clicking on “Sign In”

We go back to

Let’s go to the Keycloak Server:

We can see for our application MyApp the number of Sessions, therefore the number of Users logged in.

We can click on the MyApp

We can see all the sessions by clicking on “Show Sessions”

If we click on “Logout All” on the Sessions, we will make the user logged out.

All the sessions are removed.

When we go back to our App, the user is not logged anymore.

Now let’s do the reverse we want to logged in our user but by using KeyCloak:

Let’s go to http://localhost:8080/realms/MyRealm/account/

We get the page:

Let’s click on “Sign in”

We log in and we see

Now let’s see what happens when go to our App:

Our user is already logged-in and because the two applications : account (http://localhost:8080/realms/MyRealm/account/) and MyApp are in the same Realm MyRealm, the user can use the two applications with one Single Sign On!

The next part is available:
Part three: Securing the ASP.NET Core C# REST Web

References

[OpenId Connect et OAuth : comment choisir son flow de connexion ?
Dans des précédents articles, nous avons abordé comment sécuriser une API avec OpenIddict. Dans cet article, je vais…blogs.infinitesquare.com](https://blogs.infinitesquare.com/posts/web/open-id-connect-et-oauth-les-differents-flow-de-connexion "blogs.infinitesquare.com/posts/web/open-id-..")

[Single-Page Apps - OAuth 2.0 Simplified
Single-page apps (also known as browser-based apps) run entirely in the browser after loading the JavaScript and HTML…oauth.com](https://www.oauth.com/oauth2-servers/single-page-apps "oauth.com/oauth2-servers/single-page-apps")

[Server Administration Guide
users Users are entities that are able to log into your system. They can have attributes associated with themselves…keycloak.org](https://www.keycloak.org/docs/latest/server_admin/index.html#_authentication-flows "keycloak.org/docs/latest/server_admin/index..")

[OAuth 2.0 Grant Types
This topic explains how OAuth 2.0 grant types work with different app types. The authorization code grant type is the…docs.vmware.com](https://docs.vmware.com/en/Single-Sign-On-for-VMware-Tanzu-Application-Service/1.14/sso/GUID-grant-types.html "docs.vmware.com/en/Single-Sign-On-for-VMwar..")

[Application Grant Types
Application grant types (or flows) are methods through which applications can gain Access Tokens and by which you grant…auth0.com](https://auth0.com/docs/get-started/applications/application-grant-types#available-grant-types "auth0.com/docs/get-started/applications/app..")

[Resource Owner Password Flow
Though we do not recommend it, highly-trusted applications can use the Resource Owner Password Flow (defined in OAuth…auth0.com](https://auth0.com/docs/get-started/authentication-and-authorization-flow/resource-owner-password-flow "auth0.com/docs/get-started/authentication-a..")

[keycloak-documentation/javascript-adapter.adoc at main · keycloak/keycloak-documentation
Contribute to keycloak/keycloak-documentation development by creating an account on GitHub.github.com](https://github.com/keycloak/keycloak-documentation/blob/main/securing_apps/topics/oidc/javascript-adapter.adoc "github.com/keycloak/keycloak-documentation/..")