×
Community Blog How to Improve Web Application Security with Single Sign-On

How to Improve Web Application Security with Single Sign-On

The purpose of this blog is to demonstrate how to achieve web application SSO using Keycloak on top of Alibaba Cloud.

By Juan Patrick, Solution Architect Intern

Introduction

Web application authentication is a very important mechanism to protect your web application. Single sign-on (SSO) is an authentication method that enables users to securely authenticate with multiple applications and websites by using just one set of credentials. Users can manage their accounts more conveniently and securely because web applications don't keep their credentials. The applications only needs user data to run their services.
The purpose of this blog is to demonstrate how to achieve web application SSO using Keycloak on top of Alibaba Cloud. The blog shows steps to integrate SSO into web applications and explains design interactions between users, web applications, and KeyCloak as an authentication server. This could be a solution for you to achieve security identity access for web applications.

System Design

The demo application uses web application developed using NodeJs. KeyCloak is an open source software IAM (Identity and Access Management) to secure applications, with features like passwordless authentication, so that you don’t have to worry about identity access, and you can manage your browser flow access on KeyCloak.

The flowchart of the Single Sign-on process with KeyCloak is shown as below:
1

A diagram shows the user flow for accessing a web application with KeyCloak SSO. If users don’t have an account, they will be redirected to KeyCloak to register an account and get an access token for the web application. If users already have an account, they can go to KeyCloak to login, and once they successfully login, users will get the access token and be redirected back to the web application home page with the access granted.

Based on the application architecture, there are 2 ways for web applications to be integrated with KeyCloak SSO:

  1. Web Application SSO integration without user database;
  2. Web Application SSO integration with user database;

Web Application SSO Integration without user database

This is the scenario of integration to SSO from the web application without user database, where the web application relies on the Keycloak server to store the user data. Below is the sequential diagram to describe interaction between user, web application, and KeyCloak.
2

Users are using web browser to access Web Application and KeyCloak for authentication. Users access the Web Application for the first time, will be redirected to KeyCloak to enter credential on the KeyCloak for authentication . Once users are authenticated, KeyCloak will save the user session and redirect the user back to the Web Application. The browser will store the access token and the token will be used for authentication on the web application.

  1. Below are the steps to setup web application and install the Keycloak plugin on the web application. Before that, we need to firstly setup Keycloak server, for details, please refer to this link
  2. Create a project React called web-test by running this command:
npx create-react-app web-test
  1. Go to directory web-test and install the package KeyCloak on the web application server:
npm install keycloak-js
  1. On the web application server, open the file App.js (under web-test directory) and edit the code as following:
import Keycloak from "keycloak-js";
import { useEffect } from 'react';
import './App.css';
const kcConfig = {
  "url": "<url-keycloak-server>",
  "realm": "<realm-name>",
  "clientId": "<client-name>"
}
function App() {
  useEffect(() => {
    const keycloak = new Keycloak(kcConfig);
    keycloak
      .init({
        checkLoginIframe: false,
      })
      .then(async (authenticated) => {
        if (authenticated) {
          try {
            localStorage.setItem("kc_token", keycloak.token);
            localStorage.setItem("kc_refreshToken", keycloak.refreshToken);
          } catch (error) {
            console.log(error);
          }
        }
      });
  }, [])
  return (
    <div className="App">
      <header className="App-header">
        <button type="button" style={{
          background: "rgb(248 250 252)",
          padding: "8px 20px",
          fontSize: "18px",
          borderRadius: "5px",
          fontWeight: "500",
          cursor: "pointer",
        }} onClick={() => {
          const keycloak = new Keycloak(kcConfig);
          keycloak.init({
            checkLoginIframe: false,
          });
          keycloak.login();
        }}>
          Login with KeyCloak
        </button>
      </header>
    </div>
  );
}
export default App;

After successfully running the script, we have created a login button to redirect user to the login page on KeyCloak. Once the user has been authenticated, the access token will be stored in the browser's local storage.
• <url-keycloak-server> is your IP address KeycLoak server and uses port 8080.
• <realm-name> is your realm created in KeyCloak.
e> is your client created in the realm in KeyCloak.
Note: We have to grant access for Keycloak client on the web application server.

  1. Run the web application
npm start
  1. Now, you can see the button on the screen. Click the button Login with KeyCloak.
    3_jpg
  2. It will be redirected to Login page KeyCloak. Fill in your credential created on KeyCloak.
    4
  3. Now, you can check your browser local storage and you can see your tokens are stored in the browser.
    5
  4. We can use this token to validate the user, by creating a simple react router for testing:
npm install react-router-dom
  1. After that, we modify the code in index.js:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </BrowserRouter>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
  1. We create a simple router page for the home page and protected page in App.js.
