Building Trust with OpenID Connect Protocols

Himani Perera
14 min readOct 5, 2023

--

Implementing OIDC for Secure and Efficient User Authentication and Access Control in a Dynamic Web App.

In today’s digital age, ensuring the security of web applications has become paramount. Whether you’re developing a small-scale reservation system or a large-scale platform, user authentication and access control are critical components. In this two-part article series, I’ll take you on a journey of building a secure web application for vehicle service reservations.

In this article, I’ve demonstrated how to verify the identity of the end-user to obtain basic user profile information and implement the logout functionality using the Asgardeo OpenID Connect protocols. A simple dynamic web app that facilitates reserving vehicle services has been used for the demonstrations.

In the upcoming second part of this series, we will delve into ensuring the fortification of our web application against vulnerabilities listed in the OWASP Top 10. It will demonstrate how to proactively secure your application and defend against common threats. So, our journey towards a robust and secure web application continues, and you won’t want to miss this essential next step in our development process.

The Journey Begins

Now our mission is clear! 🤓 to develop a dynamic web application that not only caters to users’ vehicle service reservation needs, but also prioritizes their data security and privacy. To accomplish this, we’ll be harnessing the power of OIDC (OpenID Connect) protocols for robust user authentication.

Also note that, in this article, my aim is to solve the identity and access management (IAM) issue and provide a way to ensure information security. Therefore, I won’t demonstrate how to develop a dynamic web app for vehicle service reservations. However, you can access the source code here, and implement it without much effort.

I’ll show you through the stages of implementing user authentication and access control using OIDC protocols while covering the essential tools, and step-by-step procedures, and provide you with the necessary code snippets to get started.

Tools of the Trade

Before we dive into the code, let’s gather our tools. Here’s what you’ll need to follow along:

  1. Development Environment: I recommend using a reliable integrated development environment (IDE) such as Eclipse, Visual Studio Code, or IntelliJ IDEA. Choose the one you’re most comfortable with. (I used Eclipse)
  2. Programming Language: This web application has been built using Java for the backend and JSP (JavaServer Pages) for the front end along with HTML and CSS.
  3. Web Server: Apache Tomcat is an excellent choice for deploying Java-based web applications.
  4. OIDC Provider: For user authentication, I was leveraging the OIDC protocol with a cloud-based Identity Provider (IDP). In my case, I used Asgardeo as the IDP.

With our development environment and tools in place, we’re ready to begin the journey toward a more secure web application.

The Roadmap Ahead

In the upcoming sections, I’ll outline the key steps and provide you with my experience along with code snippets for each stage of development.

📌Here’s a sneak peek of what’s ahead:

  1. A glimpse about the ‘Auto Care Vehicle Services’ web app.
  2. What is an OpenID Connect?
  3. Integrating OIDC for User Authentication
  4. Retrieving User Info and Displaying User Profile
  5. Inserting Viewing and Managing Reservations
  6. Viewing Reservation History
  7. Encountered Challenges During the Implementation
  8. Learning Outcomes

With this roadmap, we’re all set to embark on our trust-building journey.

1️⃣ A glimpse of the ‘Auto Care Vehicle Services’ web app

‘Auto Care Vehicle Services’ is a small dynamic web application for vehicle service reservations. This web application has been built using JSP (JavaServer Pages), Servlet, Java™ database connectivity (JDBC), and MySQL Database. CSS and Bootstrap have been used to add 🪄 styles to the pages.

This web application facilitates adding vehicle service reservations, that include date selection, preferred time, location, vehicle details, and user messages. Users also have the option to delete their upcoming reservations and to view their vehicle service reservation history.

You can go through the application source code using this 🔗link.

2️⃣ What is an OpenID Connect?

OpenID Connect is a standardized authentication protocol that operates within the OAuth 2.0 framework. It streamlines the process of confirming user identities through an Authorization Server and fetching user profile details in a consistent and web-friendly manner.

This protocol empowers developers of applications and websites to initiate user sign-in procedures and obtain reliable user information across various platforms, including the web, mobile devices, and JavaScript-based clients. Furthermore, OpenID Connect offers flexibility by allowing the inclusion of optional features like data encryption, the identification of OpenID Providers, and session termination.

For developers, it offers a secure and confirmable solution to the question, “Who is the individual presently using the connected browser or mobile application?” Importantly, it alleviates the burden of establishing, storing, and handling passwords, which are often linked to security breaches involving login credentials.

