<?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>DEV Community: Mohamed Radwan</title>
    <description>The latest articles on DEV Community by Mohamed Radwan (@maradwan).</description>
    <link>https://dev.to/maradwan</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%2F720938%2F2fd0db10-0c82-4989-90b1-7d2dbdcb3c67.jpeg</url>
      <title>DEV Community: Mohamed Radwan</title>
      <link>https://dev.to/maradwan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/maradwan"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Mohamed Radwan</dc:creator>
      <pubDate>Sat, 19 Jul 2025 20:39:04 +0000</pubDate>
      <link>https://dev.to/maradwan/-45og</link>
      <guid>https://dev.to/maradwan/-45og</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/aws-builders/enriching-keycloak-with-linkedin-vanityname-headline-profile-picture-via-custom-spi-g40" class="crayons-story__hidden-navigation-link"&gt;Enriching Keycloak with LinkedIn VanityName, Headline &amp;amp; Profile Picture via Custom SPI&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;
          &lt;a class="crayons-logo crayons-logo--l" href="/aws-builders"&gt;
            &lt;img alt="AWS Community Builders  logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F2794%2F88da75b6-aadd-4ea1-8083-ae2dfca8be94.png" class="crayons-logo__image"&gt;
          &lt;/a&gt;

          &lt;a href="/maradwan" class="crayons-avatar  crayons-avatar--s absolute -right-2 -bottom-2 border-solid border-2 border-base-inverted  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F720938%2F2fd0db10-0c82-4989-90b1-7d2dbdcb3c67.jpeg" alt="maradwan profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/maradwan" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Mohamed Radwan
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Mohamed Radwan
                
              
              &lt;div id="story-author-preview-content-2611866" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/maradwan" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F720938%2F2fd0db10-0c82-4989-90b1-7d2dbdcb3c67.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Mohamed Radwan&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

            &lt;span&gt;
              &lt;span class="crayons-story__tertiary fw-normal"&gt; for &lt;/span&gt;&lt;a href="/aws-builders" class="crayons-story__secondary fw-medium"&gt;AWS Community Builders &lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;a href="https://dev.to/aws-builders/enriching-keycloak-with-linkedin-vanityname-headline-profile-picture-via-custom-spi-g40" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 21 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/aws-builders/enriching-keycloak-with-linkedin-vanityname-headline-profile-picture-via-custom-spi-g40" id="article-link-2611866"&gt;
          Enriching Keycloak with LinkedIn VanityName, Headline &amp;amp; Profile Picture via Custom SPI
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/keycloak"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;keycloak&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/aws-builders/enriching-keycloak-with-linkedin-vanityname-headline-profile-picture-via-custom-spi-g40" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/aws-builders/enriching-keycloak-with-linkedin-vanityname-headline-profile-picture-via-custom-spi-g40#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>keycloak</category>
      <category>opensource</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Mohamed Radwan</dc:creator>
      <pubDate>Sun, 13 Jul 2025 12:39:14 +0000</pubDate>
      <link>https://dev.to/maradwan/-1mpd</link>
      <guid>https://dev.to/maradwan/-1mpd</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/aws-builders/extract-invoice-data-automatically-using-langchain-ga7" class="crayons-story__hidden-navigation-link"&gt;Extract Invoice Data Automatically Using LangChain&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;
          &lt;a class="crayons-logo crayons-logo--l" href="/aws-builders"&gt;
            &lt;img alt="AWS Community Builders  logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F2794%2F88da75b6-aadd-4ea1-8083-ae2dfca8be94.png" class="crayons-logo__image"&gt;
          &lt;/a&gt;

          &lt;a href="/maradwan" class="crayons-avatar  crayons-avatar--s absolute -right-2 -bottom-2 border-solid border-2 border-base-inverted  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F720938%2F2fd0db10-0c82-4989-90b1-7d2dbdcb3c67.jpeg" alt="maradwan profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/maradwan" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Mohamed Radwan
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Mohamed Radwan
                
              
              &lt;div id="story-author-preview-content-2680895" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/maradwan" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F720938%2F2fd0db10-0c82-4989-90b1-7d2dbdcb3c67.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Mohamed Radwan&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

            &lt;span&gt;
              &lt;span class="crayons-story__tertiary fw-normal"&gt; for &lt;/span&gt;&lt;a href="/aws-builders" class="crayons-story__secondary fw-medium"&gt;AWS Community Builders &lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;a href="https://dev.to/aws-builders/extract-invoice-data-automatically-using-langchain-ga7" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jul 13 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/aws-builders/extract-invoice-data-automatically-using-langchain-ga7" id="article-link-2680895"&gt;
          Extract Invoice Data Automatically Using LangChain
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/langchain"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;langchain&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/aws"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;aws&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/cognito"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;cognito&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/aws-builders/extract-invoice-data-automatically-using-langchain-ga7" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;7&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/aws-builders/extract-invoice-data-automatically-using-langchain-ga7#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            1 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>ai</category>
      <category>langchain</category>
    </item>
    <item>
      <title>Extract Invoice Data Automatically Using LangChain</title>
      <dc:creator>Mohamed Radwan</dc:creator>
      <pubDate>Sun, 13 Jul 2025 12:01:54 +0000</pubDate>
      <link>https://dev.to/aws-builders/extract-invoice-data-automatically-using-langchain-ga7</link>
      <guid>https://dev.to/aws-builders/extract-invoice-data-automatically-using-langchain-ga7</guid>
      <description>&lt;p&gt;In this article, I’m sharing an app I built to automate invoice processing using image recognition and language models. The goal is simple: take scanned or photographed invoices (in JPG, PNG, or PDF format) and extract structured data in JSON.&lt;/p&gt;

&lt;p&gt;Under the hood, the system uses OpenAI’s GPT-4o (GPT-4 Vision) model via LangChain, and it’s wrapped in a lightweight FastAPI backend built with Python. The app can batch process files, run locally or in a container, and outputs clean JSON ready for downstream systems like accounting tools or CRMs.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/lCVW4B7hKCc"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack &amp;amp; Architecture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The app is structured with a modern, API-first architecture:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend&lt;/strong&gt;: Python with FastAPI, using LangChain + GPT-4o for invoice processing&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authentication&lt;/strong&gt;: AWS Cognito for secure, scalable user auth&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Database&lt;/strong&gt;: MongoDB for storing processed invoice data and metadata&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frontend&lt;/strong&gt;: A Next.js app handles the UI and connects to the backend via API&lt;/p&gt;

