Mastering SAML Implementation in PHP: A Comprehensive Step-by-Step Guide
Tags: webdev, devcommunity, saml, php
Published: 2023-04-12 12:19:01

## **Introduction**

SAML (Security Assertion Markup Language) is a standard protocol that enables secure authentication and authorization between two different systems. It provides a secure way to exchange information between two parties, eliminating the need for user credentials to be stored in multiple places. SAML is widely used in enterprise environments to provide a Single Sign-On (SSO) solution for multiple applications.

In this article, we will discuss how to implement SAML in PHP and provide sample code to help you get started. We will cover everything you need to know to get started with SAML implementation in PHP.

We will cover the following topics:

1. Setting up the environment

2. Configuring the SAML Settings

3. Implementing the SAML Authentication

Let’s get started.

## **Setting up the Environment**

Before starting the implementation, we need to set up the environment for SAML implementation in PHP. We need to install the required packages for the SAML implementation in PHP. We can use the Composer package manager to install the required packages. The following are the packages that we need to install:

- robrichards/xmlseclibs
- symfony/yaml

These packages are essential for implementing SAML in PHP.


## **Configuring the SAML Settings**

After installing the required packages, we need to configure the SAML settings. We need to create a settings.php file, which will contain the SAML settings. The SAML settings define the identity provider (idp), service provider (sp), and security settings.

We can use the following code sample to configure the SAML settings.

```
$idp_entity_id = '<https://idp.example.com/metadata>';

$idp_sso_url = '<https://idp.example.com/sso>';

$idp_slo_url = '<https://idp.example.com/slo>';

$idp_x509_cert = 'MIICizCCAfQCCQCY...';

$sp_entity_id = '<https://sp.example.com/metadata>';

$sp_acs_url = '<https://sp.example.com/acs>';

$sp_slo_url = '<https://sp.example.com/slo>';

$sp_x509_cert = 'MIICizCCAfQCCQCY...';

$sp_private_key = 'MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJH2LlNQy8...';

$authn_request_binding = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect';

$response_binding = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST';

$name_id_format = 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified';

$authn_context = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport';

$settings_info = [

'idp_entity_id' => $idp_entity_id,

'idp_sso_url' => $idp_sso_url,

'idp_slo_url' => $idp_slo_url,

'idp_x509_cert' => $idp_x509_cert,

'sp_entity_id' => $sp_entity_id,

'sp_acs_url' => $sp_acs_url,

'sp_slo_url' => $sp_slo_url,

'sp_x509_cert' => $sp_x509_cert,

'sp_private_key' => $sp_private_key,

'authn_request_binding' => $authn_request_binding,

'response_binding' => $response_binding,

'name_id_format' => $name_id_format,

'authn_context' => $authn_context,
];
```

In the above code sample, we have defined the identity provider (idp), service provider (sp), and security settings. Please note that the URLs and certificates should be replaced with your own values.

## **Implementing the SAML Authentication**

After configuring the SAML settings, we can proceed with the SAML authentication implementation in PHP. We can use the following code sample to implement the SAML authentication.


