Pages

Saturday, 4 January 2025

Detailed steps to implement SSO with multiple Identity Providers by Apache httpd + mod_auth_openidc

 0 Lab inventory

  • RockyLinux 9
  • httpd-2.4
  • php-fpm-8
  • mod_auth_openidc-2.4.16 ( the yum installed version, 2.4.10 has issues )
A web app has been registered at both Google accounts and Entra ID. As my app is only for testing, it's completely free of any charges from both google and Entra.

1 Installation

yum install httpd
yum install php-fpm
yum install mod_auth_openidc

The mod_auth_openidc installed by "yum" is 2.4.10, which has some bugs reported. The reason I still installed via yum is because I want the configuration files installed by this package.

To solve the bugs, I compiled from source and replaced the binary /usr/lib64/httpd/modules/mod_auth_openidc.so. 

The compiling process is skipped here. Just one note, some dependencies are in the crb repo, e.g to install jansson-devel, run 

dnf --enablerepo=crb install jansson-devel

Reading the INSTALL file could lead to a successful compilation.

2 Authn process quick look

Before detailed configuration, let's have a look at the login process from a high level.
  1. End user access https://xxx/protected/index.php without existing OIDC session.
  2. mod_auth_openidc checks OIDC session and redirect end user to OP discovery URL https://xxx/login.php
  3. End user select an OP which links to https://xxx/protected/redirect_uri?iss=<the-op-selected>&target_link_uri=https://xxx/protected/index.php..
  4. OP login process starts
  5. OP login process ends, redirect end user to https://xxx/protected/redirect_uri with authn response (code).
  6. mod_auth_openidc gets token/userinfo from OP, creates OIDC session based on the authn response.
  7. mod_auth_openidc redirect end user to https://xxx/protected/index.php, this time with OIDC session.
The mod_auth_openidc has two important endpoints. 

One is the RP redirect URI, which is telling the OP where to send back the authentication response. It's configured by OIDCRedirectURI directive.

The other is the URL that starts the OIDC authn request.  The interesting / confusing part is for mod_auth_openidc, this URL is the same as OIDCRedirectURI.

In our case, both of them are the same, https://home.linuxexam.net/protected/redirect_uri.

3 Configuration

The main configuration file is /etc/httpd/conf.d/auth_openidc.conf

OIDCRedirectURI https://home.linuxexam.net/protected/redirect_uri

OIDCCryptoPassphrase test1234

OIDCMetadataDir /var/cache/httpd/mod_auth_openidc/metadata

OIDCScope "openid email profile"

OIDCDiscoverURL https://home.linuxexam.net/login.php

<Location /protected/>

        AuthType openid-connect

        Require valid-user

</Location>


3.1 Metadata preparation

As configured above, both OP and RP metadata are saved under folder /var/cache/httpd/mod_auth_openidc/metadata.

Google Account's OIDC OP configuration is published at https://accounts.google.com/.well-known/openid-configuration. 

EntraID has different OIDC OP configuration publishing URL for different tenant. For my case, it's https://login.microsoftonline.com/7cd9de5d-9c0c-41e8-8d22-c73ec02a9b14/v2.0/.well-known/openid-configuration.

The RP's metadata is pretty simple, it's just a json file containing client_id and client_secret.

The mod_auth_openidc requires the OP's metadata file ending with ".provider" and the RP's metadata file ending with ".client". As a result, I have

accounts.google.com.client

accounts.google.com.provider

login.microsoftonline.com%2F7cd9de5d-9c0c-41e8-8d22-c73ec02a9b14%2Fv2.0.client

login.microsoftonline.com%2F7cd9de5d-9c0c-41e8-8d22-c73ec02a9b14%2Fv2.0.provider


3.2 OP Discovery page

As configured in auth_openidc.conf, the OP discovery page is at https://home.linuxexam.net/login.php.

Make sure this URL is NOT protected by this module.

<!doctype html>

<html>

    <head>

        <style>

            ul {

                display: flex;

                flex-direction: column;

                list-style: none;

            }

            #oplist a:link, a:visited {

                    background-color: #f44336;

                    color: white;

                    padding: 14px 25px;

                    text-align: center;

                    text-decoration: none;

                    display: inline-block;

          }

                #oplist a:hover, a:active {

                    background-color: red;

                }       

        </style>

    </head>

    <body>

