21 assert(isset($state[
'Attributes']));
22 assert(isset($state[
'SPMetadata']));
23 assert(isset($state[
'saml:ConsumerURL']));
24 assert(array_key_exists(
'saml:RequestId', $state));
25 assert(array_key_exists(
'saml:RelayState', $state));
36 $requestId = $state[
'saml:RequestId'];
38 $consumerURL = $state[
'saml:ConsumerURL'];
39 $protocolBinding = $state[
'saml:Binding'];
47 if (isset($state[
'saml:AuthenticatingAuthority'])) {
48 $assertion->setAuthenticatingAuthority($state[
'saml:AuthenticatingAuthority']);
54 'Handler' =>
'sspmod_saml_IdP_SAML2',
55 'Expires' => $assertion->getSessionNotOnOrAfter(),
57 'saml:NameID' => $state[
'saml:idp:NameID'],
58 'saml:SessionIndex' => $assertion->getSessionIndex(),
66 $ar->setInResponseTo($requestId);
68 $ar->setAssertions(array($assertion));
76 'protocol' =>
'saml2',
78 if (isset($state[
'saml:AuthnRequestReceivedAt'])) {
79 $statsData[
'logintime'] = microtime(
true) - $state[
'saml:AuthnRequestReceivedAt'];
98 assert(isset($state[
'SPMetadata']));
99 assert(isset($state[
'saml:ConsumerURL']));
100 assert(array_key_exists(
'saml:RequestId', $state));
101 assert(array_key_exists(
'saml:RelayState', $state));
110 $requestId = $state[
'saml:RequestId'];
112 $consumerURL = $state[
'saml:ConsumerURL'];
113 $protocolBinding = $state[
'saml:Binding'];
125 $ar->setInResponseTo($requestId);
129 'Code' => $error->getStatus(),
130 'SubCode' => $error->getSubStatus(),
131 'Message' => $error->getStatusMessage(),
133 $ar->setStatus($status);
138 'protocol' =>
'saml2',
141 if (isset($state[
'saml:AuthnRequestReceivedAt'])) {
142 $statsData[
'logintime'] = microtime(
true) - $state[
'saml:AuthnRequestReceivedAt'];
163 array $supportedBindings,
165 $AssertionConsumerServiceURL,
167 $AssertionConsumerServiceIndex
169 assert(is_string($AssertionConsumerServiceURL) || $AssertionConsumerServiceURL === null);
170 assert(is_string($ProtocolBinding) || $ProtocolBinding === null);
171 assert(is_int($AssertionConsumerServiceIndex) || $AssertionConsumerServiceIndex === null);
180 $firstNotFalse = null;
182 foreach ($spMetadata->
getEndpoints(
'AssertionConsumerService') as $ep) {
183 if ($AssertionConsumerServiceURL !== null && $ep[
'Location'] !== $AssertionConsumerServiceURL) {
186 if ($ProtocolBinding !== null && $ep[
'Binding'] !== $ProtocolBinding) {
189 if ($AssertionConsumerServiceIndex !== null && $ep[
'index'] !== $AssertionConsumerServiceIndex) {
193 if (!in_array($ep[
'Binding'], $supportedBindings,
true)) {
200 if (array_key_exists(
'isDefault', $ep)) {
201 if ($ep[
'isDefault'] ===
true) {
206 if ($firstFalse === null) {
211 if ($firstNotFalse === null) {
213 $firstNotFalse = $ep;
218 if ($firstNotFalse !== null) {
219 return $firstNotFalse;
220 } elseif ($firstFalse !== null) {
225 if ($AssertionConsumerServiceURL !== null) {
228 if ($ProtocolBinding !== null) {
231 if ($AssertionConsumerServiceIndex !== null) {
233 'AssertionConsumerServiceIndex: '.var_export($AssertionConsumerServiceIndex,
true)
238 return $spMetadata->
getDefaultEndpoint(
'AssertionConsumerService', $supportedBindings);
254 $supportedBindings = array(\
SAML2\Constants::BINDING_HTTP_POST);
255 if (
$idpMetadata->getBoolean(
'saml20.sendartifact',
false)) {
258 if (
$idpMetadata->getBoolean(
'saml20.hok.assertion',
false)) {
265 if (isset($_REQUEST[
'spentityid'])) {
268 if (isset($_REQUEST[
'cookieTime'])) {
269 $cookieTime = (int) $_REQUEST[
'cookieTime'];
270 if ($cookieTime + 5 > time()) {
282 if (isset($_REQUEST[
'RelayState'])) {
288 if (isset($_REQUEST[
'binding'])) {
289 $protocolBinding = (string) $_REQUEST[
'binding'];
291 $protocolBinding = null;
294 if (isset($_REQUEST[
'NameIDFormat'])) {
295 $nameIDFormat = (string) $_REQUEST[
'NameIDFormat'];
297 $nameIDFormat = null;
307 $consumerIndex = null;
310 $authnContext = null;
316 'SAML2.0 - IdP.SSOService: IdP initiated authentication: '.var_export(
$spEntityId,
true)
324 'Message received on authentication request endpoint wasn\'t an authentication request.' 331 'Received message on authentication request endpoint without issuer.' 342 $ProxyCount =
$request->getProxyCount();
343 if ($ProxyCount !== null) {
346 $RequesterID =
$request->getRequesterID();
347 $forceAuthn =
$request->getForceAuthn();
348 $isPassive =
$request->getIsPassive();
349 $consumerURL =
$request->getAssertionConsumerServiceURL();
350 $protocolBinding =
$request->getProtocolBinding();
351 $consumerIndex =
$request->getAssertionConsumerServiceIndex();
352 $extensions =
$request->getExtensions();
353 $authnContext =
$request->getRequestedAuthnContext();
355 $nameIdPolicy =
$request->getNameIdPolicy();
356 if (isset($nameIdPolicy[
'Format'])) {
357 $nameIDFormat = $nameIdPolicy[
'Format'];
359 $nameIDFormat = null;
361 if (isset($nameIdPolicy[
'AllowCreate'])) {
362 $allowCreate = $nameIdPolicy[
'AllowCreate'];
364 $allowCreate =
false;
370 'SAML2.0 - IdP.SSOService: incoming authentication request: '.var_export(
$spEntityId,
true)
377 'forceAuthn' => $forceAuthn,
378 'isPassive' => $isPassive,
379 'protocol' =>
'saml2',
380 'idpInit' => $idpInit,
383 $acsEndpoint = self::getAssertionConsumerService(
391 $IDPList = array_unique(array_merge($IDPList,
$spMetadata->getArrayizeString(
'IDPList', array())));
392 if ($ProxyCount === null) {
393 $ProxyCount =
$spMetadata->getInteger(
'ProxyCount', null);
397 $forceAuthn =
$spMetadata->getBoolean(
'ForceAuthn',
false);
400 $sessionLostParams = array(
402 'cookieTime' => time(),
408 $sessionLostURL = \SimpleSAML\Utils\HTTP::addURLParameters(
414 'Responder' => array(
'sspmod_saml_IdP_SAML2',
'sendResponse'),
420 'saml:RequestId' => $requestId,
421 'saml:IDPList' => $IDPList,
422 'saml:ProxyCount' => $ProxyCount,
423 'saml:RequesterID' => $RequesterID,
424 'ForceAuthn' => $forceAuthn,
425 'isPassive' => $isPassive,
426 'saml:ConsumerURL' => $acsEndpoint[
'Location'],
427 'saml:Binding' => $acsEndpoint[
'Binding'],
428 'saml:NameIDFormat' => $nameIDFormat,
429 'saml:AllowCreate' => $allowCreate,
430 'saml:Extensions' => $extensions,
431 'saml:AuthnRequestReceivedAt' => microtime(
true),
432 'saml:RequestedAuthnContext' => $authnContext,
437 self::processSOAPAuthnRequest(
$state);
451 $state[
'core:auth:username'] =
$_SERVER[
'PHP_AUTH_USER'];
452 $state[
'core:auth:password'] =
$_SERVER[
'PHP_AUTH_PW'];
466 SimpleSAML\Logger::info(
'Sending SAML 2.0 LogoutRequest to: '.var_export($association[
'saml:entityID'],
true));
473 'spEntityID' => $association[
'saml:entityID'],
478 'SingleLogoutService',
480 \
SAML2\Constants::BINDING_HTTP_REDIRECT,
481 \
SAML2\Constants::BINDING_HTTP_POST
486 $lr->setDestination(
$dst[
'Location']);
500 assert(isset($state[
'saml:SPEntityId']));
501 assert(isset($state[
'saml:RequestId']));
502 assert(array_key_exists(
'saml:RelayState', $state));
511 $lr->setInResponseTo($state[
'saml:RequestId']);
512 $lr->setRelayState($state[
'saml:RelayState']);
514 if (isset($state[
'core:Failed']) && $state[
'core:Failed']) {
516 $lr->setStatus(array(
517 'Code' => \
SAML2\Constants::STATUS_SUCCESS,
518 'SubCode' => \
SAML2\Constants::STATUS_PARTIAL_LOGOUT,
529 'partial' => $partial
532 'SingleLogoutService',
534 \
SAML2\Constants::BINDING_HTTP_REDIRECT,
535 \
SAML2\Constants::BINDING_HTTP_POST
539 if (isset(
$dst[
'ResponseLocation'])) {
581 $statsData[
'error'] =
$message->getStatus();
608 'Responder' => array(
'sspmod_saml_IdP_SAML2',
'sendLogoutResponse'),
610 'saml:RelayState' =>
$message->getRelayState(),
611 'saml:RequestId' =>
$message->getId(),
635 SimpleSAML\Logger::info(
'Sending SAML 2.0 LogoutRequest to: '.var_export($association[
'saml:entityID'],
true));
642 \
SAML2\Constants::BINDING_HTTP_REDIRECT,
643 \
SAML2\Constants::BINDING_HTTP_POST
647 if (
$dst[
'Binding'] === \
SAML2\Constants::BINDING_HTTP_POST) {
648 $params = array(
'association' => $association[
'id'],
'idp' => $idp->
getId());
656 $lr->setDestination(
$dst[
'Location']);
658 $binding = new \SAML2\HTTPRedirect();
675 return $metadata->getMetaDataConfig($association[
'saml:entityID'],
'saml20-sp-remote');
697 $attribute = $spMetadata->
getString(
'simplesaml.nameidattribute', null);
698 if ($attribute === null) {
699 $attribute = $idpMetadata->
getString(
'simplesaml.nameidattribute', null);
700 if ($attribute === null) {
701 if (!isset($state[
'UserID'])) {
705 $attributeValue = $state[
'UserID'];
711 $uidData =
'uidhashbase'.$secretSalt;
714 $uidData .= strlen($attributeValue).
':'.$attributeValue;
715 $uidData .= $secretSalt;
717 return hash(
'sha1', $uidData);
724 ' in the attributes of the user.');
749 $base64Attributes = $spMetadata->
getBoolean(
'base64attributes', null);
750 if ($base64Attributes === null) {
751 $base64Attributes = $idpMetadata->
getBoolean(
'base64attributes',
false);
754 if ($base64Attributes) {
755 $defaultEncoding =
'base64';
757 $defaultEncoding =
'string';
760 $srcEncodings = $idpMetadata->
getArray(
'attributeencodings', array());
761 $dstEncodings = $spMetadata->
getArray(
'attributeencodings', array());
767 $encodings = array_merge($srcEncodings, $dstEncodings);
772 if (array_key_exists(
$name, $encodings)) {
773 $encoding = $encodings[
$name];
775 $encoding = $defaultEncoding;
780 if ($value === null) {
786 if ($value instanceof DOMNodeList) {
787 $attrval = new \SAML2\XML\saml\AttributeValue($value->item(0)->parentNode);
792 $value = (string) $attrval;
795 $value = base64_encode((
string) $attrval);
798 if (is_string($value)) {
800 $value = $doc->firstChild->childNodes;
802 assert($value instanceof DOMNodeList || $value instanceof \
SAML2\XML\saml\NameID);
806 var_export(
$name,
true).
': '.var_export($encoding,
true));
850 return 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic';
870 assert(isset($state[
'Attributes']));
871 assert(isset($state[
'saml:ConsumerURL']));
875 $signAssertion = $spMetadata->
getBoolean(
'saml20.sign.assertion', null);
876 if ($signAssertion === null) {
877 $signAssertion = $idpMetadata->
getBoolean(
'saml20.sign.assertion',
true);
882 $a = new \SAML2\Assertion();
883 if ($signAssertion) {
887 $a->setIssuer($idpMetadata->
getString(
'entityid'));
888 $a->setValidAudiences(array($spMetadata->
getString(
'entityid')));
890 $a->setNotBefore($now - 30);
898 if (isset($state[
'saml:AuthnContextClassRef'])) {
899 $a->setAuthnContextClassRef($state[
'saml:AuthnContextClassRef']);
900 } elseif (\
SimpleSAML\Utils\HTTP::isHTTPS()) {
901 $a->setAuthnContextClassRef(\
SAML2\Constants::AC_PASSWORD_PROTECTED_TRANSPORT);
903 $a->setAuthnContext(\
SAML2\Constants::AC_PASSWORD);
906 $sessionStart = $now;
907 if (isset($state[
'AuthnInstant'])) {
908 $a->setAuthnInstant($state[
'AuthnInstant']);
909 $sessionStart = $state[
'AuthnInstant'];
912 $sessionLifetime =
$config->getInteger(
'session.duration', 8 * 60 * 60);
913 $a->setSessionNotOnOrAfter($sessionStart + $sessionLifetime);
915 $a->setSessionIndex(
SimpleSAML\Utils\Random::generateID());
917 $sc = new \SAML2\XML\saml\SubjectConfirmation();
920 $sc->SubjectConfirmationData->Recipient = $state[
'saml:ConsumerURL'];
921 $sc->SubjectConfirmationData->InResponseTo = $state[
'saml:RequestId'];
924 $hokAssertion = null;
925 if ($state[
'saml:Binding'] === \
SAML2\Constants::BINDING_HOK_SSO) {
926 $hokAssertion =
true;
928 if ($hokAssertion === null) {
929 $hokAssertion = $idpMetadata->
getBoolean(
'saml20.hok.assertion',
false);
936 if (isset(
$_SERVER[
'SSL_CLIENT_CERT']) && !empty(
$_SERVER[
'SSL_CLIENT_CERT'])) {
938 $clientCert =
$_SERVER[
'SSL_CLIENT_CERT'];
939 $pattern =
'/^-----BEGIN CERTIFICATE-----([^-]*)^-----END CERTIFICATE-----/m';
940 if (preg_match($pattern, $clientCert, $matches)) {
942 $x509Certificate = new \SAML2\XML\ds\X509Certificate();
943 $x509Certificate->certificate = str_replace(array(
"\r",
"\n",
" "),
'', $matches[1]);
945 $x509Data = new \SAML2\XML\ds\X509Data();
946 $x509Data->data[] = $x509Certificate;
948 $keyInfo = new \SAML2\XML\ds\KeyInfo();
949 $keyInfo->info[] = $x509Data;
951 $sc->SubjectConfirmationData->info[] = $keyInfo;
954 'Error creating HoK assertion: No valid client certificate provided during TLS handshake '.
960 'Error creating HoK assertion: No client certificate provided during TLS handshake with IdP' 965 'Error creating HoK assertion: No HTTPS connection to IdP, but required for Holder-of-Key SSO' 972 $a->setSubjectConfirmation(array(
$sc));
975 if ($spMetadata->
getBoolean(
'simplesaml.attributes',
true)) {
978 $attributes = self::encodeAttributes($idpMetadata, $spMetadata, $state[
'Attributes']);
983 if (isset($state[
'saml:NameIDFormat'])) {
991 $nameIdFormat = $spMetadata->
getString(
'NameIDFormat', null);
992 if ($nameIdFormat === null) {
993 $nameIdFormat = $idpMetadata->
getString(
'NameIDFormat', \
SAML2\Constants::NAMEID_TRANSIENT);
997 if (isset($state[
'saml:NameID'][$nameIdFormat])) {
1001 $spNameQualifier = $spMetadata->
getString(
'SPNameQualifier', null);
1002 if ($spNameQualifier === null) {
1003 $spNameQualifier = $spMetadata->
getString(
'entityid');
1006 if ($nameIdFormat === \
SAML2\Constants::NAMEID_TRANSIENT) {
1012 $nameIdValue = self::generateNameIdValue($idpMetadata, $spMetadata, $state);
1020 $nameId = new \SAML2\XML\saml\NameID();
1023 $nameId->SPNameQualifier = $spNameQualifier;
1026 $state[
'saml:idp:NameID'] =
$nameId;
1059 \
SAML2\Assertion $assertion
1062 $encryptAssertion = $spMetadata->
getBoolean(
'assertion.encryption', null);
1063 if ($encryptAssertion === null) {
1064 $encryptAssertion = $idpMetadata->
getBoolean(
'assertion.encryption',
false);
1066 if (!$encryptAssertion) {
1072 $sharedKey = $spMetadata->
getString(
'sharedkey', null);
1073 if ($sharedKey !== null) {
1075 $key->loadKey($sharedKey);
1078 if (!empty(
$keys)) {
1080 switch (
$key[
'type']) {
1081 case 'X509Certificate':
1082 $pemKey =
"-----BEGIN CERTIFICATE-----\n".
1083 chunk_split(
$key[
'X509Certificate'], 64).
1084 "-----END CERTIFICATE-----\n";
1091 $key =
new XMLSecurityKey(XMLSecurityKey::RSA_OAEP_MGF1P, array(
'type' =>
'public'));
1092 $key->loadKey($pemKey);
1094 throw new SimpleSAML_Error_ConfigurationError(
1095 'Missing encryption key for entity `' . $spMetadata->
getString(
'entityid') .
'`',
1097 $spMetadata->
getString(
'metadata-set') .
'.php' 1102 $ea = new \SAML2\EncryptedAssertion();
1103 $ea->setAssertion($assertion,
$key);
1127 $lr->setSessionIndex($association[
'saml:SessionIndex']);
1128 $lr->setNameId($association[
'saml:NameID']);
1163 $signResponse = $spMetadata->
getBoolean(
'saml20.sign.response', null);
1164 if ($signResponse === null) {
1165 $signResponse = $idpMetadata->
getBoolean(
'saml20.sign.response',
true);
1168 $r = new \SAML2\Response();
1170 $r->setIssuer($idpMetadata->
getString(
'entityid'));
1171 $r->setDestination($consumerURL);
1173 if ($signResponse) {
handleLogoutRequest(array &$state, $assocId)
Process a logout request.
const BINDING_HOK_SSO
The URN for the Holder-of-Key Web Browser SSO Profile binding.
static sendLogoutRequest(SimpleSAML_IdP $idp, array $association, $relayState)
Send a logout request to a given association.
getEndpoints($endpointType)
Helper function for dealing with metadata endpoints.
static generateID()
Generate a random identifier, ID_LENGTH bytes long.
handleAuthenticationRequest(array &$state)
Process authentication requests.
static validateMessage(SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata, \SAML2\Message $message)
Check signature on a SAML2 message if enabled.
const RESTART
The index in the state array which contains the restart URL.
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
static getByState(array &$state)
Retrieve the IdP "owning" the state.
static encodeAttributes(SimpleSAML_Configuration $idpMetadata, SimpleSAML_Configuration $spMetadata, array $attributes)
Helper function for encoding attributes.
getArray($name, $default=self::REQUIRED_OPTION)
This function retrieves an array configuration option.
log($default_level)
Print the exception to the log, by default with log level error.
foreach($paths as $path) $request
static processSOAPAuthnRequest(array &$state)
static getBinding($urn)
Retrieve a binding with the given URN.
$sc SubjectConfirmationData
const CM_HOK
Holder-of-Key subject confirmation method.
static getCurrentBinding()
Guess the current binding.
static getSecretSalt()
Retrieve the secret salt.
static handleAuthError(SimpleSAML_Error_Exception $exception, array $state)
Handle authentication error.
static buildLogoutRequest(SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata)
Build a logout request based on information in the metadata.
$metadata['__DYNAMIC:1__']
static encryptAssertion(SimpleSAML_Configuration $idpMetadata, SimpleSAML_Configuration $spMetadata, \SAML2\Assertion $assertion)
Encrypt an assertion.
static sendLogoutResponse(SimpleSAML_IdP $idp, array $state)
Send a logout response.
handleLogoutResponse($assocId, $relayState, SimpleSAML_Error_Exception $error=null)
Process a logout response.
static receiveAuthnRequest(SimpleSAML_IdP $idp)
Receive an authentication request.
static getModuleURL($resource, array $parameters=array())
Get absolute URL to a specified module resource.
static getAssertionConsumerService(array $supportedBindings, SimpleSAML_Configuration $spMetadata, $AssertionConsumerServiceURL, $ProtocolBinding, $AssertionConsumerServiceIndex)
Find SP AssertionConsumerService based on parameter in AuthnRequest.
static generateNameIdValue(SimpleSAML_Configuration $idpMetadata, SimpleSAML_Configuration $spMetadata, array &$state)
Calculate the NameID value that should be used.
if(!array_key_exists('stateid', $_REQUEST)) $state
Handle linkback() response from LinkedIn.
Attribute-related utility methods.
catch(Exception $e) $message
static getAssociationConfig(SimpleSAML_IdP $idp, array $association)
Retrieve the metadata for the given SP association.
getDefaultEndpoint($endpointType, array $bindings=null, $default=self::REQUIRED_OPTION)
Find the default endpoint of the given type.
static buildResponse(SimpleSAML_Configuration $idpMetadata, SimpleSAML_Configuration $spMetadata, $consumerURL)
Build a authentication response based on information in the metadata.
const CM_BEARER
Bearer subject confirmation method.
const EXCEPTION_HANDLER_FUNC
The index in the state array which contains the exception handler function.
static getLogoutURL(SimpleSAML_IdP $idp, array $association, $relayState)
Retrieve a logout URL for a given logout association.
getConfig()
Retrieve the configuration for this IdP.
const BINDING_PAOS
The URN for the PAOS binding.
const BINDING_HTTP_ARTIFACT
The URN for the HTTP-ARTIFACT binding.
static addSign(SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata, \SAML2\SignedElement $element)
Add signature key and sender certificate to an element (Message or Assertion).
getBoolean($name, $default=self::REQUIRED_OPTION)
This function retrieves a boolean configuration option.
if(array_key_exists('yes', $_REQUEST)) $attributes
static sendResponse(array $state)
Send a response to the SP.
static buildAssertion(SimpleSAML_Configuration $idpMetadata, SimpleSAML_Configuration $spMetadata, array &$state)
Build an assertion based on information in the metadata.
getId()
Retrieve the ID of this IdP.
const NAMEID_TRANSIENT
Transient NameID format.
static fromException(Exception $exception)
Create a SAML2 error from an exception.
static buildLogoutRequest(SimpleSAML_Configuration $idpMetadata, SimpleSAML_Configuration $spMetadata, array $association, $relayState)
Build a logout request based on information in the metadata.
getInteger($name, $default=self::REQUIRED_OPTION)
This function retrieves an integer configuration option.
static getEncryptionKey(SimpleSAML_Configuration $metadata)
Retrieve the encryption key for the given entity.
static getResponseError(\SAML2\StatusResponse $response)
Retrieve the status code of a response as a sspmod_saml_Error.
getPublicKeys($use=null, $required=false, $prefix='')
Get public key from metadata.
if(!isset($associations[$assocId])) $association
getString($name, $default=self::REQUIRED_OPTION)
This function retrieves a string configuration option.
static checkSessionCookie($retryURL=null)
Check for session cookie, and show missing-cookie page if it is missing.
if(!isset($_REQUEST['association'])) $assocId
hash(StreamInterface $stream, $algo, $rawOutput=false)
Calculate a hash of a Stream.
static receiveLogoutMessage(SimpleSAML_IdP $idp)
Receive a logout message.
static getAttributeNameFormat(SimpleSAML_Configuration $idpMetadata, SimpleSAML_Configuration $spMetadata)
Determine which NameFormat we should use for attributes.
static loadFromArray($config, $location='[ARRAY]', $instance=null)
Loads a configuration from the given array.
static getInstance($instancename='simplesaml')
Get a configuration file by its instance name.
static buildLogoutResponse(SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata)
Build a logout response based on information in the metadata.
static log($event, array $data=array())
Notify about an event.