<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Saravanan Muniraj</title>
    <description>The latest articles on Forem by Saravanan Muniraj (@smuniraj).</description>
    <link>https://forem.com/smuniraj</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1242983%2Fb38f0b7d-7503-4afe-86f4-b4a8c40b7292.png</url>
      <title>Forem: Saravanan Muniraj</title>
      <link>https://forem.com/smuniraj</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/smuniraj"/>
    <language>en</language>
    <item>
      <title>Building Custom AutoLogin Module for Liferay Integration with Azure AD B2C</title>
      <dc:creator>Saravanan Muniraj</dc:creator>
      <pubDate>Tue, 31 Mar 2026 16:36:27 +0000</pubDate>
      <link>https://forem.com/smuniraj/building-custom-autologin-module-for-liferay-integration-with-azure-ad-b2c-45a4</link>
      <guid>https://forem.com/smuniraj/building-custom-autologin-module-for-liferay-integration-with-azure-ad-b2c-45a4</guid>
      <description>&lt;p&gt;Azure AD B2C provides a robust, cloud-based identity management solution that offers secure authentication, multi-factor authentication support, and seamless integration with enterprise systems. When combined with Liferay's flexible portal capabilities, you get a powerful platform that can handle complex authentication scenarios while maintaining a great user experience.&lt;/p&gt;

&lt;p&gt;The Solution: Custom AutoLogin Module&lt;/p&gt;

&lt;p&gt;The approach involves creating a custom AutoLogin component that intercepts the authentication flow, validates tokens from Azure B2C, and automatically logs users into Liferay. Let's break down the implementation.&lt;/p&gt;

&lt;p&gt;Step 1: Create the AutoLogin Class&lt;/p&gt;

&lt;p&gt;First, we need to create a class that extends BaseAutoLogin. This class will handle the authentication logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WebsiteAutoLogin&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseAutoLogin&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Reference&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;UserLocalService&lt;/span&gt; &lt;span class="n"&gt;userLocalService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;RestTemplate&lt;/span&gt; &lt;span class="n"&gt;restTemplate&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Activate&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;activateComponent&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;restTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RestTemplate&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The @Reference annotation injects Liferay's UserLocalService, which we'll use to look up users. The RestTemplate is initialized during component activation and will be used for making HTTP calls to Azure B2C.&lt;/p&gt;

&lt;p&gt;Step 2: Implement the doLogin Method&lt;/p&gt;

&lt;p&gt;The core authentication logic resides in the doLogin method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="nf"&gt;doLogin&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpServletRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HttpServletResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;AutoLoginException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// 1) Extract the ID token from the request&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ParamUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"id_token"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 2) Exchange code for ID token - Call the B2C REST API&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;tokenEncodedString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;retrieveAzureToken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 3) Decode the JWT payload&lt;/span&gt;
    &lt;span class="nc"&gt;JSONObject&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;decodeJWTToken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenEncodedString&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 4) Extract user identifier from the token&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;screenName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MatchingScreenName"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;companyId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PortalUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCompanyId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// 5) Fetch existing user by screen name&lt;/span&gt;
        &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userLocalService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetchUserByScreenName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;companyId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;screenName&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 6) Return credentials for auto-login&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valueOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUserId&lt;/span&gt;&lt;span class="o"&gt;()),&lt;/span&gt;
            &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPassword&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
            &lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TRUE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;

        &lt;span class="o"&gt;};&lt;/span&gt;

    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error during user login or creation: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method performs the following steps:&lt;/p&gt;

&lt;p&gt;Extracts the authorization code or ID token from the incoming request&lt;br&gt;
Exchanges this code with Azure B2C to obtain a valid token&lt;br&gt;
Decodes the JWT token to extract user information&lt;br&gt;
Looks up the corresponding Liferay user&lt;br&gt;
Returns the authentication credentials for automatic login&lt;br&gt;
Step 3: Token Retrieval from Azure B2C&lt;/p&gt;