The OpenID Connect protocol, in the abstract, follows these steps:

  1. The end user navigates to a website or web application via a browser.
  2. The end user clicks sign in and types their username and password.
  3. The RP (Client) sends a request to the OpenID Provider (OP).
  4. The OP authenticates the User and obtains authorization.
  5. The OP responds with an Identity Token and usually an Access Token.
  6. The RP can send a request with the Access Token to the User device.
  7. The UserInfo Endpoint returns Claims about the End-User.
OIDC Protocol Steps

3️⃣ Integrating OIDC for User Authentication

So, in this section, we’ll walk through the process of integrating a cloud based IDP for the user authentication of our web application. As we progress through this guide, you will discover how easy it is to integrate an external identity provider into our ‘Auto Care Vehicle Services’ web application.

There are many external identity providers in the market, and I chose Asgardeo which is one of the trending IDaaS (Identity as a Service) that enables the swift integration of customer identity and access management (CIAM) functionalities into applications, requiring only a few minutes for setup. Additional details can be explored on the official Asgardeo website.

Before everything, we need to have our application registered in Asgardeo.

Follow the below steps to register for the app:

  1. On the Asgardeo Console, go to Applications.
  2. Click New Application and select Traditional Web Application.
  3. Fill out the details about your application.
  4. Click Register to complete the registration.

Upon registering your web application, a unique client ID and client secret are automatically generated. These credentials serve as your web application’s means of identification when interacting with Asgardeo. Access to the client ID and secret can be obtained from the Protocol tab of the application.
To begin, let’s take a look at the file structure of our Auto Care Vehicle Services web application, before implementing the Asgadeo.

project structure before OIDC implementation

To begin, your application needs to send a login request to Asgardeo. For this step, first I included a welcome login page with a login button.

UI — Login Page

To implement this part, we need to specify some properties including client ID, redirect URL, scope, and response type from our registered application in Asgardeo, and include them in the authorization request URL. These properties are required for the OIDC SDK to communicate with Asgardeo during the authentication process.

This is how the authorization request is formatted.

https://api.asgardeo.io/t/<organization_name>/oauth2/authorize?scope={scope}&response_type=code&redirect_uri={redirect_uri}&client_id={client_id}&code_challenge=<code_challenge>&code_challenge_method=<code_challenge_method>

In my application, this has been included as an ‘onclick’ event in the login button like below. Note that, <organization_name>can be found under the organization section on the Nav bar of your Asgardeo console.

<button id="btn-login" class="btn btn-success" onclick="window.location.href='https://api.asgardeo.io/t/<*******>/oauth2/authorize?response_type=code&client_id=<**********>&scope=openid%20email%20phone%20profile&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2FAuto-Care-Vehicle-Services%2Fauthorize.jsp'" >
<span class="btn-text">Login</span>
<div class="fill-container"></div>
</button>

When the user clicks on it, he is redirected to Asgardeo’s login page.

Upon clicking the button, the OIDC agent intercepts the request and triggers the OIDC Login flow if it detects the absence of an authenticated application session. In such cases, users will be redirected to Asgadeo Sign-In page for authentication.

UI — Asgardeo Sign In

Then the user should enter the username and password of his Asgardeo user account. If the credentials are validated, Asgardeo sends an authorization code to our application.

Then the application sends that authorization code back to Asgardeo IDP with a token request and receives the access token and ID token from Asgardeo. After these processes, the user is prompted to the home page of the application.

Basically, sending the authorization code back to Asgardeo IDP with a token request was handled in the authorize.jsp page of the application. And ‘HTTP POST’ request method was used for that. Moreover, I have configured the OpenID Connect endpoints such as Client ID, Client Secret and Redirect URI. These endpoints can be obtained from the Protocol tab and info tab of the application in the Asgadeo console.

After receiving the tokens from the Asgardeo, they were handled, extracted, and stored in session attributes. After all these steps are done, the user will redirected to the home page of my application, where I have implemented the vehicle service reservation functionalities.

UI — Home Page

Furthermore, this web application supports logout functionality. The home.jsp page features a logout link, which initiates a Single Logout (SLO) request.

<form id="logout-form" action="https://api.asgardeo.io/t/<organization_name>/oidc/logout" method="POST">
<input type="hidden" id="client-id" name="client_id" value="<%= client_id %>">
<input type="hidden" id="post-logout-redirect-uri" name="post_logout_redirect_uri" value="<%= post_logout_redirect_uri %>">
<input type="hidden" id="state" name="state" value="<%= sessionState %>">
<button id="logout-btn" type="submit">Logout</button>
</form>

When the user initiates logout, it results in clearing the local authenticated application session and terminating the session in Asgardeo. When a user is successfully logged out, the user is redirected to the post_logout_redirect_uri sent in the logout request. (In my case it’s index.jsp)

postLogoutRedirectURI=http://localhost:8080/Auto-Care-Vehicle-Services/index.jsp

