Share via


The PHP Way of Federated Login with ACS

Today's post introduces a PHP ACS Federation (ACSFed) library to assist PHP developers to enable federated login and Single Sign On (SSO) for their sites using Windows Azure AppFabric Access Control Service (ACS) 2.0.

If you are not familiar with ACS's role as the Federated Provider (FP) to support federated authentication, Eugenio explained it nicely in a series of blogs and you can find the very first post here.

Let's start with a tour of a demo PHP site built with ACSFed lib and then revisit the configurations, logic and code behind the scene.

AcsFedDemo RP Site

We use https://adventurousid.phpfogapp.com/acsfeddemo/, a very simple demo site deployed and running on PHP Fog to showcase the ACSFed lib. PHP Fog is a cloud platform hosting PHP site. It has  a very heroku-alike experience and uses Git as version control tool as well.

Since it is your first time visiting the AcsFedDemo site, the site identifies you are not authenticated and redirects you to a login page. The login page shows a list of available Identity Providers (IdPs) for you to choose to authenticate with (see below). These IdPs are preconfigured by the site owner (me in this case) in ACS and we will cover the configuration details later.

Pick one of the IdPs, say Yahoo!, to continue. You will be redirected to the Yahoo! login portal. You may notice the highlighted "adventurousid.accesscontrol.windows.net" in the red box, this is the ACS Security Token Service (STS) endpoint taking AcsFedDemo site as one of its RPs to manage. As mentioned earlier, it is configured to trust the Yahoo IdP to authentication the user. Now input your Yahoo! ID ("php.adventurousid@yahoo.com" in this case) and password and continue to sign in.

 

Next you will be prompted to give permission to sign in to the ACS STS endpoint and share the information of your Yahoo! account.

Once you allow the access, you successfully finish the federated login with the AcsFedDemo site and your browser will be redirected to the site's home page, as shown below. For exhibition purpose, the fact that you federated-login through Yahoo! as well as your Yahoo! identity information are reflected explicitly in the page (see the highlighted texts). 

 Under the Hood

A couple of tasks have been done to bring the federated login support to the AcsFedDemo site and we now discuss them in details below.

First, the site owner needs to configure ACS to enable federated authentication with the RP site.

Configuring ACS

The ACS configuration process for AcsFedDemo site is very much alike the process used to install ACS WordPress Plugin and you may find the detailed step-by-step explanation at https://acs.codeplex.com/wikipage?title=WordPress%20Plugin.

The primary differences are the Realm and Return URL settings in the Relying Party Applications setup step and the following screenshot captures them as highlights. Same as the ACS WordPress Plugin, we configure the ACS STS endpoint to issue Simple Web Token (SWT) to AcsFedDemo RP site as well.

 

Secondly, the site owner needs to integrate AcsFed lib into the RP site implementation. We will describe the AcsFed lib API briefly first and then examine the integration points in the AcsFedDemo implentation.

AcsFed Library

AcsFed library is developed based on the code base of ACS Plugin for WordPress, which is heavily abstracted and refactored. Its design goal is to be delivered as a general-purposed library to easily integrate with any PHP site to provide federated login with ACS support.