&lt;p&gt;The retrieveAzureToken method handles communication with Azure B2C's token endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;retrieveAzureToken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;authorizationCode&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Get the token endpoint URL&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;tokenEndpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getTokenEndpoint&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// Create HTTP headers&lt;/span&gt;
        &lt;span class="nc"&gt;HttpHeaders&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HttpHeaders&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setContentType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;APPLICATION_FORM_URLENCODED&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// Encode the redirect URI&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;redirectUri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;URLEncoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"YOUR_REDIRECT_URL"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// Build form parameters&lt;/span&gt;
       &lt;span class="nc"&gt;MultiValueMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;formParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LinkedMultiValueMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
        &lt;span class="n"&gt;formParams&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"client_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;B2C_AZURE_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;formParams&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"client_secret"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;B2C_AZURE_CLIENT_SECRET&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;formParams&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"code"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;authorizationCode&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;formParams&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"grant_type"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;AUTH_GRANT_TYPE&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;formParams&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"redirect_uri"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;redirectUri&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// Create and send the request&lt;/span&gt;
        &lt;span class="nc"&gt;HttpEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MultiValueMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;requestEntity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HttpEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;formParams&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;restTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;postForEntity&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;tokenEndpoint&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;requestEntity&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;

        &lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// Parse and return the ID token&lt;/span&gt;
        &lt;span class="nc"&gt;JSONObject&lt;/span&gt; &lt;span class="n"&gt;jsonObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JSONFactoryUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createJSONObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBody&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;jsonObject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id_token"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Exception occurred on B2C Azure Token Request: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 4: JWT Token Decoding&lt;/p&gt;

&lt;p&gt;The JWT token returned by Azure B2C needs to be decoded to extract user claims:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;JSONObject&lt;/span&gt; &lt;span class="nf"&gt;decodeJWTToken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Validator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isNull&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// Split the token into header, payload, and signature&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// Decode the payload (second part) from Base64&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;JSONFactoryUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createJSONObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Exception occurred while decoding the JSON token: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configuration Requirements&lt;/p&gt;

&lt;p&gt;Before deploying this module, ensure you have the following configured:&lt;/p&gt;

&lt;p&gt;Azure AD B2C Configuration:&lt;/p&gt;

&lt;p&gt;Register your application in Azure AD B2C&lt;br&gt;
Configure the redirect URIs&lt;br&gt;
Note your Client ID and Client Secret&lt;br&gt;
Set up the appropriate user flows (sign-in, sign-up)&lt;br&gt;
Liferay Configuration:&lt;/p&gt;

&lt;p&gt;Deploy the custom AutoLogin module&lt;br&gt;
Configure the module with your Azure B2C credentials&lt;br&gt;
Ensure users exist in Liferay with matching screen names&lt;br&gt;
Security Considerations&lt;/p&gt;

&lt;p&gt;When implementing this solution, keep these security best practices in mind:&lt;/p&gt;

&lt;p&gt;Secure credential storage: Store your Azure B2C client secrets in Liferay's secure configuration, not in code&lt;br&gt;
Token validation: Always validate the JWT token's signature and expiration before trusting its claims&lt;br&gt;
HTTPS only: Ensure all communication happens over HTTPS&lt;br&gt;
Error handling: Implement proper error handling to avoid exposing sensitive information&lt;br&gt;
Logging: Log authentication events for auditing, but never log sensitive token data&lt;/p&gt;

&lt;p&gt;Conclusion&lt;/p&gt;

&lt;p&gt;Integrating Azure AD B2C with Liferay through a custom AutoLogin module provides a secure and seamless authentication experience. This approach leverages Azure's enterprise-grade identity management while maintaining Liferay's flexibility and extensibility.&lt;/p&gt;

