PHP / OneLogin

Requirements

The cookbook uses the OneLogin SAML2 library and requires PHP >= 7.3.

Runtime Example

Included in the repository is a dockerized application for hands-on learners. It starts an Identity Provider and one Service Provider per example.

Clone the Repository

git clone https://gitlab.code.rit.edu/systems-public/saml-cookbook.git

Start Containers

The examples requires docker and docker-compose to run. You can download these by following instructions from the official Docker website.

After dependencies have been installed, navigate to the ./saml-cookbook/docker folder in a terminal and run the following command.

# Get a coffee, this is going to take a while
docker-compose build && docker-compose up

You can now view the example at https://saml-cookbook.localtest.me/. If you’re having issues verify that the command completed without errors and that localhost:443 is forwarding traffic to your docker containers.

All the code can be found at ./saml-cookbook/docker/<example>/.


Integration and Pre-Requisites

Install the onelogin/php-saml library with composer and you’re good to go.

Extract IdP Settings

Find the IdP metadata. RIT’s IdP metadata can be found here.

You’ll need the entity id, the signing certificate, and the single sign-on service url. All the examples in this cookbook default to using RIT’s SAML IdP metadata.

Configuration

Note

If you’re confused about any of the terms, refer to the glossary.

Selecting an Entity Id

An entity id must be a URI you own. An example of a good entity id is $serviceUrl + /saml2, or the URL of your metadata.

Do not use bare words like widgets or URIs owned by other entities like google.com/widgets.

Create a Keypair

You will need a public and private keypair to encrypt and sign communication between the IdP and your service. You must generate a separate keypair every time you create a new entity id. Included is a one-line example to show how we could easily create a keypair for our service.

openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 \
    -keyout "service.key" -out "service.crt" -subj "/CN=widgets.rit.edu"

Create Your Settings

Refer to the official documentation for an expanded list of options.

Save this alongside your application or integrate it with an existing settings file. These settings configure how you send requests to the IdP and how you parse responses.

Modify the settings in the SP Section and IdP Section to get started.

Variable Settings
 1/** SP Section **/
 2// base url of your site
 3$baseUrl = 'https://widgets.rit.edu/';
 4// your generated entity id
 5$spEntityId = 'https://widgets.rit.edu/saml2';
 6// your generated keypair information
 7// base64 encoded certificate (PEM) for the SP
 8$spCert = file_get_contents('/abs/path/to/service.crt');
 9// base64 encoded private key (PEM) for the SP