```
require_once('xmlseclibs.php');

require_once('settings.php');

function generateRequest($settings_info)

{

$authn_request_binding = $settings_info['authn_request_binding'];

$name_id_format = $settings_info['name_id_format'];

$authn_context = $settings_info['authn_context'];

$sp_entity_id = $settings_info['sp_entity_id'];

$sp_acs_url = $settings_info['sp_acs_url'];

$private_key = $settings_info['sp_private_key'];

$idp_sso_url = $settings_info['idp_sso_url'];

$xmlstr = '<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"

xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="' . '_'.sha1(uniqid(mt_rand(), true)) . '" Version="2.0" IssueInstant="' . date('Y-m-dTH:i:sZ') . '" Destination="' . $idp_sso_url . '" ProtocolBinding="' . $authn_request_binding . '" AssertionConsumerServiceURL="' . $sp_acs_url . '">

<saml:Issuer>' . $sp_entity_id . '</saml:Issuer>

<samlp:NameIDPolicy Format="' . $name_id_format . '" AllowCreate="true"/>

<samlp:RequestedAuthnContext Comparison="exact">

<saml:AuthnContextClassRef>' . $authn_context . '</saml:AuthnContextClassRef>

</samlp:RequestedAuthnContext>

</samlp:AuthnRequest>';

$doc = new DOMDocument();

$doc->loadXML($xmlstr);

$objXMLSecDSig = new XMLSecurityDSig();

$objXMLSecDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);

$objXMLSecDSig->addReference($doc, XMLSecurityDSig::SHA1, array('<http://www.w3.org/2000/09/xmldsig#enveloped-signature>'), array('force_uri' => true));

$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'private'));

$objKey->loadKey($private_key, true);

$objXMLSecDSig->sign($objKey);

$xmlDoc = $objXMLSecDSig->createEnvelope();

$authnRequest = $xmlDoc->saveXML();

return $authnRequest;

}

function processResponse($settings_info, $saml_response)

{

$response_binding = $settings_info['response_binding'];

$sp_entity_id = $settings_info['sp_entity_id'];

$sp_slo_url = $settings_info['sp_slo_url'];

$sp_private_key = $settings_info['sp_private_key'];

$idp_entity_id = $settings_info['idp_entity_id'];

$idp_sso_url = $settings_info['idp_sso_url'];

$idp_x509_cert = $settings_info['idp_x509_cert'];

$xmlDoc = new DOMDocument();

$xmlDoc->loadXML($saml_response);

$objXMLSecDSig = new XMLSecurityDSig();

$objDSig = $objXMLSecDSig->locateSignature($xmlDoc);

$objXMLSecDSig->canonicalizeSignedInfo();

$objXMLSecDSig->idKeys = array('ID');

$objXMLSecDSig->idNS = array('urn:oasis:names:tc:SAML:2.0:assertion');

$retVal = $objXMLSecDSig->validateReference();

if (!$retVal) {

throw new Exception('Invalid Signature');

}

$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'private'));

$objKey->loadKey($sp_private_key, true);

$objXMLSecDSig->verify($objKey);

$saml_response_dom = new DOMDocument();

$saml_response_dom->loadXML($saml_response);

$assertions = $saml_response_dom->getElementsByTagName('Assertion');

if ($assertions->length < 1) {

throw new Exception('No assertions found in response.');

}

$assertion = $assertions->item(0);

$issuer = $assertion->getElementsByTagName('Issuer')->item(0)->nodeValue;

if ($issuer !== $idp_entity_id) {

throw new Exception('Invalid issuer in response');

}

$subject = $assertion->getElementsByTagName('Subject')->item(0);

$name_id = $subject->getElementsByTagName('NameID')->item(0)->nodeValue;

$session_index = $assertion->getElementsByTagName('AuthnStatement')->item(0)->getAttribute('SessionIndex');

$attributes = array();

$attribute_statements = $assertion->getElementsByTagName('AttributeStatement');

if ($attribute_statements->length > 0) {

$attribute_statement = $attribute_statements->item(0);

foreach ($attribute_statement->childNodes as $node) {

if ($node->nodeType == XML_ELEMENT_NODE) {

$attributes[$node->getAttribute('Name')] = $node->nodeValue;

}

}

}

$response = array(

'name_id' => $name_id,

'session_index' => $session_index,

'attributes' => $attributes,

);

return $response;

}

function redirect($url)

{

header('Location: ' . $url);

exit();

}

if (!empty($_GET['SAMLResponse'])) {

$response = processResponse($settings_info, $_GET['SAMLResponse']);

// Process the authenticated user

// ...

redirect($sp_slo_url);

} else {

$authnRequest = generateRequest($settings_info);

redirect($idp_sso_url . '?SAMLRequest=' . urlencode(base64_encode($authnRequest)));

}
```

In the above code sample, we have defined two functions, generateRequest and processResponse, which generate a SAML authentication request and process the SAML response, respectively. We have also defined a redirect function, which is used to redirect the user to the identity provider’s login page.

The generateRequest function generates a SAML authentication request, which is sent to the identity provider. The processResponse function processes the SAML response received from the identity provider. The function validates the signature and extracts the required information, such as the name ID, session index, and attributes.

## **Conclusion**

In conclusion, we have provided a step-by-step guide on how to implement SAML in PHP and provided sample code to help you get started. SAML implementation in PHP provides a secure way to authenticate and authorize between two different systems. We hope this blog post helps you in implementing SAML in PHP and makes your web development experience more secure and seamless.

With SAML implementation in PHP, you can use a secure way to exchange information between two parties, eliminating the need for user credentials to be stored in multiple places. SAML is widely used in enterprise environments to provide a Single Sign-On (SSO) solution for multiple applications. With the help of this guide, you can easily implement SAML in PHP and make your web applications more secure.

If you have any queries or suggestions, please let us know at support@ssojet.com. We would be happy to help you!