&lt;p&gt;The authentication layer with AWS Cognito makes it easy to manage user sign-ups, login access. Invoice data and any synced product info are stored in MongoDB, which works well with JSON-like structures. &lt;br&gt;
On the frontend, Next.js provides a fast and reactive UI that lets users upload invoice images, view extracted data, and manage syncing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sync Invoice Items with Product Barcodes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the app extracts line items from the invoice such as product names, you can take it a step further by syncing these items with your internal product database. This allows you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Match items by name&lt;/li&gt;
&lt;li&gt;Automatically assign or update barcodes&lt;/li&gt;
&lt;li&gt;Link products to existing inventory systems&lt;/li&gt;
&lt;li&gt;Detect mismatches or missing items&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>langchain</category>
      <category>aws</category>
      <category>cognito</category>
    </item>
    <item>
      <title>Enriching Keycloak with LinkedIn VanityName, Headline &amp; Profile Picture via Custom SPI</title>
      <dc:creator>Mohamed Radwan</dc:creator>
      <pubDate>Sat, 21 Jun 2025 10:57:36 +0000</pubDate>
      <link>https://dev.to/aws-builders/enriching-keycloak-with-linkedin-vanityname-headline-profile-picture-via-custom-spi-g40</link>
      <guid>https://dev.to/aws-builders/enriching-keycloak-with-linkedin-vanityname-headline-profile-picture-via-custom-spi-g40</guid>
      <description>&lt;p&gt;When integrating LinkedIn as an Identity Provider in Keycloak, you may quickly discover a limitation: by default, LinkedIn only provides basic user information such as email, first name, and last name.&lt;/p&gt;

&lt;p&gt;However, in many real-world applications, you may also want to retrieve and store additional profile details, including:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Vanity name (public LinkedIn profile ID)

High-resolution profile picture