&lt;p&gt;The complete implementation gives you a foundation that can be extended to support additional features like user provisioning, role mapping based on Azure AD groups, and multi-factor authentication.&lt;/p&gt;

</description>
      <category>java</category>
      <category>azure</category>
    </item>
    <item>
      <title>Convert an Excel dataset into a SQL insert statement</title>
      <dc:creator>Saravanan Muniraj</dc:creator>
      <pubDate>Wed, 06 Nov 2024 18:52:25 +0000</pubDate>
      <link>https://forem.com/smuniraj/convert-excel-dataset-into-sql-insert-statement-38k4</link>
      <guid>https://forem.com/smuniraj/convert-excel-dataset-into-sql-insert-statement-38k4</guid>
      <description>&lt;p&gt;Utilizing Python makes converting Excel files to SQL databases a straightforward process.&lt;/p&gt;

&lt;p&gt;To begin, export the Excel data to a CSV file by following these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open your Excel file.&lt;/li&gt;
&lt;li&gt;Navigate to File &amp;gt; Save As.&lt;/li&gt;
&lt;li&gt;Select CSV (Comma delimited) (*.csv) as the file type and save the file.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By following these simple instructions, you can seamlessly transition your Excel data into a format that is compatible with SQL databases.&lt;/p&gt;

&lt;p&gt;FIRST_NAME  LAST_NAME   EMAIL   USER_ID USER_LOGIN_NAME&lt;br&gt;
First01 Last01  firstlastname01 ID001   loginname01&lt;br&gt;
First02 Last02  firstlastname02 ID002   loginname02&lt;br&gt;
First03 Last03  firstlastname03 ID003   loginname03&lt;br&gt;
First04 Last04  firstlastname04 ID004   loginname04&lt;br&gt;
First05 Last05  firstlastname05 ID005   loginname05&lt;br&gt;
First06 Last06  firstlastname06 ID006   loginname06&lt;br&gt;
First07 Last07  firstlastname07 ID007   loginname07&lt;br&gt;
First08 Last08  firstlastname08 ID008   loginname08&lt;/p&gt;

&lt;p&gt;Utilize a script or tool to convert CSV files to SQL format. For instance, you can employ a Python script to parse the CSV file and create SQL insert statements. Below is a basic Python script to help you begin the conversion process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import pandas as pd

# Read the CSV file into a DataFrame
df = pd.read_csv('D:/temp/test/TestExcel.csv') # Add the path to your CSV file

# Generate SQL insert statements
table_name = 'Test_Table_Name' # Replace with your desired table name
sql_statements = []

for index, row in df.iterrows():
    columns = ', '.join(row.index)
    values = ', '.join([f"'{str(value)}'" for value in row.values])
    sql_statements.append(f"INSERT INTO {table_name} ({columns}) VALUES ({values});")

# Save to a file
with open('D:/temp/test/insert_statements.sql', 'w') as f:
    for statement in sql_statements:
        f.write(statement + '\n')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following are the results of the scripts generated by the code above.&lt;/p&gt;