The libaray is organized as the following (the directory hierarchy is designed to match the hierarchy of the namespaces, which haven't not been implemented yet in the current code).

 

  • ms/acs: This namespace holds the classes for ACS common entities, supported protocols and token handlers.
    • ms/acs/common: ACS supported claim types (represented in URLs) and token types are defined here.
    • ms/acs/protocols: WSFederationHandler.php primarily deals with the WS-Federation RequestSecurityTokenResponse (RSTR) messages.
    • ms/acs/tokens: This namespace contains an interface for all token handlers to implement and a factory class to generate a specific token handler based on the token format type. In addition, it defines a concrete token handler to process Simple Web Token (SWT).
  • ms/acsfed: This namespace holds the classes and resources to integrate with the RP site to enable federated login.
    • AcsFed.php: It offers two primary APIs to assist the RP site to create the federation login page and parse the WS-Federation RSTR to extract ACS token and then the embedded claims.
    • AcsFedConfig.php: It defines the following configuration data required in the federated login process. Some of the data, such as the token signing keys, are sensitive and should be kept in a secure way.
      • SERVICE_NAMESPACE
      • RELYING_PARTY_APPLICATION_REALM
      • TOKEN_SIGNING_KEY and TOKEN_SIGNING_KEY_OLD
      • TOKEN_TYPE
      • HOME_REALM_DISCOVERY_FEED_URL
    • .htaccess: It is used to protected the sensitive data in AcsFedConfig.php.
    • ms/acsfed/css: Contains ACS-specific style sheet required to render the federated login widget.
    • ms/acsfed/js: Contains JavaScripts required to render the federated login widget.

Next, we show several code snippets (syntax highlighted by https://tohtml.com/) to capture the major integration points to integrate PHP ACS Federation Library with the AcsFedDemo RP site.

RP Site Integration 

  •  AcsFedDemo's login page

 The login page, redirected from the home page on an unauthenticated access, is responsible for rendering the federated login widget for the user to authenticate with RP site's supported IdPs. As illustrated in the following highlights, it makes use of the acs_fed.css CSS style sheet and acs_fed.js Javascript in the web head and calls AcsFed::createLoginForm() to render the widget in the web body.

 <?php 
require_once('ms/acsfed/AcsFed.php');
session_start();
?>

<html>
 <head>
  <title>Federated Login via ACS</title>
  <link href="ms/acsfed/css/acs_fed.css" rel="stylesheet" type="text/css">
  <script type="text/javascript" src="ms/acsfed/js/acs_fed.js"></script>
 </head>
 <body>
     <p>
<h2>Demo RP Site using <a href="https://blogs.msdn.com/b/adventurousidentity/">PHP ACS Fed Lib</a></h2>
     <?php 
     AcsFed::createLoginForm(); 
    ?> 
 </body>
</html>
  • AcsFedDemo's auth page

The auth page, whose URL is configured as the Return URL value in RP site's corresponding ACS RP configuration, will be posted with the WS-Federated RSTR containing ACS SWT token by RP site's ACS STS endpoint. It utilizes AcsFed::parseTokenAndExtractClaims(RSTR) to validate the RSTR, extract the ACS token and further extract the embedded claims. 

 <?php
function authenticate($username = "", $password = "") 
{
    //Check to see if a token is being posted from ACS (using the WS-Federation protocol)
    $rstr = @$_POST['wresult'];     if ($rstr) 
    {
        //Decode the response if it was reposted as part of the account creation form (see below)
        $rstr = (array_key_exists('user_login', $_POST)) ? urldecode($rstr) : $rstr;
        
        try
        {
            $claims = AcsFed::parseTokenAndExtractClaims($rstr);         } 
        catch (Exception $e) 
        {
            $user = new Error('login_error', $e->getMessage());
            return $user;
        } 

        //check for required UUID and IDP claims
        $user_uuid = $claims[AcsClaimType::$UUID];
        $user_idp = $claims[AcsClaimType::$IDP];
        if (empty($user_uuid))
        {
            $user = new Error('login_error', 'No user ID was returned from the selected identity provider');
            return $user;
            
        }
        elseif (empty($user_idp))
        {
            $user = new Error('login_error', 'No identity provider claim was returned');
            return $user; 
        }
        
        $user_name = $claims[AcsClaimType::$NAME];
        $user_email = $claims[AcsClaimType::$EMAIL];
        
        $user = UserFactory::Create($user_idp, $user_uuid, $user_name, $user_email); 
        return $user;
    }
    else
    {
        $user = new Error('login_error', 'no ACS token');
        return $user;
    }
};

$user = authenticate();
if ($user instanceof User)
{
    session_start();
    $_SESSION['userName'] = $user->name;
    $_SESSION['userEmail'] = $user->email;
    $_SESSION['userIdP'] = $user->idp_fname;
    $_SESSION['userUuid'] = $user->uuid;

        
   header("HTTP/1.1 301 Moved Permanently");
    header("Location: index.php");
}
?>

Code Download

Currently we are still figuring the appropriate channel and license to distribute the PHP ACS Federation Library code. Once settled, we will publish its download link as well as the AcsFedDemo RP site's source code. Keep tuned in!