1<?php 2/** 3 * This file is part of php-saml. 4 * 5 * (c) OneLogin Inc 6 * 7 * For the full copyright and license information, please view the LICENSE 8 * file that was distributed with this source code. 9 * 10 * @package OneLogin 11 * @author OneLogin Inc <saml-info@onelogin.com> 12 * @license MIT https://github.com/onelogin/php-saml/blob/master/LICENSE 13 * @link https://github.com/onelogin/php-saml 14 */ 15 16namespace OneLogin\Saml2; 17 18/** 19 * SAML 2 Authentication Request 20 */ 21class AuthnRequest 22{ 23 /** 24 * Object that represents the setting info 25 * 26 * @var Settings 27 */ 28 protected $_settings; 29 30 /** 31 * SAML AuthNRequest string 32 * 33 * @var string 34 */ 35 private $_authnRequest; 36 37 /** 38 * SAML AuthNRequest ID. 39 * 40 * @var string 41 */ 42 private $_id; 43 44 /** 45 * Constructs the AuthnRequest object. 46 * 47 * @param Settings $settings SAML Toolkit Settings 48 * @param bool $forceAuthn When true the AuthNReuqest will set the ForceAuthn='true' 49 * @param bool $isPassive When true the AuthNReuqest will set the Ispassive='true' 50 * @param bool $setNameIdPolicy When true the AuthNReuqest will set a nameIdPolicy 51 * @param string $nameIdValueReq Indicates to the IdP the subject that should be authenticated 52 */ 53 public function __construct(\OneLogin\Saml2\Settings $settings, $forceAuthn = false, $isPassive = false, $setNameIdPolicy = true, $nameIdValueReq = null) 54 { 55 $this->_settings = $settings; 56 57 $spData = $this->_settings->getSPData(); 58 $idpData = $this->_settings->getIdPData(); 59 $security = $this->_settings->getSecurityData(); 60 61 $id = Utils::generateUniqueID(); 62 $issueInstant = Utils::parseTime2SAML(time()); 63 64 $subjectStr = ""; 65 if (isset($nameIdValueReq)) { 66 $subjectStr = <<<SUBJECT 67 68 <saml:Subject> 69 <saml:NameID Format="{$spData['NameIDFormat']}">{$nameIdValueReq}</saml:NameID> 70 <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"></saml:SubjectConfirmation> 71 </saml:Subject> 72SUBJECT; 73 } 74 75 $nameIdPolicyStr = ''; 76 if ($setNameIdPolicy) { 77 $nameIDPolicyFormat = $spData['NameIDFormat']; 78 if (isset($security['wantNameIdEncrypted']) && $security['wantNameIdEncrypted']) { 79 $nameIDPolicyFormat = Constants::NAMEID_ENCRYPTED; 80 } 81 82 $nameIdPolicyStr = <<<NAMEIDPOLICY 83 84 <samlp:NameIDPolicy 85 Format="{$nameIDPolicyFormat}" 86 AllowCreate="true" /> 87NAMEIDPOLICY; 88 } 89 90 91 $providerNameStr = ''; 92 $organizationData = $settings->getOrganization(); 93 if (!empty($organizationData)) { 94 $langs = array_keys($organizationData); 95 if (in_array('en-US', $langs)) { 96 $lang = 'en-US'; 97 } else { 98 $lang = $langs[0]; 99 } 100 if (isset($organizationData[$lang]['displayname']) && !empty($organizationData[$lang]['displayname'])) { 101 $providerNameStr = <<<PROVIDERNAME 102 ProviderName="{$organizationData[$lang]['displayname']}" 103PROVIDERNAME; 104 } 105 } 106 107 $forceAuthnStr = ''; 108 if ($forceAuthn) { 109 $forceAuthnStr = <<<FORCEAUTHN 110 111 ForceAuthn="true" 112FORCEAUTHN; 113 } 114 115 $isPassiveStr = ''; 116 if ($isPassive) { 117 $isPassiveStr = <<<ISPASSIVE 118 119 IsPassive="true" 120ISPASSIVE; 121 } 122 123 $requestedAuthnStr = ''; 124 if (isset($security['requestedAuthnContext']) && $security['requestedAuthnContext'] !== false) { 125 $authnComparison = 'exact'; 126 if (isset($security['requestedAuthnContextComparison'])) { 127 $authnComparison = $security['requestedAuthnContextComparison']; 128 } 129 130 $authnComparisonAttr = ''; 131 if (!empty($authnComparison)) { 132 $authnComparisonAttr = sprintf('Comparison="%s"', $authnComparison); 133 } 134 135 if ($security['requestedAuthnContext'] === true) { 136 $requestedAuthnStr = <<<REQUESTEDAUTHN 137 138 <samlp:RequestedAuthnContext $authnComparisonAttr> 139 <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef> 140 </samlp:RequestedAuthnContext> 141REQUESTEDAUTHN; 142 } else { 143 $requestedAuthnStr .= " <samlp:RequestedAuthnContext $authnComparisonAttr>\n"; 144 foreach ($security['requestedAuthnContext'] as $contextValue) { 145 $requestedAuthnStr .= " <saml:AuthnContextClassRef>".$contextValue."</saml:AuthnContextClassRef>\n"; 146 } 147 $requestedAuthnStr .= ' </samlp:RequestedAuthnContext>'; 148 } 149 } 150 151 $spEntityId = htmlspecialchars($spData['entityId'], ENT_QUOTES); 152 $acsUrl = htmlspecialchars($spData['assertionConsumerService']['url'], ENT_QUOTES); 153 $request = <<<AUTHNREQUEST 154<samlp:AuthnRequest 155 xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" 156 xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" 157 ID="$id" 158 Version="2.0" 159{$providerNameStr}{$forceAuthnStr}{$isPassiveStr} 160 IssueInstant="$issueInstant" 161 Destination="{$idpData['singleSignOnService']['url']}" 162 ProtocolBinding="{$spData['assertionConsumerService']['binding']}" 163 AssertionConsumerServiceURL="{$acsUrl}"> 164 <saml:Issuer>{$spEntityId}</saml:Issuer>{$subjectStr}{$nameIdPolicyStr}{$requestedAuthnStr} 165</samlp:AuthnRequest> 166AUTHNREQUEST; 167 168 $this->_id = $id; 169 $this->_authnRequest = $request; 170 } 171 172 /** 173 * Returns deflated, base64 encoded, unsigned AuthnRequest. 174 * 175 * @param bool|null $deflate Whether or not we should 'gzdeflate' the request body before we return it. 176 * 177 * @return string 178 */ 179 public function getRequest($deflate = null) 180 { 181 $subject = $this->_authnRequest; 182 183 if (is_null($deflate)) { 184 $deflate = $this->_settings->shouldCompressRequests(); 185 } 186 187 if ($deflate) { 188 $subject = gzdeflate($this->_authnRequest); 189 } 190 191 $base64Request = base64_encode($subject); 192 return $base64Request; 193 } 194 195 /** 196 * Returns the AuthNRequest ID. 197 * 198 * @return string 199 */ 200 public function getId() 201 { 202 return $this->_id; 203 } 204 205 /** 206 * Returns the XML that will be sent as part of the request 207 * 208 * @return string 209 */ 210 public function getXML() 211 { 212 return $this->_authnRequest; 213 } 214} 215