&lt;p&gt;INSERT INTO Test_Table_Name (FIRST_NAME, LAST_NAME, EMAIL, USER_ID, USER_LOGIN_NAME) VALUES ('First01', 'Last01', 'firstlastname01', 'ID001', 'loginname01');&lt;br&gt;
INSERT INTO Test_Table_Name (FIRST_NAME, LAST_NAME, EMAIL, USER_ID, USER_LOGIN_NAME) VALUES ('First02', 'Last02', 'firstlastname02', 'ID002', 'loginname02');&lt;br&gt;
INSERT INTO Test_Table_Name (FIRST_NAME, LAST_NAME, EMAIL, USER_ID, USER_LOGIN_NAME) VALUES ('First03', 'Last03', 'firstlastname03', 'ID003', 'loginname03');&lt;br&gt;
INSERT INTO Test_Table_Name (FIRST_NAME, LAST_NAME, EMAIL, USER_ID, USER_LOGIN_NAME) VALUES ('First04', 'Last04', 'firstlastname04', 'ID004', 'loginname04');&lt;br&gt;
INSERT INTO Test_Table_Name (FIRST_NAME, LAST_NAME, EMAIL, USER_ID, USER_LOGIN_NAME) VALUES ('First05', 'Last05', 'firstlastname05', 'ID005', 'loginname05');&lt;br&gt;
INSERT INTO Test_Table_Name (FIRST_NAME, LAST_NAME, EMAIL, USER_ID, USER_LOGIN_NAME) VALUES ('First06', 'Last06', 'firstlastname06', 'ID006', 'loginname06');&lt;br&gt;
INSERT INTO Test_Table_Name (FIRST_NAME, LAST_NAME, EMAIL, USER_ID, USER_LOGIN_NAME) VALUES ('First07', 'Last07', 'firstlastname07', 'ID007', 'loginname07');&lt;br&gt;
INSERT INTO Test_Table_Name (FIRST_NAME, LAST_NAME, EMAIL, USER_ID, USER_LOGIN_NAME) VALUES ('First08', 'Last08', 'firstlastname08', 'ID008', 'loginname08');&lt;/p&gt;

&lt;p&gt;Please note that there are online tools available to &lt;a href="https://webutility.io/csv-to-insert-sql-online" rel="noopener noreferrer"&gt;convert CSV files to SQL insert statements&lt;/a&gt;. It is important to exercise caution when using these tools to avoid exposing sensitive data. In some cases, the company may have blocked access to certain websites for security reasons.&lt;/p&gt;

</description>
      <category>python</category>
      <category>csv</category>
    </item>
    <item>
      <title>A Simple Guide to Loading an Entire PDF into a List of Documents Using Langchain</title>
      <dc:creator>Saravanan Muniraj</dc:creator>
      <pubDate>Thu, 03 Oct 2024 02:51:29 +0000</pubDate>
      <link>https://forem.com/smuniraj/a-simple-guide-to-loading-an-entire-pdf-into-a-list-of-documents-using-langchain-4hih</link>
      <guid>https://forem.com/smuniraj/a-simple-guide-to-loading-an-entire-pdf-into-a-list-of-documents-using-langchain-4hih</guid>
      <description>&lt;p&gt;Before diving into the code, it is essential to install the necessary packages to ensure everything runs smoothly. You can do this by executing the following commands in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install langchain_community
pip install pypdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

# Load the PDF file from the specified path.

FILE_PATH = "c:/work/Test01.pdf"

loader = PyPDFLoader(file_path=FILE_PATH)

# Load the entire PDF into a list of documents

text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)

documents = loader.load_and_split(text_splitter)