Professional headline (user's job title or tagline)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To achieve this, you'll need to create a custom Identity Provider Mapper SPI (Service Provider Interface) in Keycloak. This extension allows you to fetch additional data from LinkedIn's API and map it into the Keycloak user model during the authentication process.&lt;/p&gt;

&lt;p&gt;Step 1: Set Up Your LinkedIn App Permissions&lt;/p&gt;

&lt;p&gt;Before you begin implementing the custom SPI, make sure your LinkedIn developer app has the correct permissions. You'll need to request access to the following&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fegll4foj8y14oplc6qbe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fegll4foj8y14oplc6qbe.png" alt="Image description" width="800" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These scopes allow your Keycloak instance to retrieve detailed user information such as their profile picture and professional headline.&lt;br&gt;
 Configuring LinkedIn as an Identity Provider in Keycloak (Tested on Keycloak 26.2.5)&lt;/p&gt;

&lt;p&gt;After you've created a LinkedIn App and obtained the required credentials, follow these steps to configure LinkedIn in your example Keycloak realm:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to your Keycloak admin console.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go to:&lt;br&gt;
Identity Providers → Select LinkedIn.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fill in the following fields:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;        Client ID: (from your LinkedIn app)&lt;/li&gt;
&lt;li&gt;        Client Secret: (from your LinkedIn app)&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;In the Scopes field, add the following permissions:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;openid profile email r_basicprofile&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;These scopes are necessary to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;    Authenticate the user (openid)&lt;/li&gt;
&lt;li&gt;    Retrieve profile information (profile, r_basicprofile)&lt;/li&gt;
&lt;li&gt;    Fetch the user’s verified email address (email)&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Enable the following options:&lt;/li&gt;
&lt;li&gt;Store Tokens (This is required for the SPI to extract the LinkedIn access token and make additional API calls.)&lt;/li&gt;
&lt;li&gt;Trust Email (Assumes the email provided by LinkedIn is verified.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Click Save.&lt;/p&gt;

&lt;p&gt;To use this LinkedIn SPI extension in your Keycloak setup:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Download the JAR file&lt;br&gt;
Either build it yourself using Maven, or download a prebuilt version from the &lt;a href="https://github.com/maradwan/keycloak-linkedin-profile-mapper/releases" rel="noopener noreferrer"&gt;GitHub Releases page&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy the JAR to Keycloak's provider directory&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cp linkedin-profile-mapper-1.0.0.jar /opt/keycloak/providers/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Rebuild Keycloak to recognize the new provider
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/opt/keycloak/bin/kc.sh build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Restart Keycloak
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    /opt/keycloak/bin/kc.sh start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Source Code &amp;amp; Releases&lt;br&gt;
You can find the full source code and latest releases at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://github.com/maradwan/keycloak-linkedin-profile-mapper

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

&lt;/div&gt;



&lt;p&gt;Then you need to add Identity Provider Mapper &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcv2vj3q7ph1qxspeucgx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcv2vj3q7ph1qxspeucgx.png" alt="Image description" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then you need to add attributes for vanityName, profilePicture, and headline in the User profile that is in the Realm settings of your example realm &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwjypa5tr5m0cj7ixcfs3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwjypa5tr5m0cj7ixcfs3.png" alt="Image description" width="800" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an example-client in Keycloak
&lt;/h2&gt;

&lt;p&gt;In your Keycloak Admin Console:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to your realm (e.g., example), then navigate to Clients → click Create client.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter example-client as the Client ID, choose OpenID Connect, and click Next.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set Root URL to &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;, and Valid Redirect URI to &lt;a href="http://localhost:3000/callback" rel="noopener noreferrer"&gt;http://localhost:3000/callback&lt;/a&gt;, then click Save.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go to the Credentials tab → copy the Client Secret.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Under Settings, enable Standard Flow, optionally enable Direct Access Grants.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use these details in your app:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Test the Integration (Optional FastAPI Example)
&lt;/h2&gt;

&lt;p&gt;You can test the LinkedIn login flow and see the enriched tokens by using a simple FastAPI application:&lt;/p&gt;

&lt;p&gt;Install the required packages:&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 fastapi uvicorn requests
&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;# app.py
from fastapi import FastAPI, Request
from fastapi.responses import RedirectResponse
import requests

app = FastAPI()

# === CONFIG ===
KEYCLOAK_BASE_URL = "https://idp.example.com"
REALM = "example"
CLIENT_ID = "example-client"
CLIENT_SECRET = "XXXXXXXXX"  # Only needed for confidential clients
REDIRECT_URI = "http://localhost:3000/callback"  # Your app's redirect URI

# === ROUTES ===

@app.get("/login")
def login():
    return RedirectResponse(
        f"{KEYCLOAK_BASE_URL}/realms/{REALM}/protocol/openid-connect/auth"
        f"?client_id={CLIENT_ID}"
        f"&amp;amp;redirect_uri={REDIRECT_URI}"
        f"&amp;amp;response_type=code"
        f"&amp;amp;scope=openid"
    )

@app.get("/callback")
def callback(request: Request):
    code = request.query_params.get("code")
    if not code:
        return {"error": "No authorization code provided"}

    # Exchange code with Keycloak (not LinkedIn)
    token_response = requests.post(
        f"{KEYCLOAK_BASE_URL}/realms/{REALM}/protocol/openid-connect/token",
        data={
            "grant_type": "authorization_code",
            "code": code,
            "redirect_uri": REDIRECT_URI,
            "client_id": CLIENT_ID,
            "client_secret": CLIENT_SECRET,
        },
        headers={"Content-Type": "application/x-www-form-urlencoded"},
    )

    if token_response.status_code != 200:
        return {"error": "Token exchange failed", "details": token_response.json()}

    token_data = token_response.json()

    # Decode the token to inspect (optional)
    id_token = token_data.get("id_token")
    access_token = token_data.get("access_token")
    return {
        "access_token": access_token,
        "id_token": id_token,
        "message": "Use this token to call secured APIs or decode for LinkedIn data",
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the App&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uvicorn app:app --reload --port 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvy09houqrazit25j1fs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvy09houqrazit25j1fs.png" alt="Image description" width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sources:&lt;br&gt;
&lt;a href="https://learn.microsoft.com/en-us/linkedin/marketing/quick-start?view=li-lms-2025-03#step-1-apply-for-api-access" rel="noopener noreferrer"&gt;https://learn.microsoft.com/en-us/linkedin/marketing/quick-start?view=li-lms-2025-03#step-1-apply-for-api-access&lt;/a&gt;&lt;/p&gt;

</description>
      <category>keycloak</category>
      <category>opensource</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Mohamed Radwan</dc:creator>
      <pubDate>Mon, 13 Jan 2025 09:23:36 +0000</pubDate>
      <link>https://dev.to/maradwan/-131</link>
      <guid>https://dev.to/maradwan/-131</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/aws-builders" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F2794%2F88da75b6-aadd-4ea1-8083-ae2dfca8be94.png" alt="AWS Community Builders " width="350" height="350"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1126677%2F13a7aa1d-6abb-48cb-92f0-790836504f90.PNG" alt="" width="800" height="794"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/aws-builders/how-i-automated-certificate-expiration-alerts-with-aws-1g10" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;How I automated Certificate expiration alerts with AWS&lt;/h2&gt;
      &lt;h3&gt;Tetiana Mostova for AWS Community Builders  ・ Jan 12&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#aws&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#security&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#automation&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Mohamed Radwan</dc:creator>
      <pubDate>Wed, 25 Dec 2024 12:34:15 +0000</pubDate>
      <link>https://dev.to/maradwan/-54pg</link>
      <guid>https://dev.to/maradwan/-54pg</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/aws-builders" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F2794%2F88da75b6-aadd-4ea1-8083-ae2dfca8be94.png" alt="AWS Community Builders " width="350" height="350"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1197801%2Fdbe218cb-52e9-4537-9087-e8ff2e45956c.jpeg" alt="" width="800" height="800"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/aws-builders/installing-argocd-and-securing-access-using-amazon-cognito-3gnn" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Installing ArgoCD and Securing Access Using Amazon Cognito&lt;/h2&gt;
      &lt;h3&gt;Ravindra Singh for AWS Community Builders  ・ Sep 27&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#kubernetes&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#eks&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#cognito&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>aws</category>
      <category>community</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Optimize AWS Cloud Costs</title>
      <dc:creator>Mohamed Radwan</dc:creator>
      <pubDate>Mon, 11 Nov 2024 18:01:21 +0000</pubDate>
      <link>https://dev.to/maradwan/optimize-aws-cloud-costs-2g4m</link>
      <guid>https://dev.to/maradwan/optimize-aws-cloud-costs-2g4m</guid>
      <description>&lt;p&gt;Optimize AWS cloud costs by implementing the following strategies:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VPC Optimization&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Manage Data Transfer Costs&lt;/strong&gt;: NAT gateways incur costs; in most cases, two subnets with 99.9% availability are sufficient. For 99.99% availability, use three subnets with three NAT gateways. Two subnets are generally sufficient for most scenarios.&lt;/li&gt;
&lt;li&gt;Remove IP v4 addresses that are not needed or associated with Network Interface Card (NIC).&lt;/li&gt;
&lt;li&gt;Use VPC endpoints to privately connect to supported AWS services and VPC endpoint services, rather than relying on NAT gateways, to reduce costs. Additionally, AWS PrivateLink is often required for other purposes, such as compliance, irrespective of cost considerations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;EKS/EC2/Auto Scaling Group (ASG) Optimization&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Implement Instance Auto scaling&lt;/strong&gt;: Configure autoscaling with Spot instances for worker nodes by using &lt;a href="https://karpenter.sh/" rel="noopener noreferrer"&gt;Karpenter&lt;/a&gt;  to adjust resources based on demand dynamically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Reserved Instances and Savings Plans&lt;/strong&gt;: Optimize for a single node type, such as &lt;code&gt;m5a.large&lt;/code&gt; or &lt;code&gt;m5a.xlarge&lt;/code&gt; , for most applications to gain cost efficiencies through reserved capacity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rightsize Resources&lt;/strong&gt;: Regularly reassess applications and nodes to evaluate utilization and rightsize resources for optimized performance and cost.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;scheduled actions&lt;/strong&gt; in the Auto Scaling Group ASG for test and development environments to automatically stop instances at specified times when compute nodes are not in use and start them when needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CPU Type&lt;/strong&gt;: ARM-based CPUs (such as AWS Graviton) are more cost-effective but may have compatibility issues with certain applications. Developers should ensure application compatibility to support multiple CPU types.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upgrades&lt;/strong&gt;: Cloud providers may impose fees for clusters that are not upgraded to a supported version. For EKS, using outdated Kubernetes Cluster requires Extended Support, resulting in additional expenses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;EBS Optimization&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure that EBS volumes are configured to use the GP3 storage type, which is 20% more cost-effective than GP2.&lt;/li&gt;
&lt;li&gt;Delete unused EBS volumes and snapshots that are no longer in use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;S3 Optimization&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Optimize Storage Costs for Object Storage&lt;/strong&gt;: Configure lifecycle policies, especially when using versioning, and choose the appropriate storage class (e.g., Standard or Infrequent Access). Consider S3 Intelligent Tiering for automated cost-efficient storage management.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Database Optimization&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For RDS, consider rightsizing, using Graviton-based instances, and performing mass engine version upgrades if applications support the new version.&lt;/li&gt;
&lt;li&gt;Switching to Amazon Aurora I/O-Optimized increases instance costs by approximately 25% to include I/O operations, making it an ideal choice for high I/O workloads. This option offers improved cost predictability and can potentially reduce total costs when I/O demand is significant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upgrades&lt;/strong&gt;: Cloud providers may impose fees for clusters that are not upgraded to a supported version. For RDS, using outdated database engines may require Extended Support, resulting in additional expenses.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>finops</category>
    </item>
    <item>
      <title>Take IT Shipping with Serverless Technology</title>
      <dc:creator>Mohamed Radwan</dc:creator>
      <pubDate>Thu, 21 Mar 2024 12:35:59 +0000</pubDate>
      <link>https://dev.to/aws-builders/take-it-shipping-with-serverless-technology-ehi</link>
      <guid>https://dev.to/aws-builders/take-it-shipping-with-serverless-technology-ehi</guid>
      <description>&lt;p&gt;In this article, I'll take you through the journey of creating a mobile application utilizing serverless architecture. &lt;/p&gt;

&lt;p&gt;The app idea is a platform that facilitates and supports peer-to-peer shipping services, connecting requesters and travelers.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/DP1yWZ6utHk"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;FrontEnd &lt;a href="https://github.com/maradwan/takeit-app" rel="noopener noreferrer"&gt;Flutter Code&lt;/a&gt;&lt;br&gt;
Backend &lt;a href="https://github.com/maradwan/takeit-backend" rel="noopener noreferrer"&gt;Lambda Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiby0sg0k53m1ah4mw6md.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiby0sg0k53m1ah4mw6md.jpeg" alt="Architecture" width="800" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The App Architecture:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frontend Framework (Flutter)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Flutter serves as the frontend framework for developing the mobile application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flutter allows for cross-platform development, enabling the app to run on both iOS and Android devices from a single codebase.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Backend Services (Lambda)&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;The backend service is built using Lambda, a serverless computing platform.&lt;br&gt;
Lambda functions are deployed in response to events triggered by user actions, ensuring scalability and cost-efficiency.&lt;br&gt;
"The Take IT" app utilizes Python Flask within the Lambda functions&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authentication (Cognito):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cognito, a serverless authentication service, manages the sign-up/sign-in process in the Take IT app. Additionally, it seamlessly integrates with identity providers such as Google or Facebook. Authentication is typically managed using JWT (JSON Web Tokens) for secure and efficient user authentication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API Gateway:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Integrating AWS API Gateway with Cognito offers enhanced security and scalability for the Take IT app. &lt;br&gt;
API Gateway ensures only authenticated users can access backend services. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Database (DynamoDB)&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;DynamoDB serves as the serverless database utilized to store all data in the Take IT app. It is leveraged to manage user preferences, trip details, contacts, as well as the status of sent and received requests (including accepted, pending, and declined requests).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adding a Trip&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv4275tv58eujw2jk12ul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv4275tv58eujw2jk12ul.png" alt="Adding a Trip" width="800" height="1688"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The function looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def add_trip(item):
    return query_table.put_item(Item=item)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below is an example of how a trip is saved into DynamoDB in JSON format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
 "username": "4fb1dce7-6a50-41a8-8d7d",
 "created": "2024-10-04-19-34-18-736061",
 "acceptfrom": "2024-10-05",
 "acceptto": "2024-10-30",
 "allowed": {
  "Clothes": {
   "cost": "3.0",
   "kg": "10.0"
  },
  "Electronics": {
   "cost": "20.0",
   "kg": "5.0"
  }
 },
 "currency": "EUR",
 "fromcity": "Berlin-Germany",
 "fromto": "Berlin-Germany_Cairo-Egypt",
 "tocity": "Cairo-Egypt",
 "trdate": "2024-10-31",
 "tstamp": 1732961762
}

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

&lt;/div&gt;



&lt;p&gt;By utilizing the TTL (Time to Live) feature in DynamoDB, you can automatically delete records after a specified time period. For instance, in the trip record, there is an attribute called "tstamp" that determines the deletion time of the record.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Find available trips&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The attributes in the trip record, such as "fromCity," "toCity," or "fromTo," are utilized for search functionality when users seek trips. I employ a global secondary index in DynamoDB to retrieve trips based on the originating city, destination city, or the combination of both.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkyrbttp7jpsmgpgpwcub.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkyrbttp7jpsmgpgpwcub.png" alt="Find Trips" width="800" height="1688"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The function looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Index: This refers to the name of the global secondary index.&lt;/li&gt;
&lt;li&gt;Key: This represents the DynamoDB attribute used for indexing, typically referring to "fromCity" or "toCity."&lt;/li&gt;
&lt;li&gt;City: Denotes the name of the city being referenced.&lt;/li&gt;
&lt;li&gt;Limit: Specifies the maximum number of records to retrieve.&lt;/li&gt;
&lt;li&gt;Today_date: This indicates the current date, used to filter and display only trips available from today onwards. &lt;/li&gt;
&lt;li&gt;LastKey: Utilized for pagination purposes, facilitating the retrieval of subsequent sets of records beyond the initial limit.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def get_global_index(index,key,city,limit,today_date,lastkey=None):

    if lastkey:
        return query_table.query(
        IndexName=index,KeyConditionExpression=Key(
            key).eq(city),Limit=int(limit),FilterExpression=Attr('acceptto').gte(today_date),ExclusiveStartKey=json.loads(lastkey))

    return query_table.query(
        IndexName=index,KeyConditionExpression=Key(
            key).eq(city),Limit=int(limit),FilterExpression=Attr('acceptto').gte(today_date))

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Users Sent/Received Requests&lt;/strong&gt;&lt;br&gt;
DynamoDB supports querying data with a key that begins with a specific value. When users send or receive requests in the Take IT app, these requests are stored in DynamoDB with statuses such as "pending," "accepted," "request," or "declined." This allows for efficient querying and retrieval of requests based on their status, enabling seamless management and tracking of request statuses within the application.&lt;/p&gt;

&lt;p&gt;The function looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;item: This refers to "pending," "accepted," "request," or "declined."
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;query_table.query(KeyConditionExpression=Key("username").eq(username) &amp;amp; Key("created").begins_with(item))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here's an example of how a user request is saved into DynamoDB in JSON format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
 "username": "user2-466b-a4b9-94f90",
 "created":  "request_2022-10-15-22-35-18-213147_user1-4fb1dce7-6a50",
 "dtime": "2022-10-15-22-42-41-599518",
 "tripid": "2022-10-15-22-35-18-213147",
 "tstamp": 1669766400
},
{
 "username": "user1-4fb1dce7-6a50",
 "created": "pending_2022-10-15-22-35-18-213147_user2-466b-a4b9-94f90",
 "dtime": "2022-10-15-22-42-41-599518",
 "tripid": "2022-10-15-22-35-18-213147",
 "tstamp": 1669766400
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Remove Account&lt;/strong&gt;&lt;br&gt;
When a user decides to delete their account in the Take IT app, several steps are initiated:&lt;/p&gt;

&lt;p&gt;Querying User Data: All data associated with the user is queried from DynamoDB, including trip history, pending requests, and any other relevant information.&lt;/p&gt;

&lt;p&gt;Deleting User Data: Each record associated with the user's account is deleted from DynamoDB.&lt;br&gt;
This includes trip records, request records (both sent and received), and any other user-specific data stored in the database.&lt;/p&gt;

&lt;p&gt;Removing User from Cognito: The user is removed from the Cognito user pool, deleting their account and associated authentication tokens.&lt;/p&gt;

&lt;p&gt;The Cognito delete function looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cognito = boto3.client('cognito-idp',region_name = region_name, verify=True)
cognito.admin_delete_user(UserPoolId= userpoolid, Username= username)

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

&lt;/div&gt;



&lt;p&gt;The delete function looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def delete_records(username):
   items= query_table.query(
       KeyConditionExpression=Key("username").eq(username)
   )
   for i in range(len(items['Items'])):
       query_table.delete_item(
       Key={
           'username': username,
           'created' : items['Items'][i]['created']
           }
           )
   return True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>aws</category>
      <category>serverless</category>
      <category>dynamodb</category>
      <category>lambda</category>
    </item>
    <item>
      <title>Configure a privately hosted Git repository for EMR Studio</title>
      <dc:creator>Mohamed Radwan</dc:creator>
      <pubDate>Thu, 12 Oct 2023 09:13:02 +0000</pubDate>
      <link>https://dev.to/aws-builders/configure-a-privately-hosted-git-repository-for-emr-studio-2ih</link>
      <guid>https://dev.to/aws-builders/configure-a-privately-hosted-git-repository-for-emr-studio-2ih</guid>
      <description>&lt;p&gt;By default, you can access GitHub and GitLab from the studio workspace. If you are using a private Git repository, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/emr/latest/ManagementGuide/emr-studio-create-studio.html"&gt;Create a Studio&lt;/a&gt;: You should use a VPC with private subnets that have a NAT gateway to enable communication with the internet. &lt;/li&gt;
&lt;li&gt;Ensure that the security group for the default workspace has an outbound rule allowing HTTPS traffic on port 443 to the destination 0.0.0.0/0&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/emr/latest/ManagementGuide/interface-vpc-endpoint.html"&gt;Create VPC Endpoint&lt;/a&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Make sure the endpoint uses private subnets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Confirm that the security group attached to the endpoint allows inbound rules for HTTPS traffic on port 443 with the source set to either "Your VPC Address" or "0.0.0.0/0".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Upload the following configuration file into your Amazon S3 storage location that is used for your Studio in a folder called life-cycle-configuration:&lt;br&gt;
s3://BUCKET-NAME/life-cycle-configuration/configuration.json&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitServerDnsName - The DNS name of your Git server. For example "git.example.com".&lt;/p&gt;

&lt;p&gt;GitServerIpV4List - A list of IPv4 addresses that belong to your Git servers, the example VPC CIDR is 10.0.0.0/16, &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/vpc-dns.html"&gt;DNS&lt;/a&gt; is 10.0.0.2&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
    {
        "Type": "PrivatelyHostedGitConfig",
        "Value": [
            {
                "DnsServerIpV4": "10.0.0.2",
                "GitServerDnsName": "git.example.com",
                "GitServerIpV4List": [
                    "1.2.3.4"
                ]
            }
        ]
    }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"If you are facing issues, you may need to stop the workspace and start it again."&lt;/p&gt;

</description>
      <category>emr</category>
      <category>eks</category>
      <category>aws</category>
    </item>
    <item>
      <title>Fix Cert-Manager Conflict with EKS</title>
      <dc:creator>Mohamed Radwan</dc:creator>
      <pubDate>Wed, 29 Mar 2023 21:45:03 +0000</pubDate>
      <link>https://dev.to/aws-builders/conflict-cert-manager-with-eks-2lbf</link>
      <guid>https://dev.to/aws-builders/conflict-cert-manager-with-eks-2lbf</guid>
      <description>&lt;p&gt;I was facing issue with multiple managed worker nodes running on EKS clusters. &lt;/p&gt;

&lt;p&gt;The issue was appearing randomly in different nodes, I cannot access the pods or get the logs by kubectl.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x509: cannot validate certificate for 10.0.83.153 because it doesn’t contain any IP SANs 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kube API in the CloudWatch showing the following errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;E0327 08:54:17.406029 11 status.go:71] apiserver received an error that is not an metav1.Status: &amp;amp;errors.errorString{s:"error dialing backend: x509: cannot validate certificate for 10.0.83.153 because it doesn't contain any IP SANs"}: error dialing backend: x509: cannot validate certificate for 10.0.83.153 because it doesn't contain any IP SANs 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After investigating the issue with the AWS EKS support team, we found that cert-manager-webhook is causing the issue.&lt;br&gt;
Kubelet certificate chain is being used from cert-manager-webhook-ca.&lt;/p&gt;

&lt;p&gt;Run the following command on the non-working node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openssl s_client -connect localhost:10250 
&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;CONNECTED(00000003)
---
Certificate chain
 0 s:
   i:/CN=cert-manager-webhook-ca
---
Server certificate
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
subject=
issuer=/CN=cert-manager-webhook-ca
---
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the following command on the working healthy node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openssl s_client -connect localhost:10250 
&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;CONNECTED(00000003)
---
Certificate chain
 0 s:/O=system:nodes/CN=system:node:ip-10-0-31-151.eu-west-1.compute.internal
   i:/CN=kubernetes
---
Server certificate
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
subject=/O=system:nodes/CN=system:node:ip-10-0-31-151.eu-west-1.compute.internal
issuer=/CN=kubernetes
---
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cert-manager-webhook deployment uses port 10250 which is also used for kubelet.&lt;/p&gt;

&lt;p&gt;The solution is change the port of cert-manager-webhook to 10260.&lt;/p&gt;

&lt;p&gt;By setting webhook.securePort to 10260&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version v1.10.0 \
  --set webhook.securePort=10260
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sources:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cert-manager.io/docs/concepts/webhook/"&gt;https://cert-manager.io/docs/concepts/webhook/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://cert-manager.io/docs/installation/compatibility/#aws-eks"&gt;https://cert-manager.io/docs/installation/compatibility/#aws-eks&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>eks</category>
      <category>kubernetes</category>
      <category>openssl</category>
    </item>
    <item>
      <title>Integrate Keycloak and Kubeapps on AWS EKS</title>
      <dc:creator>Mohamed Radwan</dc:creator>
      <pubDate>Sun, 25 Sep 2022 09:47:15 +0000</pubDate>
      <link>https://dev.to/aws-builders/integrate-keycloak-and-kubeapps-on-aws-eks-3fpm</link>
      <guid>https://dev.to/aws-builders/integrate-keycloak-and-kubeapps-on-aws-eks-3fpm</guid>
      <description>&lt;p&gt;In this article, I am going to show you how to integrate keycloak with kubeapps on AWS EKS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/create-cluster.html" rel="noopener noreferrer"&gt;EKS Cluster&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Register a domain in &lt;a href="https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/domain-register.html" rel="noopener noreferrer"&gt;route53&lt;/a&gt; or create a subdomain, ex: example.com &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Request a certificate from &lt;a href="https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-request-public.html" rel="noopener noreferrer"&gt;ACM&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install Keycloak&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm repo add bitnami https://charts.bitnami.com/bitnami
helm install my-release bitnami/keycloak --version 9.8.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No needs to use the service Loadbalancer for keycloak as we will use the Nginx ingress controller.&lt;br&gt;
Let's switch the service of keycloak to ClusterIP.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl patch svc my-release-keycloak  -p '{"spec": {"type": "ClusterIP"}}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the admin password of keycloak&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl get secrets/my-release-keycloak -o jsonpath='{.data.admin-password}' | base64 --decode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Install Nginx Ingress Controller
Update the AWS ACM certificate on the below installation
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

helm install nginx ingress-nginx/ingress-nginx --set controller.service.type=LoadBalancer  --set controller.service.targetPorts.https=http --set controller.service.annotations."service\.beta\.kubernetes\.io/aws-load-balancer-ssl-cert"="arn:aws:acm:eu-west-1:XXXX:certificate/XXXX-XXX-XXX-XXXX-XXXXX"  --set controller.service.annotations."service\.beta\.kubernetes\.io/aws-load-balancer-backend-protocol"=http --set controller.service.annotations."service\.beta\.kubernetes\.io/aws-load-balancer-ssl-ports"=443   --set-string controller.config.use-forwarded-headers="true" --version 4.2.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create ingress for keycloak
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-buffer-size: 10k
  name: keycloak
  namespace: default
spec:
  rules:
  - host: auth.example.com
    http:
      paths:
      - backend:
          service:
            name: my-release-keycloak
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Login to the keycloak console &lt;a href="https://auth.example.com" rel="noopener noreferrer"&gt;https://auth.example.com&lt;/a&gt;, with username: &lt;strong&gt;user&lt;/strong&gt; and password in the keycloak installation step.&lt;/p&gt;

&lt;p&gt;Create a new realm, name it kubeapps&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxumh4q0r0jg2txorxt1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxumh4q0r0jg2txorxt1.png" alt="Image description" width="800" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Groups Claim&lt;/strong&gt;&lt;br&gt;
By default, there is no "groups" scope/claim. We will create a global client scope for groups.&lt;/p&gt;

&lt;p&gt;In the admin console:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click "Client Scopes" from the left navigator menu&lt;/li&gt;
&lt;li&gt;Click on "Create" from the table (top right corner)&lt;/li&gt;
&lt;li&gt;Provide a name, ensure the protocol is set to "openid-connect" and that the option "Include in Token Scope" is on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3171x6bn8f15h5urr3ig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3171x6bn8f15h5urr3ig.png" alt="Image description" width="800" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the client scope is created, you should be redirected to a page with several tabs. Navigate to the "Mappers" tab as we need to create a mapper to populate the value of the associated claim:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on the "Mappers" tab&lt;/li&gt;
&lt;li&gt;Click on "Create" from the table to create a new mapper&lt;/li&gt;
&lt;li&gt;Configure:&lt;/li&gt;
&lt;li&gt;Enter a name&lt;/li&gt;
&lt;li&gt;Select "Group Membership" as the claim type&lt;/li&gt;
&lt;li&gt;Enter "groups" as the token claim name&lt;/li&gt;
&lt;li&gt;Ensure the "Full group path" is OFF&lt;/li&gt;
&lt;li&gt;Keep the other knobs as ON&lt;/li&gt;
&lt;li&gt;Click ‘Save'&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmek8it15nuzx46mpgwx0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmek8it15nuzx46mpgwx0.png" alt="Image description" width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kubeapps Client on Keycloak&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click "Clients" from the left navigator&lt;/li&gt;
&lt;li&gt;Click "Create" from the table&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter an "id" and Save (e.g. kubeapps)&lt;br&gt;
Once created, configure the authentication as follows:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ensure the protocol is set to "openid-connect"&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure the "Access Type" to be "confidential". This will add a new "Credentials" tab from which you can get the client secret&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ensure "Standard Flow Enabled" is enabled, this is required for the login screen.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;"Direct Access Grants Enabled" can be disabled.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the "Valid Redirect URIs" field, enter "&lt;a href="https://kubeapps.example.com/*" rel="noopener noreferrer"&gt;https://kubeapps.example.com/*&lt;/a&gt;" as a placeholder. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Save&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6uxznimcwrb6s2a54ny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6uxznimcwrb6s2a54ny.png" alt="Image description" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note: take copy of the secret in Credentials&lt;/p&gt;

&lt;p&gt;As for the cluster clients, we need to configure the client scopes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click the "Client Scopes" tab&lt;/li&gt;
&lt;li&gt;Ensure the "email" scope is available either in the "Assigned Default Client Scopes" list or the "Assigned Optional Client Scopes" list&lt;/li&gt;
&lt;li&gt;The "groups" client scope should be available in the lists on the left. &lt;/li&gt;
&lt;li&gt;Add it either to the "Assigned Default Client 
Scopes" list or the "Assigned Optional Client Scopes" list.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2zbo351dsfhe8l2u00be.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2zbo351dsfhe8l2u00be.png" alt="Image description" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note the issuer URL from keycloak Realm&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Realm Settings →  Endpoints →  OpenID Endpoint Configuration then copy the issuer URL &lt;br&gt;
ex: &lt;a href="https://auth.example.com/realms/kubeapps" rel="noopener noreferrer"&gt;https://auth.example.com/realms/kubeapps&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On the EKS Cluster:&lt;/strong&gt;&lt;br&gt;
To the EKS cluster and then to Authentication&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feeflewcj54yni07jmeii.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feeflewcj54yni07jmeii.png" alt="Image description" width="800" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add the identity provider like the following:&lt;br&gt;
Issuer URL: &lt;a href="https://auth.example.com/realms/kubeapps" rel="noopener noreferrer"&gt;https://auth.example.com/realms/kubeapps&lt;/a&gt;&lt;br&gt;
Client ID: kubernetes&lt;br&gt;
Username claim: email&lt;br&gt;
Groups claim: groups&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz3gsc3byyr7jw0dfw4j3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz3gsc3byyr7jw0dfw4j3.png" alt="Image description" width="800" height="973"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;the process takes around 20 minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Back to Keycloak&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a new Client&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click "Clients" from the left navigator&lt;/li&gt;
&lt;li&gt;Click "Create" from the table&lt;/li&gt;
&lt;li&gt;Enter an "id" kubernetes and Save &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once created, configure the authentication as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure the protocol is set to "openid-connect"&lt;/li&gt;
&lt;li&gt;Configure the "Access Type" to be "confidential"&lt;/li&gt;
&lt;li&gt;turn on the "Standard Flow Enabled"&lt;/li&gt;
&lt;li&gt;Ensure "Direct Access Grants Enabled" is enabled, as this is how we can get the tokens via API&lt;/li&gt;
&lt;li&gt;Save&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajiv1wzsh5se57l8mapo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajiv1wzsh5se57l8mapo.png" alt="Image description" width="800" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylt3qpb9hi31i6pe57mj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylt3qpb9hi31i6pe57mj.png" alt="Image description" width="800" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On Kubeapps client&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Clients → Mappers → Create &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2ni584i81vqxnbog7l9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2ni584i81vqxnbog7l9.png" alt="Image description" width="800" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx8qo8a8niu8vyaodns7z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx8qo8a8niu8vyaodns7z.png" alt="Image description" width="800" height="166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a group&lt;/strong&gt;&lt;br&gt;
The group name is admin&lt;br&gt;
Groups --&amp;gt; New&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmyqwclblaxaa0oijky5i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmyqwclblaxaa0oijky5i.png" alt="Image description" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add users to join the group&lt;/strong&gt;&lt;br&gt;
Users --&amp;gt; Your User --&amp;gt; Groups --&amp;gt; choose admin --&amp;gt; join&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fesb025hcealj9xi8kh63.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fesb025hcealj9xi8kh63.png" alt="Image description" width="800" height="293"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install Kubeapps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Update the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clientSecret gets it from the above step Kubeapps Client on keycloak&lt;/li&gt;
&lt;li&gt;issuer-url and redirect-url for your domain.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm repo add bitnami https://charts.bitnami.com/bitnami

helm install kubeapps bitnami/kubeapps \
  --set authProxy.enabled=true \
  --set authProxy.provider=oidc \
  --set authProxy.clientID=kubeapps \
  --set authProxy.clientSecret=&amp;lt;secret client credentials in the kubeapps client&amp;gt; \
  --set authProxy.cookieSecret=$(echo "not-good-secret" | base64) \
  --set authProxy.extraFlags="{--cookie-secure=false,--oidc-issuer-url=https://auth.example.com/realms/kubeapps,--redirect-url=https://kubeapps.example.com/oauth2/callback}" \
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Create an ingress for kubeapps&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/proxy-buffer-size: 10k
  name: kubeapps
  namespace: default
spec:
  ingressClassName: nginx
  rules:
  - host: kubeapps.example.com
    http:
      paths:
      - backend:
          service:
            name: kubeapps
            port:
              number: 80
        pathType: ImplementationSpecific

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

&lt;/div&gt;



&lt;p&gt;Important to add proxy-buffer-size on the ingress controller otherwise you will get an error that says "upstream sent too big header while reading response header from upstream"&lt;/p&gt;

&lt;p&gt;Create a new user on the keycloak, make sure you add email, and Email Verified is on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyfrbtu7115jwngq2rz0k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyfrbtu7115jwngq2rz0k.png" alt="Image description" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last step we need to create a role binding to access the Kubernetes cluster, this group has the same name admin as the keycloak group&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  managedFields:
  - apiVersion: rbac.authorization.k8s.io/v1
  name: keycloak-admin-group
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, log in to Kubeapps at &lt;a href="https://kubeapps.example.com" rel="noopener noreferrer"&gt;https://kubeapps.example.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn18tdhos7zkhmqc9n85a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn18tdhos7zkhmqc9n85a.png" alt="Image description" width="800" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you want to connect to EKS cluster by using OIDC&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Install &lt;a href="https://github.com/int128/kubelogin" rel="noopener noreferrer"&gt;kubelogin&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to keycloak and then go back to the Kubernetes client we created. You will see a tab called Credentials, grab the client secret.&lt;/li&gt;
&lt;li&gt;Run the below command in your terminal to verify authentication to keycloak:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl oidc-login setup --oidc-issuer-url=https://auth.example.com/realms/kubeapps --oidc-client-id=kubernetes --oidc-client-secret=XXXXXXXXXXX
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fatbb6x77yt7zg1k6sso2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fatbb6x77yt7zg1k6sso2.png" alt="Image description" width="800" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bind a cluster role&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl create clusterrolebinding oidc-cluster-admin --clusterrole=cluster-admin --user='https://auth.example.com/realms/kubeapps#f5092396-d3b8-452c-9d1a-2b1dfbd58718'

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Set up the kubeconfig&lt;/strong&gt;&lt;br&gt;
Use the same secret of Kubernetes client&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl config set-credentials oidc \
      --exec-api-version=client.authentication.k8s.io/v1beta1 \
      --exec-command=kubectl \
      --exec-arg=oidc-login \
      --exec-arg=get-token \
      --exec-arg=--oidc-issuer-url=https://auth.example.com/realms/kubeapps \
      --exec-arg=--oidc-client-id=kubernetes \
      --exec-arg=--oidc-client-secret=XXXXXXX \
          --exec-arg=--oidc-extra-scope=groups
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verify cluster access&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl --user=oidc get nodes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Switch the default context to oidc&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl config set-context --current --user=oidc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In case you have issues, you need to check if your token has added admin group:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"groups": [&lt;br&gt;
    "admin"&lt;br&gt;
  ]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -L -X POST 'https://auth.example.com/realms/kubeapps/protocol/openid-connect/token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=kubernetes' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_secret=XXXXXXX' \
--data-urlencode 'scope=openid email groups' \
--data-urlencode 'username=mohamed.radwan' \
--data-urlencode 'password=XXXXX' \
| jq -r .id_token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;check your token by &lt;a href="https://jwt.io/" rel="noopener noreferrer"&gt;jwt.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sources&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/authenticate-oidc-identity-provider.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/eks/latest/userguide/authenticate-oidc-identity-provider.html&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/vmware-tanzu/kubeapps/blob/main/site/content/docs/latest/howto/OIDC/OAuth2OIDC-keycloak.md" rel="noopener noreferrer"&gt;https://github.com/vmware-tanzu/kubeapps/blob/main/site/content/docs/latest/howto/OIDC/OAuth2OIDC-keycloak.md&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>eks</category>
      <category>keycloak</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Kubectl exec/port-forward with AWS ALB and nginx-ingress-controller</title>
      <dc:creator>Mohamed Radwan</dc:creator>
      <pubDate>Fri, 01 Jul 2022 09:46:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/kubectl-execport-forward-with-aws-alb-and-nginx-ingress-controller-4fn0</link>
      <guid>https://dev.to/aws-builders/kubectl-execport-forward-with-aws-alb-and-nginx-ingress-controller-4fn0</guid>
      <description>&lt;p&gt;I was facing issues when I performed kubectl with exec or port-forward option on my Rancher clusters that used EKS and ALB, it was giving me this error&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl exec -it app -- bash

Error from server (BadRequest): Upgrade request required
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;exec and port forward are using SPDY protocol and ALB does not support it.&lt;/p&gt;

&lt;p&gt;The HTTPS request is going from the user to ALB, then SSL is terminated on the ALB, and the request is forwarded to the Nginx controller service after that forward to the rancher service.   &lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/aws-builders/setup-rancher-on-eks-alb-50"&gt;See the part 1 to setup Rancher on EKS and ALB &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After that, you need to do the following:&lt;/p&gt;

&lt;p&gt;1- Install Nginx Ingress Controller&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kubernetes v1.16+ use version 3.x.x&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm upgrade --install nginx ingress-nginx/ingress-nginx --namespace cattle-system --set controller.service.type=NodePort  --set controller.service.targetPorts.https=http --set-string controller.config.use-forwarded-headers="true" --version 3.12.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kubernetes v1.19+ use version 4.x.x&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm upgrade --install nginx ingress-nginx/ingress-nginx --namespace cattle-system --set controller.service.type=NodePort  --set controller.service.targetPorts.https=http --set-string controller.config.use-forwarded-headers="true" --version 4.2.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2- Edit the rancher Ingress&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl edit ingress -n cattle-system rancher
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the host and name inside spec with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
spec:
   rules:
   - host: '*.example.com'
     http:
       paths:
       - backend:
           service:
             name: nginx-ingress-nginx-controller
             port:
               number: 80
         pathType: ImplementationSpecific
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3- Create a new ingress with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
  name: rancher-exec
  namespace: cattle-system

spec:
   rules:
   - host: 'rancher.example.com'
     http:
       paths:
       - backend:
           service:
             name: rancher
             port:
               number: 80
         pathType: ImplementationSpecific
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need to add ingressClassName: nginx  only for &lt;strong&gt;rancher-exec&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
  name: rancher-exec
  namespace: cattle-system

spec:
   ingressClassName: nginx 
   rules:
   - host: 'rancher.example.com'
     http:
       paths:
       - backend:
           service:
             name: rancher
             port:
               number: 80
         pathType: ImplementationSpecific
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: It will remove the ALB if you add ingressClassName: nginx to &lt;strong&gt;rancher&lt;/strong&gt; ingress&lt;/p&gt;

&lt;p&gt;Test the kubectl exec and port-forward&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl create deployment nginx2 --image nginx:alpine
kubectl expose deployment nginx2 --port=80
kubectl exec -it nginx2-XXXXX -- sh
kubectl port-forward service/nginx2 --address 0.0.0.0 80:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>alb</category>
      <category>aws</category>
      <category>nginx</category>
      <category>kubernetes</category>
    </item>
  </channel>
</rss>
