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.
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 **/
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();