The scripts below show how to sp.php script below shows how you can use SimpleSAMLphp to:
- Request a specific minimum level op assurance (LoA) from the Strong Authentication the SURFsecureID gateway
- Verify the LoA at which the user is authenticated
To use this scripts:
- Configure a SimpleSAMLphp SAML 2.0 hosted SP with the name "default-sp" (instructions). You can following the instructions on the simpleSAMLphp website.
- Add the Strong Authentication the SURFsecureID gateway as SAML 2.0 identity provider to
<simplesaml>/metadata/saml20-idp-remote.php
. See second example below. Below you find asaml20-idp-remote.php
containing the metadata of the SURFsecureID environments in simpleSAMLphp format for your convenience. - Put the spthe sp.php script php script in the <simplesaml>the <simplesaml>/www directory.
- www directory. Uncomment the
$gLOAmap
and$gRemoteIDPEntityID
variables for the SURFsecureID environment that you are connecting to.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<?php /* Copyright 2014, 2019 SURFnet bv Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ <?php // Include SimpleSAMLphp. Assume this script is placed in the <simplesaml>/www dir. require_once('../lib/_autoload.php'); // Name of session variable for storing the minimum required LOA for a login define( 'SSP_SESSION_MIN_LOA', 'RequestedMinLOA' ); //* Build return URL. This is where to ask simplesamlPHP to direct the browser to after login or logout // Point to this script, but without any request parameters so we won't trigger an login again (and again, and again, and ...) $returnURL = ($_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://'; $returnURL .= $_SERVER['HTTP_HOST']; $returnURL .= $_SERVER['SCRIPT_NAME']; // Map integer level of assurance level to the identifier used by SURFconext Strong Authentication // Note: The identifiers below are for the SURFconext Strong Authentication production gateway, when connecting to another // environment, update the identifiers accordingly. E.g. for pilot use 'http://pilot.surfconext.nl/assurance/loa1', etc... $gLOAmap = array( 1 => 'http://surfconext.nl/assurance/loa1', 2 => 'http://surfconext.nl/assurance/loa2', 3 => 'http://surfconext.nl/assurance/loa3', ); try { // Init SP instance // Assumes you have setup a SP named "default-sp" in <simplesaml>/config/authsources.php // See: https://simplesamlphp.org/docs/stable/simplesamlphp-sp $as = new SimpleSAML_Auth_Simple('default-sp'); // Init SP instance /** @var $session SimpleSAML_Session */ $session = SimpleSAML_Session::getInstance(); // Process login action. Assumes the login function of your SP uses ...?action=login if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'login' ) { // We use the SSP session to keep track of the LOA we want. // Unset any existing RequiredAuthnContextClassRef $session->deleteData('string', SSP_SESSION_MIN_LOA); Note: If your SP is located behind a HTTP proxy, you may need to update the way that this URL is generated, depending on the configuration of the proxy. */ $returnURL = ($_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://'; $returnURL .= $_SERVER['HTTP_HOST']; $returnURL .= $_SERVER['SCRIPT_NAME']; /* $gLOAmap map integer level of assurance level to the identifier used by SURFsecureID $gSURFsecureIDIdPEntityID is set to the EntityID of the SURFsecureID IdP Note: The LoA and IdP EntityID identifiers are different for each SURFsecureID environment. Uncomment the identifiers for the environment that you are using. See: https://wiki.surfnet.nl/display/SsID/SURFsecureID+Metadata+for+Service+Providers */ /* // SURFsecureID Production environment // EntityID $gSURFsecureIDIdPEntityID = 'https://sa-gw.surfconext.nl/authentication/metadata'; // LoA identifiers $gLOAmap = array( 1 => 'http://surfconext.nl/assurance/loa1', 2 => 'http://surfconext.nl/assurance/loa2', 3 => 'http://surfconext.nl/assurance/loa3', ); */ // SURFsecure Test environment // EntityID $gSURFsecureIDIdPEntityID = 'https://sa-gw.test.surfconext.nl/authentication/metadata'; // LoA identifiers $gLOAmap = array( 1 => 'http://test.surfconext.nl/assurance/loa1', 2 => 'http://test.surfconext.nl/assurance/loa2', 3 => 'http://test.surfconext.nl/assurance/loa3', ); try { // Init SP instance // Assumes you have setup a SP named "default-sp" in <simplesaml>/config/authsources.php // See: https://simplesamlphp.org/docs/stable/simplesamlphp-sp $as = new SimpleSAML_Auth_Simple('default-sp'); // Init SP instance /** @var $session SimpleSAML_Session */ $session = SimpleSAML_Session::getSessionFromRequest(); // Process login action. Assumes the login function of your SP uses ...?action=login if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'login' ) { // We use the SSP session to keep track of the LOA we want. // Unset any existing RequiredAuthnContextClassRef $session->deleteData('string', SSP_SESSION_MIN_LOA); // login $requiredLOA = 2; // The LOA level we want. // Store the requested LOA in the session so we can verify it later $session->setData('string', SSP_SESSION_MIN_LOA, $requiredLOA); $as->login( array( 'ReturnTo' => $returnURL, 'ForceAuthn' => false, 'saml:AuthnContextClassRef' => $gLOAmap[$requiredLOA] // Specify LOA ) ); exit; // Never reached. Added for clarity } // Process logout action if( isset($_REQUEST['action']) && $_REQUEST['action'] == 'logout' ) { $as->logout( array ( 'ReturnTo' => $returnURL, ) ); // Process logout exit; // Never reached. Added for clarity } // Output HTML page echo <<<head <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="Content-type" content="text/html;charset=UTF-8" /> <style type="text/css"> table,th,td {border: 1px solid black;} th,td {padding 1px} </style> <title>simpleSAMLphp LoA Authorisation Demo</title> <// loginhead> $requiredLOA<body> = 2; // The LOA we want. <h1>SimpleSAMLphp LoA Authorisation Demo<// Store the requested LOA in the session so we can verify it laterh1> head; // Page to show when SAML authentication was successful if ( $session$as->setData('string', SSP_SESSION_MIN_LOA, $requiredLOA); >isAuthenticated() ) { // Get attributes from $as->login( array(SAML Assertion $attributes = $as->getAttributes(); 'ReturnTo' => $returnURL, // Read the LoA we wanted from 'ForceAuthn' => false,the SSP session $requestedLoA 'saml:AuthnContextClassRef' => $gLOAmap[$requiredLOA]= $session->getData('string', SSP_SESSION_MIN_LOA); // Specify LOA What we requested during login ) ); $idpEntityID exit;= $as->getAuthData('saml:sp:IdP'); // Never reached. Added$nameID for clarity = $as->getAuthData('saml:sp:NameID'); } // ProcessGet the logoutauthentication actionstate if( isset($_REQUEST['action']) && $_REQUEST['action'] == 'logout' ) { $authState = $session->getAuthState('default-sp'); $as->logout( array ($authnConext = $authState['saml:sp:AuthnContext']; $authnInstant = 'ReturnTo' => $returnURL,gmdate('r', $authState['AuthnInstant'] ); )$expire ); // Process logout= gmdate('r', $authState['Expire'] ); exit; // Never reached. Added for clarity } echo "<h2>You are logged in</h2>"; echo "<h3>SimpleSAMLphp Session</h3>"; // Output HTML page echo "<p>SimpleSAMLphp session echo <<<headstart: <code>{$authnInstant}</code></br />"; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> echo "SimpleSAMLphp session expire: <code>{$expire}</code></p>"; echo "<h3>LoA and Authorisation</h3>"; $authnConextHTML=htmlentities($authnConext); <head>$IdPEntityIDHTML=htmlentities($idpEntityID); echo "<p>Authenticated by IdP <meta http-equiv="Content-type" content="text/html;charset=UTF-8"with EntityID <code>{$IdPEntityIDHTML}</code><br />"; if ( strlen($authnConext) > 0 <style type="text/css"> ) { echo "<p>Received authnConext <code>{$authnConextHTML}</code></p>"; table,th,td {border: 1px solid black;} else { th,td {padding 1px} echo "<p>The IdP did not send an authnConext</p>"; </style> } <title>simpleSAMLphp Demo<//title> We still need to verify that the </head> Authentication is at the LoA we requested, <body> and that the authenticaton // <h1>SimpleSAMLphpactually LOA Demo</h1> head; // Page to show when SAML authentication was successfulcame from the IdP we expected. Depending on the way SimpleSAMLphp is configured multiple IdPs if ( $as->isAuthenticated() ) { // could be allowed to authenticate to the $attributesSP. = $as->getAttributes(); $requestedLoA = $session->getData('string', SSP_SESSION_MIN_LOA);When using the LoA level for authorization it is important // to Whatensure wethat requestedthe duringLoA login identifier is comming from an IdP that we $authStatetrust = $session->getAuthState(); to issue this identifier. $authnConext = $authState['saml:sp:AuthnContext']; $nameID = $session->getNameID(); // Map LoA identifier back to an integer LoA level $authnInstant$actualLoA = gmdate('r', $authState['AuthnInstant'] )-1; $expire$translatedLoA = gmdate('r', $authState['Expire'] ); array_search($authnConext, $gLOAmap); echoif "<h2>You are logged in</h2>"; (false ==! $translatedLoA) { echo$actualLoA "<h3>SimpleSAMLphp Session</h3>"= $translatedLoA; echo "<p>SimpleSAMLphp<p>Actual sessionLoA startis: <b>{$authnInstant$actualLoA}</b></br />p>"; echo "SimpleSAMLphp session expire: <b>{$expire}</b></p>"; } echo "<h3>LOA</h3>";else { echo "<p>Received<p>Unrecognised LoA authnConextidentifier: <b>{$authnConext}</b><<code>$authnConextHTML</code></p>"; // We still need$actualLoA to= verify that the Authentication is at the-1; // Unrecognised LoA we requestedidentifier //} Map LoA identifier back to integer LoA level // Verify that the IdP that authenticated is $actualLoAthe = array_search($authnConext, $gLOAmap);one we expected if (false $idpEntityID !==! $gSURFsecureIDIdPEntityID $actualLoA) { echo "<p>Actual<p>Authenticated by LoAuntrusted isIdP: <b>{$actualLoA}</b><<code>$idpEntityID</code></p>"; else $actualLoA = -1; // Wrong IdP $actualLoA = -1;} if (NULL !== $requestedLoA) { echo "<p>Requested LoA was: <b>{$requestedLoA}</b></p>"; if ($actualLoA >= $requestedLoA) { echo '<p><b>You<p><b>Success!</b> You were authenticated at or above the minimally required LoA</b></p>'; } else { echo '<p><b>You<p><b>Failed!</b> You were NOTnot authenticated at the required LoA</b></p>'; // TODO: Handle this authenticationauthorisation failure } } } $nameIDValueHTML=htmlentities($nameID['Value']); }$nameIDFormatHTML=htmlentities($nameID['Format']); echo <<<html <h3>NameID</h3> <table> <tr><th>Value</th><td>{$nameID['Value']th><td><code>{$nameIDValueHTML}</code></td></tr> <tr><th>Format</th><td>{$nameID['Format']th><td><code>{$nameIDFormatHTML}</code></td></tr> </table> html; echo <<<html <h3>SAML Attributes</h3> <table> <tr><th>Attribute</th><th>Value(s)</th></tr> html; html; foreach ($attributes as $attrName => $attrVals) { foreach ($attributes as $attrName => $attrVal) {$attrNameHTML=htmlentities($attrName); echo "<tr><td><tr><td><code>{$attrName$attrNameHTML}</code></td><td>"; ifforeach (is_array($attrVals as $attrVal)) { echo implode('<br />', $attrValHTML=htmlentities($attrVal); else echo "<code>{$attrValHTML}</code><br />"; echo $attrVal;} echo "</td>"; } echo <<<html </table> <h3>Logout</h3> <p> <form name="logout" action="{$returnURL}" method="get"> <input type="hidden" name="action" value="logout"/> <input type="submit" value="Logout" /> </form> </p> html; } // User is not authenticated else { echo <<<html <h2>Your are not logged in</h2> html; } echo <<<html <h3>Login (again)</h3> <p> <form name="login" action="{$returnURL}" method="get"> <input type="hidden" name="action" value="login"/> <input type="submit" value="Login" /> </form> </p> html; echo <<<html </body> </html> html; } catch (Exception $e) { echo $e->getFile().':'.$e->getLine().' : '.$e->getMessage(); } |
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<?php // SURFconext Strong Authentication productionSURFsecureID PRODUCTION environment $metadata['https://sa-gw.surfconext.nl/authentication/metadata'] = array ( 'entityid' => 'https://sa-gw.surfconext.nl/authentication/metadata', 'metadata-set' => 'saml20-idp-remote', 'sign.authnrequest' => true, 'signature.algorithm' => 'saml20-idp-remote 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256', 'SingleSignOnService' => array ( 0 => array ( 'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', 'Location' => 'https://sa-gw.surfconext.nl/authentication/single-sign-on', ), ), 'keys' => array ( 0 => array ( 'encryption' => false, 'signing' => true, 'type' => 'X509Certificate', 'X509Certificate' => 'MIICsjCCAZoCCQDHN3+HzElEDDANBgkqhkiG9w0BAQUFADAbMRkwFwYDVQQDFBBnYXRld2F5X3NhbWxfaWRwMB4XDTE1MDcyMzEyMTUxOVoXDTIwMDcyMTEyMTUxOVowGzEZMBcGA1UEAxQQZ2F0ZXdheV9zYW1sX2lkcDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALK/JwHWd5JftXYKO9qcTQ4dfKEnl35oJj6PlEyR6gpikdpgm2OY/zy4e7vcXfBChedVF3OUI4rRDWCz4yXT2sldzjuIyONJfA86xva5lxDARqT/+gRBuZ2pyMTb0okvl1G9ZlAjPumVH14591rp6OGT5TJIkILQ/pKp1INdiBqpiR53Z5YvsXEUJ8PHHZyILO00HnBldq0d77lmATr6QamXpbY+CZ9pIw65t32fhFcUfRC68C81/P2crCn3v5GMyrQcM/tB/xdVf/haEZiqgI/bjcreBpQobnAhwEsve+uvbSLFN1Rsc7o0W/7Pn6EGBX1h9rjKjDgqssHuWkVuU4sCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAmlqfTvEfGDeqqqvuAMDG5IKDo6h21wwByywNbRhimfOvL6FqIgAgx+D3gxW1lO41PcqQQKYIVUEAuYv+tW8COLdHcFRh/UV9ei4iquMwBCkO/XOoMC9FsRBo3yPaQClRK8OYj1IXer4JXNuFHeLblzf+GLYFoqMWWwT2dnBLAePoEgANKUm2aasxyiJmnroNa+O5zTP9ExT3qHphCCG1gh3iHrQu9iSEJxY12zAQYtPomIs8Vk/GBfj+ucUiBEEqaMpCH+t6f0VOIoP1SNHgNAaeBLVuOpS0VlLnwZFJkNPVOQpFgRuoFsH3/9i53Fs3eQreb9wzq2VkjDhhlc5eyA==', ), ), ); // SURFconextSURFsecureID Strong AuthenticationTEST pilot environment $metadata['https://gateway.pilot.stepupsa-gw.test.surfconext.nl/authentication/metadata'] = array ( 'entityid' => 'https://gateway.pilot.stepup.surfconext.nl/authentication/metadata'sa-gw.test.surfconext.nl/authentication/metadata', 'metadata-set' => 'saml20-idp-remote', 'sign.authnrequest' => true, 'metadata-setsignature.algorithm' => 'saml20-idp-remotehttp://www.w3.org/2001/04/xmldsig-more#rsa-sha256', 'SingleSignOnService' => array ( 0 => array ( 'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', 'Location' => 'https://gatewaysa-gw.pilottest.stepup.surfconext.nl/authentication/single-sign-on', ), ), 'keys' => array ( 0 => array ( 'encryption' => false, 'signing' => true, 'type' => 'X509Certificate', 'X509Certificate' => 'MIICwjCCAaoCCQDs1IDIiytYMTANBgkqhkiG9w0BAQUFADAjMSEwHwYDVQQDDBhTdGVwdXAgUGlsb3QgR2F0ZXdheSBJZFAwHhcNMTUwMjI3MTA0MTU5WhcNMjUwMjI0MTA0MTU5WjAjMSEwHwYDVQQDDBhTdGVwdXAgUGlsb3QgR2F0ZXdheSBJZFAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDzlSzxPhG+B+o0ulUcR499NoMhaP4oX0+zpzi2vfY+Q1qw8x6b0eeUrIk99IpMrWN74twvuQ/eIecLZXIYhG94AYGB620OX7KMM8oCfdjc2I0lk8d+/rsxUqH0U4DDVBryrMcLjLGwe2CMncdSBuc2LCg15TveClC/QW/NJ6rvDiR1GoLouqx+CLBd+z2gwC+Od7YTUVY+22XrVzatzOZuz8Wdvja4VxHzv9Qyi6ta78ah345HWLeZsIh6pRF80qX0lpPRgwQSVXNpT8IxvoL28ViVVGkLU5SmdS3fbfLzlL5i3jHpWFcvRGPj5z8zmVZuDAka6P80WiKDVRg7gouXAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBANlnKHackl7MfHi+0lxb/ERuMkRpIGej29RSWL0aFojNpRjN2ihnuIjp4PPk98xQCKbVeN+PWXNqrrschbUfC5ikcYP5hoU7WJrHAWvEwmMNy1/UzcKtSgNby8loLFRzi68R92ZTumgFEBFYow9HzgC3HvDeBpRw/qFLZjsYqAjezTeRtafx8NIaBtKabRr5hedwUpnzldFbPqLxR1o0B/tqcUIqJOjdpEFIYus7VcBI6N6T1TKB4DyfqjbgzxhS5zrE1jFeKaamRWCqKUcEUfngoxQWlKd9LSBWRXjw0aM+P22WHdxxX/1rIneV5jVgOIlRgDO0Dpxn0qie4XnIqzo=' => 'MIIC6jCCAdICCQCWUmXBRox3ZDANBgkqhkiG9w0BAQsFADA3MRkwFwYDVQQDDBBHYXRld2F5IFNBTUwgSWRQMRowGAYDVQQKDBFTVVJGc2VjdXJlSUQgVEVTVDAeFw0xODA4MDEwODA2MjdaFw0yMzA3MzEwODA2MjdaMDcxGTAXBgNVBAMMEEdhdGV3YXkgU0FNTCBJZFAxGjAYBgNVBAoMEVNVUkZzZWN1cmVJRCBURVNUMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3okLxR7J2re6j7/rLjEYLc7iWiELcypmFLvL9BmCYqYZ80Pn+SR9LPUOXcplFUt6LYh/NOK5JMT0P6o0OTUP1P4zEMzLEl0wSJ1jBcu88yNppJoUn/TEgXMGNB1DW8jlVvzcgNSsJjxuw2Fj6J/6D+b77+7PhNMagbnfBEFStz0RBv7JOBdzuEC71wVxlGXB7C1Y8ZF3AwZgIp0jOVdiMub9i6neaKV9ZBLSv+azkT8BtAauMdKBpBxC+KxUFV9ccHKFnF2YOTLQ3CJNNGyQTtCJVI82fggraKnl+by2elV3+Dmzc0iqMcAdECasSS+2E8iOqw+3Qss+RgtS7QvCdQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCq/j+uXLvYDHhL7c/Y3+oj25+ur2UtZ/uSBqZIIqGlAzlCEL/zdgDI8XmePaRLtc2hYWUH4bD5Iu8HqxrMPrdBkG/5cjbMmlhU5uV3EX7S+m89k9vrok9+7B+uynCkMIdA/1Uif2btfEQi9hevvyP/1vvyoHqftym+ivIOyvELJNIgdTUaqvcJy//QvkmpvSpgTvlzHSVgKkSmMoBhTmevu7lQUGYSk/Mt53Zd3WmZhev+emS/MTKwV39JkZg7aykIRqXGVe/yTlttW/zaV9WtSIzNZfaKqASraAaClKgv8lsTjWFv88HZrsP/UuEseIWh4NjOo5HHvHYgqN/atX3t', ), ), ); |
...