phew… With that (that’s a quite long way though😅), we have done the steps that need to integrate an external identity provider into our ‘Auto Care Vehicle Services’ web application.

4️⃣ Retrieving User Info and Displaying User Profile

In this section, I’ll show you how to display user profile information, including name, email, contact number, and country.
OpenID Connect offers a userinfo endpoint for fetching user details in a payload.

 https://api.asgardeo.io/t/<organization_name>/oauth2/userinfo

To access this information, the application needs to send a request containing the access token to invoke the userinfo endpoint. So, for that, after retrieving the access token and ID token from session attributes we can request necessary user information that is encoded inside the ID token returned along with the access token.

Then we can check the retrieved response code, and whether it is a success (HTTP 200 OK) or not and proceed with the next steps. You can do that in multiple ways. In my home.jsp file, I followed the below steps.

  1. Check for the response code.
  2. If it is a success, then Create a BufferedReader to read the response and read it line by line.
  3. Parse the JSON response into a JSON object. (then we can easily access and manipulate the data within the JSON response in our Java code)
  4. Retrieve the user information.
  5. If the response code is not successful, handle the error response.
if (responseCode == HttpURLConnection.HTTP_OK){

BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuilder responseStringBuilder = new StringBuilder();

while ((inputLine = in.readLine()) != null) {
responseStringBuilder.append(inputLine);
}
in.close();
JSONObject jsonResponse = new JSONObject(responseStringBuilder.toString());
String given_name = jsonResponse.getString("given_name");
String country = jsonResponse.getString("country");
String phone_number = jsonResponse.getString("phone_number");
String email = jsonResponse.getString("email");
} else {
out.println("HTTP Error: " + responseCode);
}

After that I set up a JavaScript event listener on the DOMContentLoaded event of the home page.

<script>
document.addEventListener("DOMContentLoaded", function() {

var first_name = '<%=given_name%>';
var coun_try = '<%=country %>';
var phone_num = '<%=phone_number%>';
var e_mail = '<%=email%>';

document.getElementById("fName").textContent = first_name;
document.getElementById("country").textContent = coun_try;
document.getElementById("phone_no").textContent = phone_num;
document.getElementById("email").textContent = e_mail;
});
</script>

Inside the event listener, the retrieved user information was stored in JavaScript variables, and they were used to update the text content of HTML elements with specific id attributes in necessary places within my home page.

Displaying the Retrieved User Information

5️⃣ Inserting Viewing and Managing Reservations

Well, now I’ll walk you through the process of making vehicle service reservations, covering everything from selecting the date and preferred time to specifying the location, vehicle details, and user messages. I’ll also show you how to give users the ability to delete their upcoming reservations. 😎

To start, users can click the ‘New Reservation’ button located in the top-left corner of the navigation bar. This action redirects them to a page called ‘vehicle-service-form.jsp’. On this page, there’s a convenient form that users can fill out with all the necessary information.

One standout feature of this form is that the ‘Username’ field is automatically populated with the authenticated user’s email address, provided during the Asgardeo sign-in process. This eliminates the need for users to manually enter their email, making the reservation process smoother and more user-friendly.

UI — Add New Reservation

You may wonder how I passed the email to the reservation form’s Username field. I used session attributes to temporarily store the email, which was obtained from the response code at home.jsp file, and then retrieve it in the vehicle-service-form.jsp’ file. Here’s how I did it.

<%
// In home.jsp file -> Store the email in a session attribute
session.setAttribute("userEmail", email);
%>
<%
// In vehicle-service-form.jsp file -> Retrieve the email from the session attribute
String userEmail = (String) session.getAttribute("userEmail");
%>

<!-- Reservation form -->
<form action="insert" method="post">
<!-- Other form fields -->

<!-- Email field with pre-filled value -->
<label>Username (Authenticated User)</label>
<input type="text" class="form-control" name="username" value="<%=userEmail%>" required="required" readonly>

<!-- Submit button -->
<button type="submit" class="btn btn-success">Save</button>
</form>

Make sure to keep the username field as ‘readonly’.

The next functionality is retrieving and displaying all the reservations that were added by the user. For that, I again used the user’s email address, provided during the Asgardeo sign-in process, and appended the userEmail parameter to the URL within the href attribute. The value of userEmail is obtained from the session attribute as same as I've shown earlier.

<%
String useremail = (String) session.getAttribute("userEmail"); // Get the user's email from the session
%>

<a href="<%=request.getContextPath()%>/list?email=<%=useremail%>" class="btn btn-success">View My Reservations</a>