import Keycloak from "keycloak-js";
import { useEffect } from 'react';
import './App.css';
import { Routes, Route, useNavigate } from "react-router-dom";
const kcConfig = {
  "url": "<url-keycloak-server>",
  "realm": "<realm-name>",
  "clientId": "<client-name>"
}
function App() {
  return (
    <div className="App">
      <header className="App-header">
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="protected" element={<Protected />} />
        </Routes>
      </header>
    </div>
  );
}
function Home() {
  useEffect(() => {
    const keycloak = new Keycloak(kcConfig);
    keycloak
      .init({
        checkLoginIframe: false,
      })
      .then(async (authenticated) => {
        if (authenticated) {
          try {
            localStorage.setItem("kc_token", keycloak.token);
            localStorage.setItem("kc_refreshToken", keycloak.refreshToken);
          } catch (error) {
            console.log(error);
          }
        }
      });
  }, [])
return (
    <>
      <button type="button" style={{
        background: "rgb(248 250 252)",
        padding: "8px 20px",
        fontSize: "18px",
        borderRadius: "5px",
        fontWeight: "500",
        cursor: "pointer",
      }} onClick={() => {
        const keycloak = new Keycloak(kcConfig);
        keycloak.init({
          checkLoginIframe: false,
        });
        keycloak.login();
      }}>
        Login with KeyCloak
      </button>
    </>
  )
}
function Protected() {
  const navigate = useNavigate();

  useEffect(() => {
    const keycloak = new Keycloak(kcConfig);
    const token = localStorage.getItem("kc_token");
    const refreshToken = localStorage.getItem("kc_refreshToken");
    keycloak
      .init({
        checkLoginIframe: false,
        token,
        refreshToken,
      })
      .then(async (authenticated) => {
        if (authenticated) {
          console.log("AUTHENTICATED")
        }
      }).catch((error) => {
        navigate("/");
      });
  }, [navigate]);

  return (
    <>
      <h1>
        User Authenticated
      </h1>
    </>
  );
}
export default App;
  1. Now, open http://localhost:3000/protected and see the result:
    6

This is a direct access for the protected pages that can only be accessed by users who have authenticated with KeyCloak to demonstrate the SSO process. You can try to explore more demostration, like register and logout at:
https://www.keycloak.org/docs/latest/securing_apps/#_javascript_adapter

Web Application SSO Integration with user database

This is the scenario of integration to SSO from the web application with the user database. It’s different from the above use case, and we will store user data in a database of web server. Here is the sequential diagram:
7

The main differentiating parts in this diagram are the user validation process and the storage of user data. We can see that the web server has a role in storing user session data that will be used for the authentication process with KeyCloak. The Web Server uses a session as a user identity. Users do not need to store tokens but using session with better security control.

  1. Before the use case, make sure you have database PostgreSQL on Alibaba Cloud. You can try to setup database following at: https://www.alibabacloud.com/help/en/apsaradb-for-rds/latest/quick-start-create-an-apsaradb-rds-for-postgresql-instance
  2. You can start with sample project from https://github.com/Patrix2001/Nodejs-KeyCloak , and setup on your web application server;
  3. Access your KeyCloak website and choose your realm and your client. In the corner right of the tab client, click button Action, and choose Download adapter config
    8

The sample adapter config file looks like this:

{
  "realm": "….",
  "auth-server-url": "…..",
  "ssl-required": "….",
  "resource": "….",
  "public-client": ….,
  "confidential-port": ….
}
  1. Save the file keycloak.json on your web application server directory you created in step 1.
  2. Create .env file in the same directory and configure the database connection. The format follows like this:
DATABASE_URL=postgres://[user]:[password]@localhost:5432/[database_name]
  1. Finally, you can run the web server by npm start.
  2. Access the http://localhost:3000/profile on browser and try to login using your credential account KeyCloak. You can see the result here:
    9

Below are some of the sample code in the app.js file under the web directory.

…
const dbStore = new SequelizeStore({ db: sequelize });
const keycloak = new Keycloak({ store: dbStore }, kcConfig);
…
app.use(
    session({
        secret: "Secret",
        resave: false,
        saveUninitialized: true,
        store: dbStore,
    })
);
app.use(keycloak.middleware());

….
app.get("/profile", keycloak.protect(), async (req, res) => {
    try {
        const user = req.kauth.grant.access_token.content;
        return res.status(200).send({
            user
        });
    } catch (error) {
        return res.status(500).send({
            error
        });
    }
});

The express.js framework is used by the web server, and we will see how to integrate KeyCloak into the web server. Here, we connect web server to the database using sequelize package as represented by the dbStore variable. To use the KeyCloak package, we need the KeyCloak server configuration and storage.
Since we need this session to be stored on the server, we initialize the session on express.js by app.use(). The KeyCloak protect will authenticate the user. If unauthenticated, the users will be redirected to the login page of KeyCloak. There is an endpoint "/profile" which is protected using KeyCloak. When the user is authenticated, we can see the data format from the user as in the previous image. The data sessions about users will be stored in the table “Sessions” in the PostgreSQL database:
10

More details, you can check at: https://www.keycloak.org/docs/latest/securing_apps/#_nodejs_adapter

Conclusion

We have demonstrated the SSO integration with web application without database and with database. Overall, I recommend to second option, which stores user profile and token on local database which will provide more secure process.

0 1 0
Share on

Alibaba Cloud Indonesia

91 posts | 12 followers

You may also like

Comments