<?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: sabbir alam</title>
    <description>The latest articles on DEV Community by sabbir alam (@sabbir_alam_aa9e289fbd684).</description>
    <link>https://dev.to/sabbir_alam_aa9e289fbd684</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%2F2173354%2F21f0c15a-ab34-45e7-bc59-d232733d95b6.jpg</url>
      <title>DEV Community: sabbir alam</title>
      <link>https://dev.to/sabbir_alam_aa9e289fbd684</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sabbir_alam_aa9e289fbd684"/>
    <language>en</language>
    <item>
      <title>Agentic Ai - Multi Agent development using Google ADK</title>
      <dc:creator>sabbir alam</dc:creator>
      <pubDate>Tue, 04 Nov 2025 07:30:38 +0000</pubDate>
      <link>https://dev.to/sabbir_alam_aa9e289fbd684/agentic-ai-multi-agent-development-using-google-adk-4m9j</link>
      <guid>https://dev.to/sabbir_alam_aa9e289fbd684/agentic-ai-multi-agent-development-using-google-adk-4m9j</guid>
      <description>&lt;h2&gt;
  
  
  Building a Multi-Agent System Using Google’s Agent Development Kit
&lt;/h2&gt;

&lt;p&gt;In modern AI system design, we’re moving away from monolithic, single-purpose models and toward &lt;strong&gt;modular, multi-agent architectures&lt;/strong&gt; — systems composed of specialized components that can reason, collaborate, and delegate.&lt;/p&gt;

&lt;p&gt;Recently, I explored this concept hands-on using &lt;strong&gt;Google’s latest Agent Development Kit&lt;/strong&gt;, and built a multi-agent architecture consisting of four agents — one &lt;strong&gt;Parent Agent&lt;/strong&gt; and three &lt;strong&gt;specialized sub-agents&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Recommendation Agent&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Search Agent&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Completion Agent&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each agent is equipped with its own tools for &lt;strong&gt;retrieving and updating data&lt;/strong&gt; in a connected database.&lt;/p&gt;

&lt;p&gt;In this post, I’ll walk through the concept, architecture, and data flow — using the analogy of an &lt;em&gt;intelligent control tower system&lt;/em&gt; to explain how this architecture functions in real-world scenarios.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Analogy: An Intelligent Control Tower
&lt;/h2&gt;

&lt;p&gt;Think of the &lt;strong&gt;Parent Agent&lt;/strong&gt; as the &lt;strong&gt;control tower&lt;/strong&gt; of an airport.&lt;br&gt;&lt;br&gt;
Every incoming request is like an aircraft asking for landing instructions.&lt;/p&gt;

&lt;p&gt;The tower (Parent Agent) doesn’t fly the planes itself — it &lt;strong&gt;analyzes the situation&lt;/strong&gt;, determines the &lt;strong&gt;type of aircraft&lt;/strong&gt;, and assigns it to the appropriate &lt;strong&gt;runway or terminal&lt;/strong&gt; (sub-agent).&lt;/p&gt;

&lt;p&gt;If an unexpected event occurs — say, the assigned runway can’t handle that aircraft — the control tower steps back in, reassesses, and redirects it appropriately.&lt;/p&gt;

&lt;p&gt;This analogy reflects how modular agent orchestration enables adaptive and context-aware decision-making.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;The system is composed of four main agents:&lt;/p&gt;

&lt;h3&gt;
  
  
  Parent Agent — The Orchestrator
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Acts as the &lt;strong&gt;entry point&lt;/strong&gt; for all incoming user requests.&lt;/li&gt;
&lt;li&gt;Performs &lt;strong&gt;intent classification&lt;/strong&gt; and &lt;strong&gt;task routing&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Delegates work to specialized sub-agents.&lt;/li&gt;
&lt;li&gt;Handles &lt;strong&gt;re-delegation&lt;/strong&gt; or &lt;strong&gt;fallback&lt;/strong&gt; responses when needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Parent Agent’s logic acts like a &lt;strong&gt;router combined with a context analyzer&lt;/strong&gt;, determining which sub-agent should take ownership of the request.&lt;/p&gt;




&lt;h3&gt;
  
  
  Recommendation Agent — Contextual Insights
&lt;/h3&gt;

&lt;p&gt;This agent specializes in generating &lt;strong&gt;context-based recommendations&lt;/strong&gt; — such as suggesting items, actions, or next steps.&lt;/p&gt;

&lt;p&gt;It uses internal reasoning plus a custom-built tool that retrieves relevant data from the database to make informed suggestions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example use cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Show me trending items.”&lt;/li&gt;
&lt;li&gt;“What are the top-rated restaurants nearby?”&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Search Agent — Information Retrieval
&lt;/h3&gt;