10$spKey = file_get_contents('/abs/path/to/service.key');
11/** End SP Section **/
12
13/** IdP Section **/
14// The following fields will be supplied to you
15// or extracted from metadata.
16
17// entity id for your IdP
18$idpEntityId = 'https://shibboleth.main.ad.rit.edu/idp/shibboleth';
19// single-sign-on url for your IdP, defaults to Redirect binding
20$idpSSOUrl = 'https://shibboleth.main.ad.rit.edu/idp/profile/SAML2/Redirect/SSO';
21
22// base64 encoded certificate (PEM)
23$idpCert = file_get_contents('/abs/path/to/idp.crt');
24/** End IdP Section **/
Common Settings
 1return [
 2    'strict' => true,
 3    // Enable debug mode (to print errors).
 4    'debug' => false,
 5    'baseurl' => $baseUrl,
 6    'security' => array (
 7        'signMetadata' => true,
 8        /** signatures and encryptions required **/
 9        // Indicates a requirement for the <samlp:Response>, <samlp:LogoutRequest>
10        // and <samlp:LogoutResponse> elements received by this SP to be signed.
11        'wantMessagesSigned' => true,
12        // Indicates a requirement for the <saml:Assertion> elements received by
13        // this SP to be encrypted.
14        'wantAssertionsEncrypted' => true,
15        // Indicates a requirement for the <saml:Assertion> elements received by
16        // this SP to be signed. [Metadata of the SP will offer this info]
17        'wantAssertionsSigned' => true,
18        'signatureAlgorithm' => 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
19        'digestAlgorithm' => 'http://www.w3.org/2001/04/xmlenc#sha256',
20        // Don't request an AuthnContext unless you know what you're doing,
21        // the default AunthnContext is PasswordProtectedTransport, which is 
22        // likely already what the IdP is doing.
23        'requestedAuthnContext' => false
24    ),
25    // Service provider configuration.
26    'sp' => array (
27        // Identifier of the SP entity  (must be a URI)
28        'entityId' => $spEntityId,
29        // Specifies info about where and how the <AuthnResponse> message MUST be
30        // returned to the requester, in this case our SP.
31        'assertionConsumerService' => array (
32            // URL Location where the <Response> from the IdP will be returned
33            'url' => "{$baseUrl}/acs/",
34            // SAML protocol binding to be used when returning the <Response>
35            // message. SAML Toolkit supports this endpoint for the
36            // HTTP-POST binding only.
37            'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
38        ),
39        // This is a x509 keypair generated specifically for this service
40        'x509cert' => $spCert,
41        'privateKey' => $spKey
42    ),
43    // Identity Provider Data that we want connected with our SP.
44    'idp' => array (
45        // Identifier of the IdP entity  (must be a URI)
46        'entityId' => $idpEntityId,
47        // SSO endpoint info of the IdP. (Authentication Request protocol)
48        'singleSignOnService' => array (
49            // URL Target of the IdP where the Authentication Request Message
50            // will be sent.
51            'url' => $idpSSOUrl,
52            // SAML protocol binding to be used when returning the <Response>
53            // message. SAML Toolkit supports the HTTP-Redirect binding
54            // only for this endpoint.
55            'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
56        ),
57        'x509cert' => $idpCert
58    ),
59];

Integrating SAML

Once your settings are taken care of you can begin integration. To integrate with a production IdP, you will need to exchange metadata and define the attributes you need for your service.

An example list of attributes you may need is:

  • User name (uid)

  • First name (givenName)

  • Last name (sn)

  • email (mail)

Generating Metadata

The IdP will require a copy of the metadata produced at this endpoint to register your service.

1$auth = new OneLogin\Saml2\Auth($samlSettings);
2$settings = $auth->getSettings();
3$metadata = $settings->getSPMetadata();
4header('Content-Type: text/xml');
5echo $metadata;

Creating the AuthnRequest

The Auth::login function handles redirecting the user to the IdP with the correct parameters.

1$auth = new OneLogin\Saml2\Auth($samlSettings);
2$auth->login();

Parsing the Response from the IdP

The ACS endpoint extracts and operates on a payload set by the IdP. This is handled by the Auth::processResponse method. Any errors should be shown to the user and must prevent further processing of the request.

 1try {
 2    $auth = new OneLogin\Saml2\Auth($samlSettings);
 3    $auth->processResponse();
 4    $errors = $auth->getErrors();
 5
 6    if (!empty($errors)) {
 7        echo '<p>' . htmlentities(implode(', ', $errors)) . '</p>';
 8        exit();
 9    }
10
11    // Successful authentication, get attributes and create a very simple
12    // session for the identity.
13    $_SESSION['saml_identity'] = $auth->getAttributes();
14    header('Location: /php/');
15    exit();
16}catch(Exception $exc){
17    // Do not echo this directly to the client, this is only shown
18    // for demonstration purposes. Convert the error to something
19    // appropriate for the client and log this message.
20    echo $exc->getMessage();
21}

Extracting Attributes

After successfully parsing the IdP payload, the ACS can then extract attributes. These are returned as an associative array and may be mapped to an alias such as mail, uid, givenName or may use an oid such as 0.9.2342.19200300.100.1.1.

The above example includes processing attributes, and the relevant lines have been highlighted.

 1$auth->processResponse();
 2$errors = $auth->getErrors();
 3
 4if (!empty($errors)) {
 5    echo '<p>' . htmlentities(implode(', ', $errors)) . '</p>';
 6    exit();
 7}
 8
 9// Successful authentication, get attributes and create a very simple
10// session for the identity.
11$_SESSION['saml_identity'] = $auth->getAttributes();