<pre>

        <?php 

                print_r($_GET);

        ?>

</pre>

        <ul id="oplist">

<?php

function get_op_name($issuer) {

   if($issuer == "https://accounts.google.com"){

                return "Google";

   }

   if($issuer == "https://login.microsoftonline.com/7cd9de5d-9c0c-41e8-8d22-c73ec02a9b14/v2.0"){

                return "EntraID (LinuxExam.net)";

     }

   return $issuer;

}


$oidc_callback = $_GET['oidc_callback'];

$metadata_dir = '/var/cache/httpd/mod_auth_openidc/metadata/';


if ($handle = opendir($metadata_dir)) {

        while (false !== ($entry = readdir($handle))) {

                $type = ".provider";

                $len = strlen($type);

                if (substr($entry, -$len) !== $type) continue;

                $json = json_decode(file_get_contents($metadata_dir . $entry));

                echo '<p><a href="' . htmlspecialchars($oidc_callback . "?iss=" . urlencode($json->issuer) . "&" . $_SERVER['QUERY_STRING']) . '">' . htmlspecialchars("Login via " . get_op_name($json->issuer)) . '</a></p>';

        }

        closedir($handle);

}


?>

        </ul>

    </body>

</html>



More about discovery can be found here.

3.3 Resource page

A resource page is a page protected by mod_auth_openidc. It can be anything. To better demonstrate OIDC proto, I created a simple php page to display all the env variables configured by apache and mod_auth_openidc. 

<html>

<head></head>

<body>

        <div>

                <a href="/protected/redirect_uri?logout=https%3A%2F%2Fhome.linuxexam.net/index.html">Logout</a>

        </div>

        <pre>

                <?php print_r($_SERVER); ?>

        </pre>

</body>

</html>


4 Screen shots

On https://home.linuxexam.net/protected/index.php page, we can see the OIDC claims.
    [OIDC_access_token_expires] => 1736022480
    [OIDC_access_token_type] => Bearer
    [OIDC_access_token] => ya29.a0ARW5m75Ej6IqxQ_m-lsDJmvON0lTwjLqTY-4f0CZ5AOffMvlizb9hncnQwCQ4AC3nuABID4ycZUTuAVPi8yugwYTC3ZHmgiQoiukrUg2waKvtfF8o3THG_2aVG4wXBh-4jNA2FmpLNdTwcb3lCJ6BnrzOddoVGQ9kJSqEK3-aCgYKAVASARESFQHGX2Mi60xnl2b8Glik9x7_9TwD_Q0175
    [OIDC_CLAIM_exp] => 1736022481
    [OIDC_CLAIM_iat] => 1736018881
    [OIDC_CLAIM_family_name] => Zhao
    [OIDC_CLAIM_given_name] => Jonathan
    [OIDC_CLAIM_picture] => https://lh3.googleusercontent.com/a/ACg8ocJsdoadQJZwUadpiJPKHbKBAwcc-h4wP7-t5YP8qJOqKZJENw=s96-c
    [OIDC_CLAIM_name] => Jonathan Zhao
    [OIDC_CLAIM_nonce] => 0KWOL72-N1BWqKsNwT70fd2Qxmf24e5S_-fZBm4NSOU
    [OIDC_CLAIM_at_hash] => rZnQRep2c6HhbRZi2wtztQ
    [OIDC_CLAIM_email_verified] => 1
    [OIDC_CLAIM_email] => <my-googleaccount>@gmail.com
    [OIDC_CLAIM_sub] => 105062469164708568178
    [OIDC_CLAIM_aud] => 243577510543-kked70a1dpheii3jmbqkvmv82ha7hrmd.apps.googleusercontent.com
    [OIDC_CLAIM_azp] => 243577510543-kked70a1dpheii3jmbqkvmv82ha7hrmd.apps.googleusercontent.com
    [OIDC_CLAIM_iss] => https://accounts.google.com


No comments:

Post a Comment