{"id":2581,"date":"2025-08-05T10:20:30","date_gmt":"2025-08-05T10:20:30","guid":{"rendered":"https:\/\/www.devopsconsulting.in\/blog\/?p=2581"},"modified":"2025-08-05T12:18:34","modified_gmt":"2025-08-05T12:18:34","slug":"a-comprehensive-guide-to-implementing-single-sign-on-sso-for-your-application-suite","status":"publish","type":"post","link":"https:\/\/www.devopsconsulting.in\/blog\/a-comprehensive-guide-to-implementing-single-sign-on-sso-for-your-application-suite\/","title":{"rendered":"A Comprehensive Guide to Implementing Single Sign-On (SSO) for Your Application Suite"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\" id=\"sso-logic-and-architecture\">SSO Logic and Architecture<\/h2>\n\n\n\n<p>At its core, the SSO system works by centralizing the login process. Instead of each application managing its own user credentials, they all delegate authentication to a single, trusted <strong>Identity Provider (IdP)<\/strong>. Your applications (Dashboard, Trips, Events, Blog, Forum) become <strong>Service Providers (SPs)<\/strong> that trust the IdP.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Core logic<\/h2>\n\n\n\n<p><strong>Entities Involved:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>User-Agent:<\/strong> The user&#8217;s web browser.<\/li>\n\n\n\n<li><strong>SP-Forum:<\/strong> The Service Provider, your Flarum application at <code>HolidayLandmark.com\/forum<\/code>.<\/li>\n\n\n\n<li><strong>IdP:<\/strong> The central Identity Provider (your custom Laravel application).<\/li>\n\n\n\n<li><strong>SP-Trips:<\/strong> Another Service Provider, your <code>EventMie<\/code> application at <code>HolidayLandmark.com\/trips<\/code>.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Phase 1: Initial Login at SP-Forum<\/h3>\n\n\n\n<p><strong>Step 1: User Initiates Access to SP-Forum<\/strong><br>The user navigates their browser to <code>https:\/\/HolidayLandmark.com\/forum<\/code>.<\/p>\n\n\n\n<p><strong>Step 2: SP-Forum Checks for Local Session and Redirects<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>SP-Forum<\/strong> checks for a local session cookie to determine if the user is already authenticated with the forum application.<\/li>\n\n\n\n<li>Finding no active session, it determines the user must be authenticated.<\/li>\n\n\n\n<li><strong>SP-Forum<\/strong> constructs an <strong>OAuth 2.0 Authorization Request<\/strong> and redirects the user&#8217;s browser to the <strong>IdP&#8217;s<\/strong> <code>\/authorize<\/code> endpoint. This request includes parameters like <code>client_id<\/code> (identifying SP-Forum), <code>redirect_uri<\/code> (where to send the user back), <code>response_type=code<\/code>, and <code>scope<\/code>.<\/li>\n<\/ul>\n\n\n\n<p><strong>Step 3: IdP Authenticates the User<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The <strong>IdP<\/strong> receives the request. It checks for its <em>own<\/em> session cookie to see if the user is already authenticated with the IdP.<\/li>\n\n\n\n<li>Since this is the user&#8217;s first visit to any application in the ecosystem, there is no active IdP session.<\/li>\n\n\n\n<li>The <strong>IdP<\/strong> displays its login page, prompting the user for their username and password.<\/li>\n<\/ul>\n\n\n\n<p><strong>Step 4: User Submits Credentials<\/strong><br>The user enters their credentials and submits the form to the <strong>IdP<\/strong>.<\/p>\n\n\n\n<p><strong>Step 5: IdP Validates Credentials and Establishes Session<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The <strong>IdP<\/strong> validates the credentials against its user database.<\/li>\n\n\n\n<li>Upon successful validation, the <strong>IdP<\/strong> creates a central authentication session for the user and sets a session cookie in the user&#8217;s browser. This cookie is scoped to the IdP&#8217;s domain.<\/li>\n\n\n\n<li>The <strong>IdP<\/strong> generates a single-use <strong>authorization code<\/strong>.<\/li>\n<\/ul>\n\n\n\n<p><strong>Step 6: IdP Redirects Back to SP-Forum<\/strong><br>The <strong>IdP<\/strong> redirects the user&#8217;s browser back to the <code>redirect_uri<\/code> provided by <strong>SP-Forum<\/strong> in Step 2. The authorization code is included as a query parameter in the URL.<\/p>\n\n\n\n<p><strong>Step 7: SP-Forum Exchanges Code for Tokens<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The user&#8217;s browser loads the <code>redirect_uri<\/code> at <strong>SP-Forum<\/strong>.<\/li>\n\n\n\n<li><strong>SP-Forum&#8217;s<\/strong> backend receives the authorization code.<\/li>\n\n\n\n<li>It then makes a secure, back-channel (server-to-server) POST request to the <strong>IdP&#8217;s<\/strong> <code>\/token<\/code> endpoint. This request includes the <code>authorization_code<\/code>, its <code>client_id<\/code>, and <code>client_secret<\/code>.<\/li>\n<\/ul>\n\n\n\n<p><strong>Step 8: IdP Issues Access and ID Tokens<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The <strong>IdP<\/strong> validates the authorization code, <code>client_id<\/code>, and <code>client_secret<\/code>.<\/li>\n\n\n\n<li>If valid, it invalidates the authorization code (to prevent reuse) and returns an <strong>access token<\/strong> and optionally an <strong>ID token<\/strong> to <strong>SP-Forum<\/strong>.<\/li>\n<\/ul>\n\n\n\n<p><strong>Step 9: SP-Forum Fetches User Info and Creates Local Session<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>SP-Forum<\/strong> uses the received <strong>access token<\/strong> to make an API call to the <strong>IdP&#8217;s<\/strong> user info endpoint (e.g., <code>\/api\/user<\/code>).<\/li>\n\n\n\n<li>The <strong>IdP<\/strong> validates the access token and returns the user&#8217;s details (e.g., <code>user_id<\/code>, <code>email<\/code>, <code>name<\/code>).<\/li>\n\n\n\n<li><strong>SP-Forum<\/strong> checks its database for a user with the <code>user_id<\/code> from the IdP.\n<ul class=\"wp-block-list\">\n<li>If the user doesn&#8217;t exist, it creates a new user record in its <code>users<\/code> table (without a password).<\/li>\n\n\n\n<li>If the user exists, it proceeds.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>SP-Forum<\/strong> creates a local session for the user, setting its own session cookie.<\/li>\n<\/ul>\n\n\n\n<p><strong>Result of Phase 1:<\/strong> The user is now successfully logged into <code>HolidayLandmark.com\/forum<\/code>. The browser holds two important cookies: one for the <strong>SP-Forum<\/strong> session and one for the central <strong>IdP<\/strong> session.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Phase 2: Seamless Access to SP-Trips<\/h3>\n\n\n\n<p><strong>Step 10: User Navigates to SP-Trips<\/strong><br>The user opens <code>https:\/\/HolidayLandmark.com\/trips<\/code> in the same browser.<\/p>\n\n\n\n<p><strong>Step 11: SP-Trips Checks for Local Session and Redirects<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>SP-Trips<\/strong> checks for its own local session cookie and finds none.<\/li>\n\n\n\n<li>It constructs its own OAuth 2.0 Authorization Request and redirects the user&#8217;s browser to the <strong>IdP&#8217;s<\/strong> <code>\/authorize<\/code> endpoint.<\/li>\n<\/ul>\n\n\n\n<p><strong>Step 12: IdP Detects Active Session and Skips Login<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The <strong>IdP<\/strong> receives the request from <strong>SP-Trips<\/strong>.<\/li>\n\n\n\n<li>Crucially, it checks for the IdP session cookie set in Step 5. It finds an active session.<\/li>\n\n\n\n<li>Because the user is already authenticated with the IdP, <strong>it completely bypasses the login form<\/strong>.<\/li>\n\n\n\n<li>The <strong>IdP<\/strong> immediately generates a new authorization code for <strong>SP-Trips<\/strong> and redirects the user&#8217;s browser back to the <code>redirect_uri<\/code> specified by <strong>SP-Trips<\/strong>. This happens automatically and almost instantly.<\/li>\n<\/ul>\n\n\n\n<p><strong>Step 13-15: SP-Trips Completes the Login Flow<\/strong><br>The process now follows the same final steps as the first login, but for <strong>SP-Trips<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>SP-Trips<\/strong> receives the new authorization code.<\/li>\n\n\n\n<li>It exchanges the code for its own <strong>access token<\/strong> via a back-channel call to the IdP.<\/li>\n\n\n\n<li>It uses the token to fetch user info from the IdP.<\/li>\n\n\n\n<li>It creates a local user record (if one doesn&#8217;t exist) and establishes a local session for the user.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>The flow follows the OAuth 2.0 Authorization Code Grant protocol:<\/strong><\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Access Request<\/strong>: A user tries to access a protected page on an SP (e.g., the Flarum forum).<\/li>\n\n\n\n<li><strong>Redirect to IdP<\/strong>: The SP sees the user is not logged in and redirects them to the IdP&#8217;s login page, including a unique <code>client_id<\/code> and <code>redirect_uri<\/code>.<\/li>\n\n\n\n<li><strong>User Authentication<\/strong>: The user enters their single set of credentials on the IdP&#8217;s login page.<\/li>\n\n\n\n<li><strong>Authorization Grant<\/strong>: The IdP validates the credentials and redirects the user back to the SP&#8217;s specified <code>redirect_uri<\/code>, providing a temporary authorization code.<\/li>\n\n\n\n<li><strong>Token Exchange<\/strong>: The SP&#8217;s backend securely communicates with the IdP, exchanging the authorization code, its <code>client_id<\/code>, and its <code>client_secret<\/code> for a permanent <strong>access token<\/strong>.<\/li>\n\n\n\n<li><strong>User Info &amp; Login<\/strong>: The SP uses this access token to request the user&#8217;s information (like email and name) from the IdP&#8217;s API. Upon receiving the data, the SP creates a local session and logs the user in.<\/li>\n<\/ol>\n\n\n\n<p>If the user then visits a different SP, that SP will also redirect to the IdP, which, recognizing the existing authenticated session, will immediately send back a new authorization code without requiring the user to log in again.<\/p>\n\n\n\n<p><em>Note: You can visualize the process as a central hub (the IdP) with spokes connecting to each of your applications (the SPs). All user authentication traffic is routed through this hub.<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"part-1-building-the-central-identity-provider-idp\">Part 1: Building the Central Identity Provider (IdP) with Laravel Passport<\/h2>\n\n\n\n<p>This is the most critical component. It will be a standalone Laravel application responsible for managing all users and authentication.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1.1: Set Up the IdP Project<\/h2>\n\n\n\n<p>Create a new Laravel project. This will serve as your authentication server (e.g., <code>auth.holidaylandmark.com<\/code>).<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>laravel new holidaylandmark-idp<br>cd holidaylandmark-idp<br><\/code><\/pre>\n\n\n\n<p>Configure your <code>.env<\/code> file with your database details and <code>APP_URL<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1.2: Install and Configure Laravel Passport<\/h2>\n\n\n\n<p>Passport will transform your Laravel app into a full OAuth2 server.<\/p>\n\n\n\n<p><strong>1. Install via Composer:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>composer require laravel\/passport<br><\/code><\/pre>\n\n\n\n<p><strong>2. Run Migrations:<\/strong> This creates the necessary database tables for clients and tokens.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>php artisan migrate<br><\/code><\/pre>\n\n\n\n<p><strong>3. Run Passport Installer:<\/strong> This command creates encryption keys and default clients.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>php artisan passport:install<br><\/code><\/pre>\n\n\n\n<p><strong>4. Configure User Model:<\/strong> Add the <code>HasApiTokens<\/code> trait.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>File:<\/strong> <code>app\/Models\/User.php<\/code><\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-preformatted\">php<code>&lt;?php\nnamespace App\\Models;\n\nuse Laravel\\Passport\\HasApiTokens; <em>\/\/ &lt;-- Add this<\/em>\nuse Illuminate\\Foundation\\Auth\\User as Authenticatable;\nuse Illuminate\\Notifications\\Notifiable;\n\nclass User extends Authenticatable\n{\n    use HasApiTokens, Notifiable; <em>\/\/ &lt;-- And add this<\/em>\n    <em>\/\/ ... rest of the model<\/em>\n}\n<\/code><\/pre>\n\n\n\n<p><strong>5. Configure AuthServiceProvider:<\/strong> Register Passport&#8217;s routes.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>File:<\/strong> <code>app\/Providers\/AuthServiceProvider.php<\/code><\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-preformatted\">php<code>&lt;?php\nnamespace App\\Providers;\n\nuse Laravel\\Passport\\Passport; <em>\/\/ &lt;-- Add this<\/em>\nuse Illuminate\\Foundation\\Support\\Providers\\AuthServiceProvider as ServiceProvider;\n\nclass AuthServiceProvider extends ServiceProvider\n{\n    <em>\/\/ ...<\/em>\n    public function boot()\n    {\n        $this-&gt;registerPolicies();\n        Passport::routes(); <em>\/\/ &lt;-- Add this line<\/em>\n    }\n}\n<\/code><\/pre>\n\n\n\n<p><strong>6. Configure API Auth Guard:<\/strong> Tell Laravel to use Passport for API authentication.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>File:<\/strong> <code>config\/auth.php<\/code><\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-preformatted\">php<code>'guards' =&gt; [\n    <em>\/\/ ...<\/em>\n    'api' =&gt; [\n        'driver' =&gt; 'passport', <em>\/\/ &lt;-- Change 'token' to 'passport'<\/em>\n        'provider' =&gt; 'users',\n    ],\n],\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1.3: Create an API Route to Fetch User Data<\/h2>\n\n\n\n<p>Service providers will use this endpoint to get user details after getting an access token.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>File:<\/strong> <code>routes\/api.php<\/code><\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-preformatted\">php<code>&lt;?php\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Support\\Facades\\Route;\n\nRoute::middleware('auth:api')-&gt;get('\/user', function (Request $request) {\n    return $request-&gt;user();\n});\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1.4: Register Each Service Provider as an OAuth Client<\/h2>\n\n\n\n<p>For each of your applications, run this command in your IdP&#8217;s terminal. You will receive a <strong>Client ID<\/strong> and <strong>Client Secret<\/strong> for each one\u2014save these credentials securely.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code><em># For the Dashboard<\/em><br>php artisan passport:client --name=\"Dashboard\" --redirect_uri=\"https:\/\/holidaylandmark.com\/auth\/callback\"<br><br><em># For the Trips App<\/em><br>php artisan passport:client --name=\"Trips\" --redirect_uri=\"https:\/\/holidaylandmark.com\/trips\/auth\/callback\"<br><br><em># For the Events App<\/em><br>php artisan passport:client --name=\"Events\" --redirect_uri=\"https:\/\/holidaylandmark.com\/events\/auth\/callback\"<br><br><em># For the WordPress Blog (using custom code callback)<\/em><br>php artisan passport:client --name=\"Blog\" --redirect_uri=\"https:\/\/holidaylandmark.com\/blogs\/wp-login.php?action=hl_sso_callback\"<br><br><em># For the Flarum Forum (using custom code callback)<\/em><br>php artisan passport:client --name=\"Forum\" --redirect_uri=\"https:\/\/holidaylandmark.com\/forum\/auth\/holidaylandmark\"<br><\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"part-2-configuring-the-laravel-service-providers-d\">Part 2: Configuring the Laravel Service Providers (Dashboard, Trips, Events)<\/h2>\n\n\n\n<p>This section applies to your Dashboard, Trips, and Events applications. The steps are identical for each.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2.1: Install Socialite<\/h2>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>composer require laravel\/socialite<br><\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2.2: Add Credentials<\/h2>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>File:<\/strong> <code>config\/services.php<\/code><\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-preformatted\">php<code>'holidaylandmark_sso' =&gt; [\n    'client_id' =&gt; env('HL_SSO_CLIENT_ID'),\n    'client_secret' =&gt; env('HL_SSO_CLIENT_SECRET'),\n    'redirect' =&gt; env('HL_SSO_REDIRECT_URI'),\n],\n<\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>File:<\/strong> <code>.env<\/code> (Use the unique credentials for each SP)<\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-preformatted\">text<code>HL_SSO_CLIENT_ID=your-client-id-for-this-app\nHL_SSO_CLIENT_SECRET=your-client-secret-for-this-app\nHL_SSO_REDIRECT_URI=https:\/\/your-app-domain.com\/auth\/callback\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2.3: Create a Custom Socialite Provider<\/h2>\n\n\n\n<p>Since your IdP is not a standard one like Google, you must teach Socialite how to communicate with it.<\/p>\n\n\n\n<p><strong>1. Install the Socialite Providers Manager:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>composer require socialiteproviders\/manager<br><\/code><\/pre>\n\n\n\n<p><strong>2. Create the Provider Class:<\/strong> This class contains the IdP&#8217;s URLs.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>File:<\/strong> <code>app\/Services\/HolidayLandmarkSSOProvider.php<\/code> (Create the <code>Services<\/code> folder if needed).<\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-preformatted\">php<code>&lt;?php\nnamespace App\\Services;\nuse SocialiteProviders\\Manager\\OAuth2\\AbstractProvider;\nuse SocialiteProviders\\Manager\\OAuth2\\User;\n\nclass HolidayLandmarkSSOProvider extends AbstractProvider\n{\n    public const IDENTIFIER = 'HOLIDAYLANDMARK_SSO';\n\n    protected function getAuthUrl($state) {\n        return $this-&gt;buildAuthUrlFromBase('https:\/\/auth.holidaylandmark.com\/oauth\/authorize', $state);\n    }\n    protected function getTokenUrl() {\n        return 'https:\/\/auth.holidaylandmark.com\/oauth\/token';\n    }\n    protected function getUserByToken($token) {\n        $response = $this-&gt;getHttpClient()-&gt;get('https:\/\/auth.holidaylandmark.com\/api\/user', [\n            'headers' =&gt; ['Authorization' =&gt; 'Bearer ' . $token],\n        ]);\n        return json_decode($response-&gt;getBody(), true);\n    }\n    protected function mapUserToObject(array $user) {\n        return (new User())-&gt;setRaw($user)-&gt;map([\n            'id' =&gt; $user['id'], 'nickname' =&gt; $user['name'],\n            'name' =&gt; $user['name'], 'email' =&gt; $user['email'], 'avatar' =&gt; null,\n        ]);\n    }\n}\n<\/code><\/pre>\n\n\n\n<p><strong>3. Create the Event Listener:<\/strong> This tells Socialite to use your provider.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>File:<\/strong> <code>app\/Listeners\/HolidayLandmarkSSOExtendSocialite.php<\/code> (Create the <code>Listeners<\/code> folder).<\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-preformatted\">php<code>&lt;?php\nnamespace App\\Listeners;\nuse SocialiteProviders\\Manager\\SocialiteWasCalled;\n\nclass HolidayLandmarkSSOExtendSocialite {\n    public function handle(SocialiteWasCalled $socialiteWasCalled) {\n        $socialiteWasCalled-&gt;extendSocialite('holidaylandmark_sso', \\App\\Services\\HolidayLandmarkSSOProvider::class);\n    }\n}\n<\/code><\/pre>\n\n\n\n<p><strong>4. Register the Listener:<\/strong><\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>File:<\/strong> <code>app\/Providers\/EventServiceProvider.php<\/code><\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-preformatted\">php<code>protected $listen = [\n    \\SocialiteProviders\\Manager\\SocialiteWasCalled::class =&gt; [\n        \\App\\Listeners\\HolidayLandmarkSSOExtendSocialite::class.'@handle',\n    ],\n];\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2.4: Create the Login and Callback Routes<\/h2>\n\n\n\n<p>This handles the user-facing login flow.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>File:<\/strong> <code>routes\/web.php<\/code><\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-preformatted\">php<code>&lt;?php\nuse Illuminate\\Support\\Facades\\Route;\nuse Laravel\\Socialite\\Facades\\Socialite;\nuse App\\Models\\User;\nuse Illuminate\\Support\\Facades\\Auth;\n\n<em>\/\/ Redirects user to the IdP<\/em>\nRoute::get('\/login', function () {\n    return Socialite::driver('holidaylandmark_sso')-&gt;redirect();\n})-&gt;name('login');\n\n<em>\/\/ Handles the callback from the IdP<\/em>\nRoute::get('\/auth\/callback', function () {\n    $ssoUser = Socialite::driver('holidaylandmark_sso')-&gt;user();\n\n    <em>\/\/ Find or create a user in this application's database<\/em>\n    $user = User::updateOrCreate(\n        ['email' =&gt; $ssoUser-&gt;getEmail()],\n        [\n            'name' =&gt; $ssoUser-&gt;getName(),\n            'sso_id' =&gt; $ssoUser-&gt;getId(), <em>\/\/ A new column to store the master user ID from IdP<\/em>\n            'password' =&gt; Illuminate\\Support\\Facades\\Hash::make(Illuminate\\Support\\Str::random(24))\n        ]\n    );\n\n    <em>\/\/ Log the user into this application<\/em>\n    Auth::login($user, true); <em>\/\/ 'true' for \"remember me\"<\/em>\n\n    return redirect('\/dashboard'); <em>\/\/ Redirect to a protected page<\/em>\n});\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"part-3-configuring-the-wordpress-service-provider\">Part 3: Configuring the WordPress Service Provider (Code-Based)<\/h2>\n\n\n\n<p>Here&#8217;s how to create your own plugin for full control over the WordPress integration.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Create Plugin Directory<\/strong>: In your WordPress site, go to <code>wp-content\/plugins\/<\/code> and create a folder named <code>holidaylandmark-sso-client<\/code>.<\/li>\n\n\n\n<li><strong>Create Plugin File<\/strong>: Inside that folder, create <code>holidaylandmark-sso-client.php<\/code>.<\/li>\n\n\n\n<li><strong>Add the Code<\/strong>:<\/li>\n<\/ol>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>File:<\/strong> <code>wp-content\/plugins\/holidaylandmark-sso-client\/holidaylandmark-sso-client.php<\/code><\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-preformatted\">php<code>&lt;?php\n<em>\/**\n<\/em><em> * Plugin Name:       HolidayLandmark SSO Client\n<\/em><em> * Description:       A custom SSO client to connect to the HolidayLandmark Identity Provider.\n<\/em><em> *\/<\/em>\n\n<em>\/\/ --- Configuration: Replace with your actual credentials ---<\/em>\ndefine('HL_SSO_CLIENT_ID', 'your-wordpress-client-id');\ndefine('HL_SSO_CLIENT_SECRET', 'your-wordpress-client-secret');\ndefine('HL_SSO_REDIRECT_URI', site_url('\/wp-login.php?action=hl_sso_callback'));\ndefine('HL_IDP_AUTHORIZE_URL', 'https:\/\/auth.holidaylandmark.com\/oauth\/authorize');\ndefine('HL_IDP_TOKEN_URL', 'https:\/\/auth.holidaylandmark.com\/oauth\/token');\ndefine('HL_IDP_USERINFO_URL', 'https:\/\/auth.holidaylandmark.com\/api\/user');\n\n<em>\/\/ Adds the login button to the WordPress login form<\/em>\nadd_action('login_form', function () {\n    if (session_status() === PHP_SESSION_NONE) session_start();\n    $state = bin2hex(random_bytes(16));\n    $_SESSION['oauth2_state'] = $state;\n\n    $authorization_url = HL_IDP_AUTHORIZE_URL . '?' . http_build_query([\n        'client_id' =&gt; HL_SSO_CLIENT_ID, 'redirect_uri' =&gt; HL_SSO_REDIRECT_URI,\n        'response_type' =&gt; 'code', 'scope' =&gt; '', 'state' =&gt; $state,\n    ]);\n    echo '&lt;a href=\"' . esc_url($authorization_url) . '\" style=\"display:block; text-align:center; padding:10px; background-color:#3498db; color:#fff; text-decoration:none; margin:10px 0;\"&gt;Login with HolidayLandmark&lt;\/a&gt;';\n});\n\n<em>\/\/ Handles the callback from the IdP<\/em>\nadd_action('login_init', function () {\n    if (!isset($_GET['action']) || $_GET['action'] !== 'hl_sso_callback') return;\n    if (session_status() === PHP_SESSION_NONE) session_start();\n    if (empty($_GET['state']) || !isset($_SESSION['oauth2_state']) || $_GET['state'] !== $_SESSION['oauth2_state']) {\n        wp_die('Invalid state parameter.');\n    }\n    unset($_SESSION['oauth2_state']);\n\n    <em>\/\/ Exchange code for token<\/em>\n    $response = wp_remote_post(HL_IDP_TOKEN_URL, [\n        'body' =&gt; [ 'grant_type' =&gt; 'authorization_code', 'client_id' =&gt; HL_SSO_CLIENT_ID,\n                    'client_secret' =&gt; HL_SSO_CLIENT_SECRET, 'redirect_uri' =&gt; HL_SSO_REDIRECT_URI,\n                    'code' =&gt; sanitize_text_field($_GET['code']), ],\n    ]);\n    if (is_wp_error($response)) wp_die('Failed to get access token.');\n    $token_data = json_decode(wp_remote_retrieve_body($response), true);\n    if (empty($token_data['access_token'])) wp_die('Access token not found.');\n    \n    <em>\/\/ Fetch user info<\/em>\n    $user_info_response = wp_remote_get(HL_IDP_USERINFO_URL, ['headers' =&gt; ['Authorization' =&gt; 'Bearer ' . $token_data['access_token']]]);\n    if (is_wp_error($user_info_response)) wp_die('Failed to fetch user info.');\n    $user_info = json_decode(wp_remote_retrieve_body($user_info_response), true);\n    if (empty($user_info['email'])) wp_die('User email not found.');\n    \n    <em>\/\/ Find or create user and log them in<\/em>\n    $user = get_user_by('email', $user_info['email']);\n    if (!$user) {\n        $username = sanitize_user(explode('@', $user_info['email'])[0], true);\n        $base_username = $username; $i = 1;\n        while (username_exists($username)) $username = $base_username . $i++;\n        $user_id = wp_create_user($username, wp_generate_password(20), $user_info['email']);\n        wp_update_user(['ID' =&gt; $user_id, 'display_name' =&gt; sanitize_text_field($user_info['name'])]);\n        $user = get_user_by('id', $user_id);\n    }\n    \n    wp_set_current_user($user-&gt;ID, $user-&gt;user_login);\n    wp_set_auth_cookie($user-&gt;ID, true);\n    do_action('wp_login', $user-&gt;user_login, $user);\n    wp_redirect(admin_url());\n    exit;\n});\n<\/code><\/pre>\n\n\n\n<p><strong>4. Activate Plugin:<\/strong> In your WordPress admin panel, go to &#8220;Plugins&#8221; and activate the &#8220;HolidayLandmark SSO Client&#8221; plugin.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"part-4-configuring-the-flarum-service-provider-cod\">Part 4: Configuring the Flarum Service Provider (Code-Based)<\/h2>\n\n\n\n<p>This requires creating a Flarum extension. The process is more complex due to Flarum&#8217;s architecture.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Set up Extension Skeleton<\/strong>: Use <code>flarum-cli<\/code> or manually create the extension folder (e.g., <code>packages\/your-vendor\/flarum-ext-holidaylandmark-sso<\/code>) with <code>composer.json<\/code>, <code>extend.php<\/code>, and <code>js<\/code> directories.<\/li>\n\n\n\n<li><strong>Add Backend Logic<\/strong>: This code handles the entire OAuth2 flow.<\/li>\n<\/ol>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>File:<\/strong> <code>extend.php<\/code> (in your extension&#8217;s root)<\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-preformatted\">php<code>&lt;?php\nnamespace YourVendor\\FlarumHolidayLandmarkSSO;\nuse Flarum\\Extend;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse GuzzleHttp\\Client;\nuse Flarum\\Http\\Rememberer;\nuse Flarum\\Http\\SessionAuthenticator;\nuse Flarum\\User\\User;\nuse Laminas\\Diactoros\\Response\\RedirectResponse;\n\nreturn [\n    (new Extend\\Routes('forum'))\n        -&gt;get('\/auth\/holidaylandmark', 'auth.holidaylandmark', function (ServerRequestInterface $request, SessionAuthenticator $authenticator, Rememberer $rememberer) {\n            <em>\/\/ --- Config (Store these in Flarum's settings DB or config.php) ---<\/em>\n            $idp_authorize_url = 'https:\/\/auth.holidaylandmark.com\/oauth\/authorize';\n            $idp_token_url = 'https:\/\/auth.holidaylandmark.com\/oauth\/token';\n            $idp_user_url = 'https:\/\/auth.holidaylandmark.com\/api\/user';\n            $client_id = 'your-flarum-client-id';\n            $client_secret = 'your-flarum-client-secret';\n            $redirect_uri = app('flarum.config')['url'] . '\/auth\/holidaylandmark';\n            \n            $session = $request-&gt;getAttribute('session');\n            $queryParams = $request-&gt;getQueryParams();\n\n            if (!isset($queryParams['code'])) { <em>\/\/ Step 1: Redirect to IdP<\/em>\n                $session-&gt;put('oauth2state', ($state = \\Illuminate\\Support\\Str::random(40)));\n                $query = http_build_query(['client_id' =&gt; $client_id, 'redirect_uri' =&gt; $redirect_uri,\n                    'response_type' =&gt; 'code', 'scope' =&gt; '', 'state' =&gt; $state]);\n                return new RedirectResponse($idp_authorize_url . '?' . $query);\n            }\n            \n            <em>\/\/ Step 2: Handle Callback<\/em>\n            if (empty($queryParams['state']) || $queryParams['state'] !== $session-&gt;get('oauth2state')) throw new \\Exception('Invalid state');\n            \n            $httpClient = new Client();\n            $response = $httpClient-&gt;post($idp_token_url, [ 'form_params' =&gt; [ 'grant_type' =&gt; 'authorization_code',\n                'client_id' =&gt; $client_id, 'client_secret' =&gt; $client_secret, 'redirect_uri' =&gt; $redirect_uri,\n                'code' =&gt; $queryParams['code'] ]]);\n            $token = json_decode((string) $response-&gt;getBody(), true)['access_token'];\n\n            $response = $httpClient-&gt;get($idp_user_url, ['headers' =&gt; ['Authorization' =&gt; 'Bearer ' . $token]]);\n            $ssoUser = json_decode((string) $response-&gt;getBody(), true);\n            \n            $user = User::where('email', $ssoUser['email'])-&gt;first();\n            if (!$user) {\n                $user = User::register(['username' =&gt; $ssoUser['name'], 'email' =&gt; $ssoUser['email'], 'password' =&gt; \\Illuminate\\Support\\Str::random(20)]);\n                $user-&gt;activate(); $user-&gt;save();\n            }\n            \n            $authenticator-&gt;logIn($session, $user-&gt;id);\n            return $rememberer-&gt;remember(new RedirectResponse(app('flarum.config')['url']));\n        }),\n];\n<\/code><\/pre>\n\n\n\n<p><strong>3. Add Frontend Login Button<\/strong>:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>File:<\/strong> <code>js\/src\/forum\/index.js<\/code><\/p>\n<\/blockquote>\n\n\n\n<pre class=\"wp-block-preformatted\">javascript<code>import { extend } from 'flarum\/common\/extend';\nimport LogInButtons from 'flarum\/forum\/components\/LogInButtons';\nimport Button from 'flarum\/common\/components\/Button';\n\napp.initializers.add('your-vendor-holidaylandmark-sso', () =&gt; {\n    extend(LogInButtons.prototype, 'items', function (items) {\n        items.add('holidaylandmark',\n            &lt;Button className=\"Button LogInButton--holidaylandmark\"\n                icon=\"fas fa-id-card\" path=\"\/auth\/holidaylandmark\"&gt;\n                Login with HolidayLandmark\n            &lt;\/Button&gt;\n        );\n    });\n});\n<\/code><\/pre>\n\n\n\n<p><strong>4. Build and Activate<\/strong>: You must compile the JavaScript and then enable the extension in the Flarum admin panel.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Strategy: Central Roles, Local Mapping<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Define a Superset of Roles in the IdP<\/strong>: In your Laravel IdP, you will define roles that are descriptive of their function across the entire ecosystem. This creates a single source of truth for a user&#8217;s permissions. Instead of creating roles like\u00a0<code>blog_editor<\/code>\u00a0or\u00a0<code>events_organizer<\/code>, you create more generic roles.For your specific case, you could define these roles in your IdP:\n<ul class=\"wp-block-list\">\n<li><code>Super Admin<\/code>: Has administrative rights everywhere.<\/li>\n\n\n\n<li><code>Content Editor<\/code>: Can create and manage content (maps to Editor in WordPress, could be a Moderator in Flarum).<\/li>\n\n\n\n<li><code>Event Organizer<\/code>: Specific to the Events app.<\/li>\n\n\n\n<li><code>General User<\/code>: The default role for all basic users.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Pass Roles from IdP to SPs<\/strong>: As established in the previous answer, you must modify your IdP&#8217;s\u00a0<code>\/api\/user<\/code>\u00a0endpoint to include an array of the user&#8217;s assigned roles in the JSON response.<a href=\"https:\/\/www.devopsconsulting.in\/blog\/a-comprehensive-guide-to-implementing-single-sign-on-sso-for-your-application-suite\/\" target=\"_blank\" rel=\"noreferrer noopener\"><\/a><\/li>\n\n\n\n<li><strong>Implement Mapping Logic in Each SP<\/strong>: This is the most important step. Each application&#8217;s SSO callback handler will contain logic to map the IdP&#8217;s roles to its own internal roles.<\/li>\n<\/ol>\n\n\n\n<p>Here\u2019s how you would implement this for each of your applications:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1. Events App (<code>holidaylandmark.com\/events<\/code>)<\/h2>\n\n\n\n<p>This Laravel application has the roles&nbsp;<code>user<\/code>,&nbsp;<code>organizer<\/code>, and&nbsp;<code>admin<\/code>. You will map the IdP roles to these local roles within your&nbsp;<code>\/auth\/callback<\/code>&nbsp;route.<\/p>\n\n\n\n<p>Let&#8217;s assume your&nbsp;<code>users<\/code>&nbsp;table has a&nbsp;<code>role<\/code>&nbsp;column.<\/p>\n\n\n\n<p><strong>File:&nbsp;<code>routes\/web.php<\/code>&nbsp;(in your Events app)<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">php<code><em>\/\/ ...<\/em>\nRoute::get('\/auth\/callback', function () {\n    $ssoUser = Socialite::driver('holidaylandmark_sso')-&gt;user();\n\n    <em>\/\/ Get roles from the IdP.<\/em>\n    <em>\/\/ The key 'roles' depends on your IdP's API response structure.<\/em>\n    $idpRoles = array_column($ssoUser-&gt;getRaw()['roles'] ?? [], 'name');\n\n    <em>\/\/ --- Role Mapping Logic ---<\/em>\n    $localRole = 'user'; <em>\/\/ Default role<\/em>\n\n    if (in_array('Super Admin', $idpRoles)) {\n        $localRole = 'admin';\n    } elseif (in_array('Event Organizer', $idpRoles)) {\n        $localRole = 'organizer';\n    }\n    <em>\/\/ --- End Mapping Logic ---<\/em>\n\n    <em>\/\/ Find or create the user and assign the mapped role<\/em>\n    $user = User::updateOrCreate(\n        ['email' =&gt; $ssoUser-&gt;getEmail()],\n        [\n            'name' =&gt; $ssoUser-&gt;getName(),\n            'sso_id' =&gt; $ssoUser-&gt;getId(),\n            'password' =&gt; Illuminate\\Support\\Facades\\Hash::make(Illuminate\\Support\\Str::random(24)),\n            'role' =&gt; $localRole <em>\/\/ Assign the determined local role<\/em>\n        ]\n    );\n\n    Auth::login($user, true);\n    return redirect('\/dashboard');\n});\n<\/code><\/pre>\n\n\n\n<p><strong>Result<\/strong>: If a user logs in with the&nbsp;<code>Super Admin<\/code>&nbsp;role from the IdP, they are assigned the&nbsp;<code>admin<\/code>&nbsp;role in the Events app. If they have the&nbsp;<code>Event Organizer<\/code>&nbsp;role, they become an&nbsp;<code>organizer<\/code>. Otherwise, they are a regular&nbsp;<code>user<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. WordPress Blog (<code>holidaylandmark.com\/blog<\/code>)<\/h2>\n\n\n\n<p>Your WordPress blog has the roles&nbsp;<code>user<\/code>&nbsp;(Subscriber),&nbsp;<code>editor<\/code>, and&nbsp;<code>admin<\/code>&nbsp;(Administrator). You will implement the mapping in your custom SSO plugin.<\/p>\n\n\n\n<p><strong>File:&nbsp;<code>wp-content\/plugins\/holidaylandmark-sso-client\/holidaylandmark-sso-client.php<\/code><\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">php<code><em>\/\/ ... inside the add_action('login_init', ...) function, after you fetch user_info<\/em>\n\n$user_info = json_decode(wp_remote_retrieve_body($user_info_response), true);\n$idpRoles = array_column($user_info['roles'] ?? [], 'name');\n\n<em>\/\/ --- Role Mapping Logic ---<\/em>\n$role_mapping = [\n    'Super Admin' =&gt; 'administrator',\n    'Content Editor' =&gt; 'editor',\n    'General User' =&gt; 'subscriber'\n];\n\n$wp_role = 'subscriber'; <em>\/\/ Default role<\/em>\n\n<em>\/\/ Find the highest-priority role the user has that exists in our mapping<\/em>\nif (in_array('Super Admin', $idpRoles)) {\n    $wp_role = $role_mapping['Super Admin'];\n} elseif (in_array('Content Editor', $idpRoles)) {\n    $wp_role = $role_mapping['Content Editor'];\n} elseif (in_array('General User', $idpRoles)) {\n    $wp_role = $role_mapping['General User'];\n}\n<em>\/\/ --- End Mapping Logic ---<\/em>\n\n$user = get_user_by('email', $user_info['email']);\nif (!$user) {\n    <em>\/\/ (user creation logic from the guide)<\/em>\n    $user_id = wp_create_user(...);\n    $user = get_user_by('id', $user_id);\n}\n\n<em>\/\/ Update the user's role every time they log in<\/em>\nwp_update_user([\n    'ID' =&gt; $user-&gt;ID,\n    'role' =&gt; $wp_role\n]);\n\n<em>\/\/ ... rest of the login logic<\/em>\n<\/code><\/pre>\n\n\n\n<p><strong>Result<\/strong>: A user with the IdP role&nbsp;<code>Content Editor<\/code>&nbsp;is granted the&nbsp;<code>editor<\/code>&nbsp;role in WordPress, and&nbsp;<code>Super Admin<\/code>&nbsp;gets&nbsp;<code>administrator<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3. Flarum Forum (<code>holidaylandmark.com\/forum<\/code>)<\/h2>\n\n\n\n<p>Flarum uses&nbsp;<strong>Groups<\/strong>&nbsp;for permissions. Let&#8217;s assume you&#8217;ve created groups in your Flarum admin panel named&nbsp;<code>Administrators<\/code>,&nbsp;<code>Moderators<\/code>, and the default&nbsp;<code>Members<\/code>.<\/p>\n\n\n\n<p><strong>File:&nbsp;<code>extend.php<\/code>&nbsp;(in your Flarum SSO extension)<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">php<code><em>\/\/ ... inside the -&gt;get('\/auth\/holidaylandmark', ...) route, after you fetch $ssoUser<\/em>\n\n$ssoUser = json_decode((string) $response-&gt;getBody(), true);\n$idpRoles = array_column($ssoUser['roles'] ?? [], 'name');\n\n$user = User::where('email', $ssoUser['email'])-&gt;first();\nif (!$user) {\n    <em>\/\/ (user registration logic)<\/em>\n}\n\n<em>\/\/ --- Role Mapping Logic ---<\/em>\n$groupMapping = [\n    'Super Admin' =&gt; 'Administrators',\n    'Content Editor' =&gt; 'Moderators'\n    <em>\/\/ 'General User' maps to the default 'Members' group, which is automatic for new users<\/em>\n];\n\n<em>\/\/ Find the IDs of the Flarum groups to assign<\/em>\n$groupNamesToAssign = [];\nforeach ($idpRoles as $role) {\n    if (isset($groupMapping[$role])) {\n        $groupNamesToAssign[] = $groupMapping[$role];\n    }\n}\n\n$groupIds = \\Flarum\\Group\\Group::whereIn('name_singular', $groupNamesToAssign)-&gt;pluck('id')-&gt;all();\n\n<em>\/\/ Always include the default Member group (ID 3)<\/em>\nif (!in_array(3, $groupIds)) {\n    $groupIds[] = 3;\n}\n\n<em>\/\/ Sync the user's groups based on their IdP roles<\/em>\n$user-&gt;groups()-&gt;sync($groupIds);\n$user-&gt;save();\n<em>\/\/ --- End Mapping Logic ---<\/em>\n\n$authenticator-&gt;logIn($session, $user-&gt;id);\nreturn $rememberer-&gt;remember(new RedirectResponse(app('flarum.config')['url']));\n<\/code><\/pre>\n\n\n\n<p><strong>Result<\/strong>: A user with the&nbsp;<code>Content Editor<\/code>&nbsp;role in the IdP will be added to the&nbsp;<code>Moderators<\/code>&nbsp;group in Flarum upon login, giving them the specific permissions you have configured for that group.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>SSO Logic and Architecture At its core, the SSO system works by centralizing the login process. Instead of each application [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-2581","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v25.7 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>A Comprehensive Guide to Implementing Single Sign-On (SSO) for Your Application Suite - DevOps Consulting<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.devopsconsulting.in\/blog\/a-comprehensive-guide-to-implementing-single-sign-on-sso-for-your-application-suite\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"A Comprehensive Guide to Implementing Single Sign-On (SSO) for Your Application Suite - DevOps Consulting\" \/>\n<meta property=\"og:description\" content=\"SSO Logic and Architecture At its core, the SSO system works by centralizing the login process. Instead of each application [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.devopsconsulting.in\/blog\/a-comprehensive-guide-to-implementing-single-sign-on-sso-for-your-application-suite\/\" \/>\n<meta property=\"og:site_name\" content=\"DevOps Consulting\" \/>\n<meta property=\"article:published_time\" content=\"2025-08-05T10:20:30+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-08-05T12:18:34+00:00\" \/>\n<meta name=\"author\" content=\"Abhishek Singh\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Abhishek Singh\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.devopsconsulting.in\/blog\/a-comprehensive-guide-to-implementing-single-sign-on-sso-for-your-application-suite\/\",\"url\":\"https:\/\/www.devopsconsulting.in\/blog\/a-comprehensive-guide-to-implementing-single-sign-on-sso-for-your-application-suite\/\",\"name\":\"A Comprehensive Guide to Implementing Single Sign-On (SSO) for Your Application Suite - DevOps Consulting\",\"isPartOf\":{\"@id\":\"https:\/\/www.devopsconsulting.in\/blog\/#website\"},\"datePublished\":\"2025-08-05T10:20:30+00:00\",\"dateModified\":\"2025-08-05T12:18:34+00:00\",\"author\":{\"@id\":\"https:\/\/www.devopsconsulting.in\/blog\/#\/schema\/person\/fc397ba8be42f9fdd53450edfc73006f\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.devopsconsulting.in\/blog\/a-comprehensive-guide-to-implementing-single-sign-on-sso-for-your-application-suite\/\"]}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.devopsconsulting.in\/blog\/#website\",\"url\":\"https:\/\/www.devopsconsulting.in\/blog\/\",\"name\":\"DevOps Consulting\",\"description\":\"DevOps Consulting | SRE Consulting | DevSecOps Consulting | MLOps Consulting\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.devopsconsulting.in\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.devopsconsulting.in\/blog\/#\/schema\/person\/fc397ba8be42f9fdd53450edfc73006f\",\"name\":\"Abhishek Singh\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.devopsconsulting.in\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/790feefe779852cdf344ca7318bf6c13832223c9b3c6bf4d217658412041026d?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/790feefe779852cdf344ca7318bf6c13832223c9b3c6bf4d217658412041026d?s=96&d=mm&r=g\",\"caption\":\"Abhishek Singh\"},\"description\":\"I\u2019m Abhishek, a DevOps, SRE, DevSecOps, and Cloud expert with a passion for sharing knowledge and real-world experiences. I\u2019ve had the opportunity to work with Cotocus and continue to contribute to multiple platforms where I share insights across different domains: \u2022 DevOps School \u2013 Tech blogs and tutorials \u2022 Holiday Landmark \u2013 Travel stories and guides \u2022 Stocks Mantra \u2013 Stock market strategies and tips \u2022 My Medic Plus \u2013 Health and fitness guidance \u2022 TrueReviewNow \u2013 Honest product reviews \u2022 Wizbrand \u2013 SEO and digital tools for businesses I\u2019m also exploring the fascinating world of Quantum Computing.\",\"url\":\"https:\/\/www.devopsconsulting.in\/blog\/author\/abhishek\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"A Comprehensive Guide to Implementing Single Sign-On (SSO) for Your Application Suite - DevOps Consulting","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.devopsconsulting.in\/blog\/a-comprehensive-guide-to-implementing-single-sign-on-sso-for-your-application-suite\/","og_locale":"en_US","og_type":"article","og_title":"A Comprehensive Guide to Implementing Single Sign-On (SSO) for Your Application Suite - DevOps Consulting","og_description":"SSO Logic and Architecture At its core, the SSO system works by centralizing the login process. Instead of each application [&hellip;]","og_url":"https:\/\/www.devopsconsulting.in\/blog\/a-comprehensive-guide-to-implementing-single-sign-on-sso-for-your-application-suite\/","og_site_name":"DevOps Consulting","article_published_time":"2025-08-05T10:20:30+00:00","article_modified_time":"2025-08-05T12:18:34+00:00","author":"Abhishek Singh","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Abhishek Singh","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.devopsconsulting.in\/blog\/a-comprehensive-guide-to-implementing-single-sign-on-sso-for-your-application-suite\/","url":"https:\/\/www.devopsconsulting.in\/blog\/a-comprehensive-guide-to-implementing-single-sign-on-sso-for-your-application-suite\/","name":"A Comprehensive Guide to Implementing Single Sign-On (SSO) for Your Application Suite - DevOps Consulting","isPartOf":{"@id":"https:\/\/www.devopsconsulting.in\/blog\/#website"},"datePublished":"2025-08-05T10:20:30+00:00","dateModified":"2025-08-05T12:18:34+00:00","author":{"@id":"https:\/\/www.devopsconsulting.in\/blog\/#\/schema\/person\/fc397ba8be42f9fdd53450edfc73006f"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.devopsconsulting.in\/blog\/a-comprehensive-guide-to-implementing-single-sign-on-sso-for-your-application-suite\/"]}]},{"@type":"WebSite","@id":"https:\/\/www.devopsconsulting.in\/blog\/#website","url":"https:\/\/www.devopsconsulting.in\/blog\/","name":"DevOps Consulting","description":"DevOps Consulting | SRE Consulting | DevSecOps Consulting | MLOps Consulting","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.devopsconsulting.in\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.devopsconsulting.in\/blog\/#\/schema\/person\/fc397ba8be42f9fdd53450edfc73006f","name":"Abhishek Singh","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.devopsconsulting.in\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/790feefe779852cdf344ca7318bf6c13832223c9b3c6bf4d217658412041026d?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/790feefe779852cdf344ca7318bf6c13832223c9b3c6bf4d217658412041026d?s=96&d=mm&r=g","caption":"Abhishek Singh"},"description":"I\u2019m Abhishek, a DevOps, SRE, DevSecOps, and Cloud expert with a passion for sharing knowledge and real-world experiences. I\u2019ve had the opportunity to work with Cotocus and continue to contribute to multiple platforms where I share insights across different domains: \u2022 DevOps School \u2013 Tech blogs and tutorials \u2022 Holiday Landmark \u2013 Travel stories and guides \u2022 Stocks Mantra \u2013 Stock market strategies and tips \u2022 My Medic Plus \u2013 Health and fitness guidance \u2022 TrueReviewNow \u2013 Honest product reviews \u2022 Wizbrand \u2013 SEO and digital tools for businesses I\u2019m also exploring the fascinating world of Quantum Computing.","url":"https:\/\/www.devopsconsulting.in\/blog\/author\/abhishek\/"}]}},"_links":{"self":[{"href":"https:\/\/www.devopsconsulting.in\/blog\/wp-json\/wp\/v2\/posts\/2581","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsconsulting.in\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsconsulting.in\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsconsulting.in\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsconsulting.in\/blog\/wp-json\/wp\/v2\/comments?post=2581"}],"version-history":[{"count":4,"href":"https:\/\/www.devopsconsulting.in\/blog\/wp-json\/wp\/v2\/posts\/2581\/revisions"}],"predecessor-version":[{"id":2586,"href":"https:\/\/www.devopsconsulting.in\/blog\/wp-json\/wp\/v2\/posts\/2581\/revisions\/2586"}],"wp:attachment":[{"href":"https:\/\/www.devopsconsulting.in\/blog\/wp-json\/wp\/v2\/media?parent=2581"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsconsulting.in\/blog\/wp-json\/wp\/v2\/categories?post=2581"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsconsulting.in\/blog\/wp-json\/wp\/v2\/tags?post=2581"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}