&lt;p&gt;This agent functions as a &lt;strong&gt;retrieval pipeline&lt;/strong&gt;, focusing purely on &lt;strong&gt;fetching structured data&lt;/strong&gt; with high precision, using database-integrated search tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example queries:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Find restaurant details by city.”&lt;/li&gt;
&lt;li&gt;“Get all entries with a 5-star rating.”&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Completion Agent — Task Executor
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Completion Agent&lt;/strong&gt; is responsible for executing &lt;strong&gt;create&lt;/strong&gt;, &lt;strong&gt;update&lt;/strong&gt;, and &lt;strong&gt;completion-based tasks&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Whenever a query involves action rather than retrieval (like inserting or updating data), the Parent Agent delegates it here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example tasks:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Add this restaurant to the database.”&lt;/li&gt;
&lt;li&gt;“Update the dish price.”&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Workflow: Intelligent Delegation
&lt;/h2&gt;

&lt;p&gt;Here’s how a request moves through the system:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Request Entry:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Every request enters via the &lt;strong&gt;Parent Agent&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Intent Classification:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The Parent Agent analyzes the intent using reasoning or classification logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Delegation:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
It assigns the task to the appropriate sub-agent — Recommendation, Search, or Completion.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Execution:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The selected sub-agent performs the task using its specific toolset.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Re-routing:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
If a sub-agent encounters a query outside its scope, it escalates the request back to the Parent Agent.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fallback:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
If no suitable agent exists, the Parent Agent provides a default response.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This process ensures &lt;strong&gt;autonomy, modularity, and fault tolerance&lt;/strong&gt;, enabling the system to adapt dynamically based on the context of the request.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tooling Layer: Data Access and Actions
&lt;/h2&gt;

&lt;p&gt;Each sub-agent has access to &lt;strong&gt;custom tools&lt;/strong&gt; that interact with the backend database.&lt;/p&gt;

