By Juan Patrick, Solution Architect Intern
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.
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:
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:
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.
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.
npx create-react-app web-test
npm install keycloak-js
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.
npm start
npm install react-router-dom
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();
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;
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
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:
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.
The sample adapter config file looks like this:
{
"realm": "….",
"auth-server-url": "…..",
"ssl-required": "….",
"resource": "….",
"public-client": ….,
"confidential-port": ….
}
DATABASE_URL=postgres://[user]:[password]@localhost:5432/[database_name]
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:
More details, you can check at: https://www.keycloak.org/docs/latest/securing_apps/#_nodejs_adapter
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.
Langkah-langkah Menambahkan Alibaba Cloud Log Service di Grafana Self Hosted Sebagai Data Source
99 posts | 15 followers
FollowNick Patrocky - November 28, 2022
Alibaba Clouder - January 29, 2021
Alibaba Clouder - September 22, 2020
Alibaba Cloud Community - February 17, 2022
Alibaba Clouder - January 24, 2019
Kidd Ip - October 22, 2024
99 posts | 15 followers
FollowA cloud firewall service utilizing big data capabilities to protect against web-based attacks
Learn MoreExplore Web Hosting solutions that can power your personal website or empower your online business.
Learn MoreWeb App Service allows you to deploy, scale, adjust, and monitor applications in an easy, efficient, secure, and flexible manner.
Learn MoreAlibaba Cloud is committed to safeguarding the cloud security for every business.
Learn MoreMore Posts by Alibaba Cloud Indonesia