So, when the user clicks the “Show My Reservations” link, it will include the email parameter in the URL, and it will be sent to the listVehicleService method in my VehicleServiceServlet.java class.

String email = request.getParameter("email"); // retrieving the user email fom the home page.
listVehicleService(request, response,email);

In this way, we can pass the user email to select an authenticated user’s all vehicle reservations from the database. Finally, I have displayed the reservations in a simple table shown below.

UI — Viewing Reservation History

Last but not least, I’ve included an option that allows users to delete any upcoming reservations. You’re welcome to delve into the source code to see how I’ve implemented this functionality under the hood.

With that, I’ve demonstrated all the necessary parts which I wanted to cover in this article. However, I thought to add another section to share the obstacles which I have faced during the implementation process. how to verify the identity of the end-user, and how to obtain basic user profile information and implement the logout functionality using the Asgardeo OpenID Connect protocols.

6️⃣ Encountered Challenges During the Implementation

Challenge: Variable Name Confusion in Retrieving User Info

During the implementation process, one challenge I encountered was related to variable naming when retrieving and storing user information from the access token and storing them in the JavaScript function. This may seem like a minor issue, but it can lead to unexpected problems.

Explanation:

When we retrieve user information from the access token and store it in JavaScript variables, it’s crucial to use unique variable names in both places. In other words, the variable names used to capture user data in the server-side code (e.g., Java) and the variable names used in client-side JavaScript should not clash or share the same references.

If the same variable names are used in both server-side and client-side code, it can lead to confusion, especially when the server sends data to the client. The server may have its own variables with the same names, and this naming conflict can cause unexpected behavior.

<script>
document.addEventListener("DOMContentLoaded", function() {

// use different names in Java and JavaScript

var first_name = '<%=given_name%>';
var coun_try = '<%=country %>';
var phone_num = '<%=phone_number%>';
var e_mail = '<%=email%>';


// use different names in JavaScript variables and in your HTML elements

document.getElementById("fName").textContent = first_name;
document.getElementById("country").textContent = coun_try;
document.getElementById("phone_no").textContent = phone_num;
document.getElementById("email").textContent = e_mail;
});
</script>

Solution:

To avoid this issue, it’s a best practice to use distinct and clearly differentiated variable names in both the server-side and client-side code. This ensures that there is no confusion or overlap between the variables used in different parts of the application, allowing for smooth and error-free data display.

By paying attention to variable naming conventions, we can prevent this seemingly minor issue from causing significant challenges and ensure a seamless user experience in our web applications.

8️⃣ Learning Outcomes

Finally, I would like to highlight the learning outcomes of the first part of this project, which involving the verification of the end user’s identity, obtaining basic user profile information, and implementing logout functionality using Asgardeo OpenID Connect protocols in a simple dynamic web app for reserving vehicle services, utilizing JSP, Java, JDBC, and Java Servlets, may encompass the following:

  1. Authentication and Identity Verification: Gain a deep understanding of authentication mechanisms, specifically OpenID Connect protocols, and how they can be used to securely verify the identity of end-users in a web application.
  2. User Profile Management: Learn how to retrieve and manage basic user profile information from authenticated users, such as names, email addresses, or other relevant details, using Java and database interactions (JDBC).
  3. Web Application Development: Acquire practical experience in building dynamic web applications using Java Servlets and JSP (JavaServer Pages), including handling user requests, processing form submissions, and generating dynamic content.
  4. Database Integration: Develop proficiency in integrating a relational database (e.g., MySQL, PostgreSQL) with a web application, including creating database tables, executing SQL queries, and handling data storage and retrieval.
  5. Session Management: Implement session management techniques to maintain user state and securely manage user sessions during their interaction with the web application.
  6. Exception Handling: Learn how to handle exceptions, errors, and edge cases gracefully to ensure the robustness and reliability of the web application.
  7. Project Organization: Develop skills in project organization, code structure, and documentation to ensure maintainability and collaboration with team members.
  8. Testing and Debugging: Master the art of testing and debugging web applications to identify and resolve issues efficiently.

Congratulations! 🎉 We’ve reached the conclusion of this article. However, the journey doesn’t end here! In my next article of this series, titled ‘Defending Against Threats: Securing Your Web App by Defeating OWASP Top 10 Vulnerabilities’, we’ll dive deeper into ensuring the fortification of our Auto care Vehicle Services application against vulnerabilities listed in the OWASP Top 10. It will demonstrate how to secure your application and defend against common threats proactively. Stay tuned for a comprehensive guide on enhancing your application’s resilience and data protection.

Let’s continue our journey together!

See you soon! 👋

--

--

Himani Perera

Final Year Software Engineering Undergraduate at University of Kelaniya, Sri Lanka