&lt;p&gt;Common operations include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data retrieval (&lt;code&gt;get&lt;/code&gt;, &lt;code&gt;findByField&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;Data modification (&lt;code&gt;insert&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt;, &lt;code&gt;delete&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Validation and error handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tools enable agents to perform &lt;strong&gt;real operations&lt;/strong&gt;, not just generate text.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Add a new restaurant to the list.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Parent Agent identifies it as a task completion request → delegates to the &lt;strong&gt;Completion Agent&lt;/strong&gt; → which triggers the &lt;strong&gt;database insertion tool&lt;/strong&gt; → and returns a confirmation message.&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%2Fpoi6z1j1gulmvxg0qesk.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%2Fpoi6z1j1gulmvxg0qesk.png" alt="Flow Chart" width="800" height="658"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Multi-Agent Design?
&lt;/h2&gt;

&lt;p&gt;The multi-agent approach offers several engineering benefits:&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Modularity&lt;/strong&gt; — Each agent focuses on a specific domain, simplifying debugging and scaling.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Reusability&lt;/strong&gt; — Agents can be reused or extended for different applications.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Scalability&lt;/strong&gt; — New agents can be integrated without disturbing existing logic.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Context Isolation&lt;/strong&gt; — Each agent handles its domain-specific tasks, improving accuracy and reducing confusion.&lt;/p&gt;

&lt;p&gt;Compared to a single monolithic agent, this design allows for &lt;strong&gt;better adaptability, maintainability, and distributed reasoning&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;Implementing this architecture with &lt;strong&gt;Google’s Agent Development Kit&lt;/strong&gt; highlighted how &lt;strong&gt;structured delegation&lt;/strong&gt; transforms conversational AI into a &lt;strong&gt;cooperative, context-aware framework&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of one model doing everything, we now design &lt;strong&gt;ecosystems of intelligent agents&lt;/strong&gt; that collaborate effectively — much like distributed microservices in a backend architecture.&lt;/p&gt;

&lt;p&gt;The result is a system that’s:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Autonomous
&lt;/li&gt;
&lt;li&gt;Modular
&lt;/li&gt;
&lt;li&gt;Scalable
&lt;/li&gt;
&lt;li&gt;Context-aware
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;As AI continues to evolve, the distinction between “chatbots” and “autonomous systems” is fading.&lt;br&gt;&lt;br&gt;
The true challenge now lies in &lt;strong&gt;orchestrating intelligence&lt;/strong&gt; — designing agents that can reason, delegate, and cooperate efficiently.&lt;/p&gt;

&lt;p&gt;By combining modular architectures with tools like &lt;strong&gt;Google’s Agent Development Kit&lt;/strong&gt;, we’re moving toward the era of &lt;strong&gt;composable AI systems&lt;/strong&gt; — where intelligence is not centralized but &lt;strong&gt;distributed, dynamic, and domain-aware&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;#AI #MultiAgentSystems #AgentDevelopmentKit #GoogleDevelopers #LLMs #SoftwareArchitecture #AIEngineering #Automation #IntelligentSystems&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>google</category>
      <category>architecture</category>
      <category>ai</category>
    </item>
    <item>
      <title>How I Integrated Google Sign-In with Keycloak Token Exchange in Flutter — Step-by-Step 🚀</title>
      <dc:creator>sabbir alam</dc:creator>
      <pubDate>Sat, 09 Aug 2025 23:24:11 +0000</pubDate>
      <link>https://dev.to/sabbir_alam_aa9e289fbd684/how-i-integrated-google-sign-in-with-keycloak-token-exchange-in-flutter-step-by-step-193l</link>
      <guid>https://dev.to/sabbir_alam_aa9e289fbd684/how-i-integrated-google-sign-in-with-keycloak-token-exchange-in-flutter-step-by-step-193l</guid>
      <description>&lt;p&gt;Hey Dev Community! 👋&lt;/p&gt;

&lt;p&gt;I recently built a Flutter app that uses Google Sign-In for authentication and then exchanges the Google access token for a Keycloak token to secure backend APIs.&lt;/p&gt;

&lt;p&gt;I ran into some challenges during the token exchange process, so here’s a walkthrough of how I solved it — plus a snippet of my Flutter Google Sign-In code!&lt;/p&gt;

&lt;p&gt;The Challenge 🐞&lt;br&gt;
When I first tried exchanging the Google access token for a Keycloak token, Keycloak kept rejecting the token with errors like:&lt;br&gt;
&lt;code&gt;TOKEN_EXCHANGE_ERROR: subject_token validation failure&lt;/code&gt;&lt;br&gt;
and &lt;br&gt;
&lt;code&gt;invalid_request: Parameter 'subject_issuer' is not supported for standard token exchange&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;before any configuration a very important step is to start the keyclock server correctly&lt;/strong&gt; &lt;br&gt;
&lt;code&gt;kc.bat start-dev --hostname=&amp;lt;your-host-name&amp;gt; --features="preview,token-exchange,admin-fine-grained-authz:v1"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;rest all the configuration remains the same &lt;br&gt;
First you need to create google OAuth Credentials&lt;/p&gt;
&lt;h1&gt;
  
  
  How to Create Google OAuth Credentials (Client ID &amp;amp; Secret)
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Step 1: Go to Google Cloud Console
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Open your browser and visit:
&lt;a href="https://console.cloud.google.com/apis/credentials" rel="noopener noreferrer"&gt;https://console.cloud.google.com/apis/credentials&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Sign in with your Google account.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Step 2: Create or Select a Project
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Select an existing project from the top dropdown, &lt;strong&gt;or&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;New Project&lt;/strong&gt;, enter a project name, and create it.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Step 3: Enable the OAuth Consent Screen
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;In the left sidebar, click &lt;strong&gt;OAuth consent screen&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;External&lt;/strong&gt; (for apps used by any Google user) or &lt;strong&gt;Internal&lt;/strong&gt; (for G Suite users only).&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Fill out the required fields:

&lt;ul&gt;
&lt;li&gt;App name
&lt;/li&gt;
&lt;li&gt;User support email
&lt;/li&gt;
&lt;li&gt;Developer contact email
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Save and continue through scopes and test users (default settings usually work).&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save and Continue&lt;/strong&gt; until done.&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  Step 4: Create OAuth 2.0 Credentials
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Credentials&lt;/strong&gt; on the left menu.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create Credentials&lt;/strong&gt; &amp;gt; &lt;strong&gt;OAuth client ID&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Application type&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Web application&lt;/strong&gt; (if your app uses redirects or backend)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Other&lt;/strong&gt; (for standalone apps like Flutter)
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Enter a name for the client (e.g., "Flutter App Client").&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  Step 5: Configure Authorized Redirect URIs (for Web Application)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If you chose &lt;strong&gt;Web application&lt;/strong&gt;, add the redirect URI where Google will send users after authentication.
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  - For Keycloak, this is usually:  &lt;code&gt;https://&amp;lt;your-keycloak-domain&amp;gt;/realms/&amp;lt;realm-name&amp;gt;/broker/google/endpoint&lt;/code&gt;
&lt;/h2&gt;
&lt;h2&gt;
  
  
  Step 6: Save and Get Your Client ID and Secret
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Copy the &lt;strong&gt;Client ID&lt;/strong&gt; and &lt;strong&gt;Client Secret&lt;/strong&gt; displayed.&lt;/li&gt;
&lt;li&gt;Keep them safe to configure in Keycloak.&lt;/li&gt;
&lt;/ul&gt;


&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Go to Google Cloud Console&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Create/select a project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Configure OAuth consent screen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Create OAuth client credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Set redirect URIs if needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Save and copy Client ID &amp;amp; Secret&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  Part 2: Configure Google as Identity Provider in Keycloak
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1: Login to Keycloak Admin Console
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Open Keycloak Admin UI and select your realm.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Part 2: Configure Google Identity Provider in Keycloak
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Login to Keycloak Admin Console and Open Your Realm
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Open the Keycloak Admin UI.&lt;/li&gt;
&lt;li&gt;Select your realm.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Add Google as an Identity Provider
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Identity Providers&lt;/strong&gt; in the sidebar.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add provider&lt;/strong&gt; and select &lt;strong&gt;Google&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Enter your &lt;strong&gt;Client ID&lt;/strong&gt; and &lt;strong&gt;Client Secret&lt;/strong&gt; from Google.&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Advanced Settings&lt;/strong&gt;, enable &lt;strong&gt;Store Tokens&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Configure Your Client (&lt;code&gt;flutter-app&lt;/code&gt;) for Token Exchange
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Clients&lt;/strong&gt; and select your client (&lt;code&gt;flutter-app&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Enable the following settings:

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Standard Flow&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Direct Access Grants&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client Authentication&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Check &lt;strong&gt;Service Account Roles&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Enable and Configure Permissions (Crucial Step!)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ This is the crucial part of the whole process!&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Under &lt;strong&gt;Permissions&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If Permissions tab is not visible, start Keycloak with the command:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;  kc.bat start-dev &lt;span class="nt"&gt;--hostname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your-host-name&amp;gt; &lt;span class="nt"&gt;--features&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"preview,token-exchange,admin-fine-grained-authz:v1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Go to the &lt;strong&gt;Permissions&lt;/strong&gt; tab inside your client settings.&lt;/li&gt;
&lt;li&gt;Enable &lt;strong&gt;Permissions Enabled&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;token-exchange&lt;/strong&gt; permission.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create Client Policy&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Name the policy.&lt;/li&gt;
&lt;li&gt;Select the client (&lt;code&gt;flutter-app&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Set decision to &lt;strong&gt;Positive&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Go back to the &lt;strong&gt;token-exchange&lt;/strong&gt; permission, select the policy you just created, and save.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Configure Permissions for the Google Identity Provider
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to the &lt;strong&gt;Google Identity Provider&lt;/strong&gt; you added.&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Permissions&lt;/strong&gt;, enable &lt;strong&gt;Permissions Enabled&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;token-exchange&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select the same policy you created for the client.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Part 3: Token Exchange Request
&lt;/h2&gt;

&lt;p&gt;Make this request to exchange a Google access token for a Keycloak token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://&amp;lt;keycloak-server&amp;gt;/realms/&amp;lt;your-releam&amp;gt;/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&amp;amp;subject_token=&amp;lt;GOOGLE_ACCESS_TOKEN&amp;gt;
&amp;amp;subject_token_type=urn:ietf:params:oauth:token-type:access_token
&amp;amp;client_id=&amp;lt;your-client-id&amp;gt;
&amp;amp;client_secret=&amp;lt;CLIENT_SECRET&amp;gt;
&amp;amp;subject_issuer=google
&amp;amp;scope=openid profile email
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;subject_issuer&lt;/code&gt; is the Identity Provider’s name (usually &lt;code&gt;"google"&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Flutter Google Sign-In and Keycloak Token Exchange Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// auth_service.dart&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:google_sign_in/google_sign_in.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:dio/dio.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter_secure_storage/flutter_secure_storage.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuthService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;scopes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;[&lt;/span&gt;
    &lt;span class="s"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'openid'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'profile'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'https://www.googleapis.com/auth/userinfo.email'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'https://www.googleapis.com/auth/userinfo.profile'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;GoogleSignIn&lt;/span&gt; &lt;span class="n"&gt;_googleSignIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GoogleSignIn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;_initialized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Google SignIn Instance&lt;/span&gt;
  &lt;span class="c1"&gt;// final GoogleSignIn _googleSignIn = GoogleSignIn(&lt;/span&gt;
  &lt;span class="c1"&gt;//   scopes: ['email', 'profile', 'openid'],&lt;/span&gt;
  &lt;span class="c1"&gt;// );&lt;/span&gt;

  &lt;span class="c1"&gt;// Secure Storage&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;FlutterSecureStorage&lt;/span&gt; &lt;span class="n"&gt;_storage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;FlutterSecureStorage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Keycloak Configuration&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;keycloakUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="s"&gt;"https://knows-pride-future-bon.trycloudflare.com"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;realm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Medical"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;clientId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"flutter-app"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_initializeGoogle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_initialized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_googleSignIn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;clientId:&lt;/span&gt;
            &lt;span class="s"&gt;"&amp;lt;your-android-client-id&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;serverClientId:&lt;/span&gt;
            &lt;span class="s"&gt;"&amp;lt;your-web-client-id&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;_initialized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GoogleSign-In initialization failed: &lt;/span&gt;&lt;span class="si"&gt;$e&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;_initialized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;/// Step 1: Google Sign-In to get ID Token&lt;/span&gt;
  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;signInWithGoogle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_initializeGoogle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;GoogleSignInAccount&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_googleSignIn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&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="p"&gt;;&lt;/span&gt;
      &lt;span class="c1"&gt;// print(account);&lt;/span&gt;

      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;GoogleSignInClientAuthorization&lt;/span&gt; &lt;span class="n"&gt;authorization&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authorizationClient&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authorizeScopes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scopes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"access token &lt;/span&gt;&lt;span class="si"&gt;${authorization.accessToken}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;authorization&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- Return access token here&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Google Sign-In Error: &lt;/span&gt;&lt;span class="si"&gt;$e&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&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="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;/// Step 2: Exchange Google ID Token with Keycloak for Access &amp;amp; Refresh Tokens&lt;/span&gt;
  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;exchangeTokenWithKeycloak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;googleIdToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;dio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dio&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Exchanging token with Keycloak... &lt;/span&gt;&lt;span class="si"&gt;${keycloakUrl}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="si"&gt;$keycloakUrl&lt;/span&gt;&lt;span class="s"&gt;/realms/&lt;/span&gt;&lt;span class="si"&gt;$realm&lt;/span&gt;&lt;span class="s"&gt;/protocol/openid-connect/token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;data:&lt;/span&gt; &lt;span class="p"&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="n"&gt;clientId&lt;/span&gt;&lt;span class="p"&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="s"&gt;'&amp;lt;your-client-secret&amp;gt;'&lt;/span&gt;&lt;span class="p"&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="s"&gt;'urn:ietf:params:oauth:grant-type:token-exchange'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s"&gt;'subject_token'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;googleIdToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s"&gt;'subject_token_type'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'urn:ietf:params:oauth:token-type:access_token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s"&gt;'requested_token_type'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
              &lt;span class="s"&gt;'urn:ietf:params:oauth:token-type:access_token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s"&gt;'subject_issuer'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'google'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nl"&gt;options:&lt;/span&gt; &lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;contentType:&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;formUrlEncodedContentType&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;return&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;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Keycloak Token Exchange Error: &lt;/span&gt;&lt;span class="si"&gt;$e&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&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="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;/// Step 3: Store Tokens Securely&lt;/span&gt;
  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;storeTokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;key:&lt;/span&gt; &lt;span class="s"&gt;'access_token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;value:&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'access_token'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;key:&lt;/span&gt; &lt;span class="s"&gt;'refresh_token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;value:&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'refresh_token'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;/// Step 4: Get Dio Instance with Authorization Header&lt;/span&gt;
  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Dio&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getAuthorizedDio&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;dio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dio&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;final&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;await&lt;/span&gt; &lt;span class="n"&gt;_storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;key:&lt;/span&gt; &lt;span class="s"&gt;'access_token'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;dio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'Authorization'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'Bearer &lt;/span&gt;&lt;span class="si"&gt;$token&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dio&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Step 5: Complete Login Flow&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Step 1: Google Sign-In to get ID Token&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;googleIdToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;signInWithGoogle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;googleIdToken&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&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="p"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;keycloakTokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;exchangeTokenWithKeycloak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;googleIdToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keycloakTokens&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&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="p"&gt;;&lt;/span&gt;

      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;storeTokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keycloakTokens&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;keycloakTokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'access_token'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

      &lt;span class="c1"&gt;// Return Access Token&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;keycloakTokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'access_token'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Login Error: &lt;/span&gt;&lt;span class="si"&gt;$e&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&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="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;/// Step 6: Logout from Google and Clear Tokens&lt;/span&gt;
  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_googleSignIn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;signOut&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deleteAll&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Logged out successfully"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Happy coding! 🚀&lt;/p&gt;

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