for i in range(len(documents)):
    print(documents[i].page_content + "\n")```



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>ai</category>
      <category>langchain</category>
      <category>python</category>
    </item>
    <item>
      <title>Integrating SendGrid SMTP Mail with Liferay</title>
      <dc:creator>Saravanan Muniraj</dc:creator>
      <pubDate>Wed, 04 Sep 2024 17:33:08 +0000</pubDate>
      <link>https://forem.com/smuniraj/integrating-sendgrid-smtp-mail-with-liferay-1fe9</link>
      <guid>https://forem.com/smuniraj/integrating-sendgrid-smtp-mail-with-liferay-1fe9</guid>
      <description>&lt;p&gt;&lt;a href="https://sendgrid.com/why-sendgrid/" rel="noopener noreferrer"&gt;SendGrid&lt;/a&gt; is a mail relay service that operates through APIs and cloud-based technology. As the leading provider of email delivery services, we understand the importance of ensuring that your emails are delivered without any issues or being caught in filters. That is why SendGrid has optimized our mail transfer agent (MTA) and developed efficient tools for managing deliverability.&lt;/p&gt;

&lt;p&gt;You have multiple options to begin using SendGrid.&lt;br&gt;
To utilize the API, you must complete the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Establish a SendGrid Account.&lt;/li&gt;
&lt;li&gt; Generate an API Key. Consult the Sendgrid documentation for instructions on creating an API key.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Liferay Support SMTP service operates as a means of transmitting emails from one mail server (or mail client) to another over the Internet. SendGrid primarily serves as a third-party SMTP service, allowing us to send emails on behalf of our users and their clients in order to enhance their delivery rates. By signing up for a SendGrid account, you gain access to our SMTP mail servers and have the option to send your emails through us instead of using a webmail client or your own server.&lt;/p&gt;

&lt;p&gt;The built-in mail session of &lt;a href="https://help.liferay.com/hc/en-us/articles/360029031591-Configuring-Mail" rel="noopener noreferrer"&gt;Liferay DXP&lt;/a&gt; is the most convenient method for configuring mail and is highly recommended. You can set up the built-in mail session using any of the following approaches:&lt;br&gt;
• Control Panel&lt;br&gt;
configure the mail session from the Control Panel.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Sign in as the administrative user (the user you specified on the Basic Configuration page).&lt;/li&gt;
&lt;li&gt; Navigate to Control Panel → Configuration → Server Administration → Mail.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Outgoing SMTP Server: smtp.sendgrid.net&lt;br&gt;
Outgoing Port:25&lt;br&gt;
User Name:apikey&lt;br&gt;
Password:&amp;lt; API Key &amp;gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Convert all the first letters in a statement to capital letters</title>
      <dc:creator>Saravanan Muniraj</dc:creator>
      <pubDate>Sat, 30 Dec 2023 05:37:46 +0000</pubDate>
      <link>https://forem.com/smuniraj/convert-all-the-first-letters-in-a-statement-to-capital-letters-i4i</link>
      <guid>https://forem.com/smuniraj/convert-all-the-first-letters-in-a-statement-to-capital-letters-i4i</guid>
      <description>&lt;p&gt;To convert all the first letters in a statement to capital letters, you can achieve this by checking if the addresses stored in the database are all in lowercase and then display them with the first letter capitalized using Java.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private static String capitalizeFirst(String word) {
    return word.substring(0, 1).toUpperCase()
      + word.substring(1).toLowerCase();
}


public static String capitalizeAllFirstLetters(String address){

    Pattern pattern = Pattern.compile("^[A-Z,a-z]");
    String[] addressArray = address.split(" ");
    StringBuffer afterChangesAddress = new StringBuffer();

    for(int i=0;i&amp;lt;addressArray.length;i++){
         Matcher m = pattern.matcher(addressArray[i]);
          if(m.find()){
              afterChangesAddress.append(capitalizeFirst(addressArray[i])).append(" ");
         }else{
             afterChangesAddress.append(addressArray[i]).append(" ");
         }
    }
    return afterChangesAddress.substring(0, afterChangesAddress.lastIndexOf(" "));

 }




public static void main(String[] args) {


    String str = capitalizeAllFirstLetters("1180 seven seats dr, orlando, fl 3283");

    System.out.println(str);

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output:&lt;br&gt;
1180 Seven Seats Dr, Orlando, Fl 3283&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Eliminate duplicate values in a table</title>
      <dc:creator>Saravanan Muniraj</dc:creator>
      <pubDate>Thu, 28 Dec 2023 04:00:00 +0000</pubDate>
      <link>https://forem.com/smuniraj/eliminate-duplicate-values-in-a-table-3kj8</link>
      <guid>https://forem.com/smuniraj/eliminate-duplicate-values-in-a-table-3kj8</guid>
      <description>&lt;p&gt;In SQL server, the "PARTITION BY" clause is utilized to divide a result set into partitions depending on the values of one or more columns. This feature can be employed to eliminate duplicate values in a table.&lt;/p&gt;

&lt;p&gt;WITH CTE AS&lt;br&gt;
(&lt;br&gt;
SELECT *,ROW_NUMBER() OVER (PARTITION BY Column_Name ORDER BY Column_Name ) AS RN&lt;br&gt;
FROM Table_Name&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;DELETE FROM CTE WHERE RN&amp;lt;&amp;gt;1&lt;/p&gt;

</description>
    </item>
    <item>
      <title>com.liferay.petra.process.ProcessException: java.lang.OutOfMemoryError: Unable to process file version</title>
      <dc:creator>Saravanan Muniraj</dc:creator>
      <pubDate>Thu, 28 Dec 2023 03:26:34 +0000</pubDate>
      <link>https://forem.com/smuniraj/comliferaypetraprocessprocessexception-javalangoutofmemoryerror-unable-to-process-file-version-elf</link>
      <guid>https://forem.com/smuniraj/comliferaypetraprocessprocessexception-javalangoutofmemoryerror-unable-to-process-file-version-elf</guid>
      <description>&lt;p&gt;&lt;strong&gt;Issue&lt;/strong&gt;&lt;br&gt;
Liferay Site goes down due to outofMemoryError. When someone upload the inappropriate PDF file in Liferay Media and Document &lt;br&gt;
The flowing error message thrown:&lt;/p&gt;

&lt;p&gt;[liferay/document_library_pdf_processor-1][BaseProcessorMessageListener:40] Unable to process file version 123456&lt;br&gt;
java.util.concurrent.ExecutionException: com.liferay.petra.process.ProcessException: java.lang.OutOfMemoryError&lt;br&gt;
               at java.util.concurrent.FutureTask.report(FutureTask.java:122)&lt;br&gt;
               at java.util.concurrent.FutureTask.get(FutureTask.java:206)&lt;br&gt;
         [ommited]&lt;br&gt;
Caused by: com.liferay.petra.process.ProcessException: java.lang.OutOfMemoryError: Java heap space&lt;br&gt;
    at com.liferay.petra.process.local.LocalProcessLauncher.main(LocalProcessLauncher.java:153)&lt;br&gt;
Caused by: java.lang.OutOfMemoryError: Java heap space&lt;br&gt;
    at java.awt.image.DataBufferInt.(DataBufferInt.java:75)&lt;br&gt;
    at java.awt.image.Raster.createPackedRaster(Raster.java:467)&lt;br&gt;
    at java.awt.image.DirectColorModel.createCompatibleWritableRaster(DirectColorModel.java:1032)&lt;br&gt;
    at java.awt.image.BufferedImage.(BufferedImage.java:324)&lt;/p&gt;

&lt;p&gt;How Can I fix this error?&lt;br&gt;
&lt;strong&gt;Environment&lt;/strong&gt;&lt;br&gt;
   Liferay DXP 7.1&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resolution&lt;/strong&gt;&lt;br&gt;
Run the below SQL script and get the file details: &lt;br&gt;
SELECT USERNAME,CREATEDATE,MODIFIEDDATE,FILENAME,TITLE,SIZE_ FROM DLFILEENTRY (NOLOCK)  WHERE FILEENTRYID IN &lt;br&gt;
(SELECT  FILEENTRYID FROM DLFILEENTRYMETADATA (NOLOCK) WHERE FILEVERSIONID IN (123456))&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Remove the file from Liferay document and media. &lt;/li&gt;
&lt;li&gt; Ensure that all content/pages of the PDF file are in the same layout orientation throughout the file. All pages must either be in portrait or landscape orientation — the file cannot contain a mixture of portrait and landscape sizes.&lt;/li&gt;
&lt;li&gt; Once you have verified that the pages are in the same orientation, save the PDF files using the Save as Optimized option via Adobe Acrobat Pro. This will compress the file size and remove unnecessary code for faster viewing.&lt;/li&gt;
&lt;/ol&gt;

</description>
    </item>
  </channel>
</rss>
