diff options
author | Nicolas Clapies <nclapies@entrouvert.com> | 2006-12-20 12:53:25 +0000 |
---|---|---|
committer | Nicolas Clapies <nclapies@entrouvert.com> | 2006-12-20 12:53:25 +0000 |
commit | b13136af8a5e7e2e61ccd4e0a388f963b25f24d1 (patch) | |
tree | 258914b6c406c3e023fc92f9c1f5475afe285d0b /docs/lasso-book | |
parent | 13f707bf30acd26ae9c6fac5321d0accd47a5b90 (diff) | |
download | lasso-b13136af8a5e7e2e61ccd4e0a388f963b25f24d1.tar.gz lasso-b13136af8a5e7e2e61ccd4e0a388f963b25f24d1.tar.xz lasso-b13136af8a5e7e2e61ccd4e0a388f963b25f24d1.zip |
Added SAML2 PHP documentation. Need to complete with some Login and Logout details. Need to add description of Name Id Management profile.
Diffstat (limited to 'docs/lasso-book')
-rw-r--r-- | docs/lasso-book/Makefile.am | 6 | ||||
-rw-r--r-- | docs/lasso-book/writing-a-saml2-php-sp.txt | 415 |
2 files changed, 419 insertions, 2 deletions
diff --git a/docs/lasso-book/Makefile.am b/docs/lasso-book/Makefile.am index f887828b..33421f94 100644 --- a/docs/lasso-book/Makefile.am +++ b/docs/lasso-book/Makefile.am @@ -11,9 +11,11 @@ LASSOBOOK_FILES = book.rst common-knowledge.rst getting-lasso.rst \ liberty-architecture.rst other-profiles.rst preface.rst \ single-sign-on.rst -SP_DOC_FILES = writing-a-c-sp.txt writing-a-php-sp.txt writing-a-java-sp.txt +SP_DOC_FILES = writing-a-c-sp.txt writing-a-php-sp.txt writing-a-java-sp.txt \ + writing-a-saml2-php-sp.txt -SP_DOC_HTML = writing-a-c-sp.html writing-a-php-sp.html writing-a-java-sp.html +SP_DOC_HTML = writing-a-c-sp.html writing-a-php-sp.html writing-a-java-sp.html \ + writing-a-saml2-php-sp.html if HAVE_REST2HTML doc_DATA = $(SP_DOC_HTML) book.html diff --git a/docs/lasso-book/writing-a-saml2-php-sp.txt b/docs/lasso-book/writing-a-saml2-php-sp.txt new file mode 100644 index 00000000..fcb31167 --- /dev/null +++ b/docs/lasso-book/writing-a-saml2-php-sp.txt @@ -0,0 +1,415 @@ +========================================= +Writing a SAML2 service Provider in PHP +========================================= + +:author Nicolas Clapiès +:contact: nclapies@entrouvert.com +:date: $Date$ +:revision: $Revision$ +:copyright: Copyright © 2005, 2006 Entr'ouvert + +.. contents:: Table of Contents +.. section-numbering:: + + +Lasso PHP Binding Basics +======================== + +Lasso functions are available from the Lasso PHP extension. + +There are two ways to load this extension. + +This first one is to add the line:: + + extension = lasso.so + +in your ``php.ini`` configuration file, which can be found in something like +``/etc/php4/apache2/php.ini`` (if you're using apache2 and php4 on Debian, +otherwise you may need to adapt the path to your local configuration). + + +The other way is to load it dynamically, like:: + + if (!extension_loaded('lasso')) { + $prefix = (PHP_SHLIB_SUFFIX == 'dll') ? 'php_' : ''; + dl($prefix . 'lasso.' . PHP_SHLIB_SUFFIX); + } + +You can easily include this code every time you need lasso. + + +The first thing to do is to call ``lasso_init()``. Similarly, the last thing +should be to call ``lasso_shutdown()``. + +Once ``lasso_init()`` is called. The smallest and useless Lasso project will +therefore be:: + + lasso_init(); + print("Hello world.\n"); + lasso_shutdown(); + +If your PHP code is used in HTML script environment, it could be difficult to +call ``lasso_shutdown()``, this is not mandatory. + + +SAML2 and Lasso profiles +======================== + +Lasso provides the necessary functions to implement SAML2 profiles, +as defined in the `SAML2 Profiles Specification`_. They +are: + +- Single Sign-On +- Single Logout +- Name Identifier Management + +Each profile maps to a Lasso object : Single Sign-on profile is implemented by +``LassoLogin`` object, Single Logout profile is implemented by ``LassoLogout`` +object. + +Those are initialised with data known about identity and service providers, +available in a ``LassoServer`` object. + +The ``LassoServer`` object may be created as follows:: + + lasso_init(); + $server = new LassoServer("sp-metadata.xml", "sp-private-key.pem", + NULL, "sp-crt.pem"); + $server->addProvider(LASSO_PROVIDER_ROLE_IDP, "idp-metadata.xml", + "idp-public-key.pem", "ca-crt.pem"); + lasso_shutdown(); + +- ``sp-metadata.xml`` is the Liberty metadata file for the service provider +- ``idp-metadata.xml`` is the Liberty metadata file for the identity provider +- ``sp-private-key.pem`` is the service provider private key; used to sign + documents +- ``sp-crt.pem`` is the service provider certificate; sent inside signed + documents +- ``idp-public-key.pem`` is the identity provider public key; used to verify + signature in documents sent by the identity provider +- ``ca-crt.pem`` is the certificate of the certification authority used by the + identity provider. + +It is of course possible to have several calls to the ``addProvider`` method of +an instantiated ``LassoServer`` object if there are more than one identity provider. + +.. note:: Figures in the previously referred Binding and Profiles specification + document are quite helpful in figuring out the message passing. + +Serialisation +------------- + +``LassoServer`` objects can be serialised into a XML formatted string:: + + $dump = $server->dump(); + +It is then really easy to get back properly constructed objects:: + + $server = LassoServer::newFromDump($dump); + +.. warning:: The server dump only contains the file names, not the actual file + contents. Files should not be moved afterwards. + + +Liberty Metadata Files +====================== + +Providers are described by metadata containing an ``entityID`` and various +normative URLs for supported profiles :: + + <?xml version="1.0"?> + <EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" + xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" + xmlns:ds="http://www.w3.org/2000/09/xmldsig#" + entityID="http://sp.example.com/liberty/metadata"> + + <SPSSODescriptor + AuthnRequestsSigned="true" + protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> + + <KeyDescriptor use="signing"> + <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> + <ds:KeyValue></ds:KeyValue> + </ds:KeyInfo> + </KeyDescriptor> + + <SingleLogoutService + Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" + Location="https://sp.example.com/liberty/singleLogoutSoap" /> + + <ManageNameIDService + Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" + Location="https://sp.example.com/liberty/nameIdManagmentSoap" /> + + <AssertionConsumerService index="0" isDefault="true" + Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" + Location="https://sp.example.com/liberty/assertionConsumerServiceArtifact" /> + + </SPSSODescriptor> + + <Organization> + <OrganizationName xml:lang="en"></OrganizationName> + </Organization> + + </EntityDescriptor> + +``SingleLogoutService`` entry allows to define method and url supported where +service provider wants to receive single logout requests. In previous metadata +example, service provider receives SOAP single logout requests on the +url ``https://sp.example.com/liberty/singleLogoutSoap``. + +``ManageNameIDService`` entry allows to define method and url supported where +service provider wants to receive name id management requests. In previous metadata +example, service provider receives SOAP name id management requests on the +url ``http://sp.example.com/liberty/nameIdManagmentSoap``. + +``AssertionConsumerService`` entry allows to define method and url supported where +service provider wants to receive assertion consumer responses. In previous metadata +example, service provider consume assertions responses on the +url ``https://sp.example.com/liberty/assertionConsumerServicePost``. + + +Single Sign-On and Federation Profile +===================================== + +.. warning:: The source code presented in this section has for sole purpose + to explain the different steps necessary to implement this + profile; they notably lack proper error checking. See `Proper + Error Checking`_ for details on error checking. + + +As a first step the user points its browser to the service provider to the +login URL; the service provider must then respond with an HTTP 302 Redirect +response, pointing the user browser to the identity provider single sign on +service. + +.. note:: the login URL is not normative; any name will do. + + +``$server`` is a instantiated ``LassoServer`` as seen earlier and +``$idpEntityId`` is a string with the idp entity Id (the string must +match a entityID defined in the metadata file). + +:: + + + + $entityIdList = $server->providerIds; + $idpEntityId = $entityIdList->getItem(0); + + $lassoLogin = new LassoLogin($server); + $lassoLogin->initAuthnRequest($idpEntityId, LASSO_HTTP_METHOD_REDIRECT); + + $lassoRequest = $lassoLogin->request; + $lassoRequest->ProtocolBinding = LASSO_SAML2_METADATA_BINDING_POST; + + $lassoNameIdPolicy = $lassoRequest->NameIDPolicy; + $lassoNameIdPolicy->Format = LASSO_SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT; + $lassoNameIdPolicy->AllowCreate = TRUE; + + $lassoLogin->buildAuthnRequestMsg(); + + +You can now redirect the user to the URL defined in ``$lassoLogin->msgUrl``; for +example:: + + header("Location: ".$lassoLogin->msgUrl); + + +The user then logs in on the identity provider which ultimately redirects back +to the service provider; to the assertion consumer URL. A SAML artifact is +passed in the query parameter. + +.. note:: the assertion consumer URL is defined by Liberty; it must be declared + in the ``AssertionConsumerServiceURL`` element of the metadata file. + +:: + + $lassoLogin = new LassoLogin($server); + $lassoLogin->initRequest($query_string, LASSO_HTTP_METHOD_ARTIFACT_GET); + $lassoLogin->buildRequestMsg(); + +The service provider must check this artifact using a SOAP request to the +identity provider. The URL is ``$lassoLogin->msgUrl`` while the +request is ``$lassoLogin->msgBody``. The request must succeed with +an HTTP 200 status code; let's consider its content is put in the ``$answer``, +the next statement would be:: + + $lassoLogin->processResponseMsg($answer); + +The users are defined by a ``nameIdentifier`` (accessible through +``$lassoLogin->nameIdentifier``). Those typically map to users +and sessions in some database on the service provider. If existing; the +session should probably contains a ``session_dump`` element and the user a +``identity_dump`` element. See `Database Considerations`_ below for more +information. + +It is now time to get them out of the database and apply them to the ``login`` +object. + +:: + + if ($session_dump != NULL) { + $lassoLogin->setSessionFromDump($session_dump); + } + if ($identity_dump != NULL) { + $lassoLogin->setIdentityFromDump($identity_dump); + } + $lassoLogin->acceptSso(); + +After ``lassoLogin->acceptSso()`` the session and the identity are updated (or +created) and should then be saved. If the identity has not recognised by the +service provider an account will probably have to be created on the service +provider; this is a good opportunity to ask the user for more information. + +You can get respective dumps like this:: + + if ($lassoLogin->isIdentityDirty) { + $lassoIdentity = $lassoLogin->identity; + $lassoIdentityDump = $lassoIdentity->dump(); + } + + if ($lassoLogin->isSessionDirty) { + $lassoSession = $lassoLogin->session; + $lassoSessionDump = $lassoSession->dump(); + } + + /* code to store $identity_dump and $session_dump */ + + +A success web page can be displayed. + + +Single Logout Profile +===================== + +There are different single logout profiles; some initiated on the identity +provider, others initiated on the service provider, using either HTTP redirects +or SOAP requests. + +This part is about a logout using SOAP and initiated on the service provider. + +:: + + $lassoLogout = new LassoLogout($server); + + +Identity and session dumps should be restored to prepare the logout request. + +:: + + if ($session_dump != NULL) { + $lassoLogout->setSessionFromDump($session_dump); + } + if ($identity_dump != NULL) { + $lassoLogout->setIdentityFromDump($identity_dump); + } + + $lassoLogout->initRequest($idpProviderId, LASSO_HTTP_METHOD_SOAP); + $lassoLogout->buildRequestMsg(); + + +The service provider must then make a SOAP request to the identity provider; +``$msgUrl`` and ``$msgBody``. You should then pass the answer to Lasso:: + + $lassoLogout->processResponseMsg($answer)); + +And save back session and user dump; the process is similar as the one at the +end of the single sign on profile. + + +Proper Error Checking +===================== + +Most Lasso functions raise PHP error (fatal) or warning (non-fatal). + +It is strongly advised to code an user error handler:: + + function userErrorHandler($errno, $errmsg, $filename, $linenum, $vars) { + print("No: ".$errno." - ".$errmsg." at ".$filename.", line: ".$linenum."\n"); + } + +and to set up the script to use it:: + + set_error_handler("userErrorHandler"); + +Most Lasso functions returns 0 on success and a negative number on failure. It +is strongly advised to check this return code on each call. If the error raise a +PHP warning, the code resume after the call to the error handler function. + +:: + + $lrv = $lassoLogin->processResponseMsg($responseMsg); + if ($lrv > 0) { + print("Lasso Error: ".$lrv."\n"); + /* handling error; most probably bailing out */ + } + + +Database Considerations +======================= + +Lasso has been designed to let the service provider keep on using existing +databases. Typically there is already a table describing users; just add an +identity dump column to the existing table: + +======= ======================================== ============== +User Id existing data (name, address...) Identity dump +======= ======================================== ============== +1 ... <Identity> ... +2 ... <Identity> ... +======= ======================================== ============== + +Mapping between existing users and name identifiers sent by the identity +provider can be done with a simple table. + +=============== ======= +Name Identifier User Id +=============== ======= +AQWWRRS... 1 +CGFASDE... 2 +YYSSSDS... 1 +=============== ======= + +.. note:: A separate table is needed because one user Id could map + to several name identifiers; in case there are several identity + providers. + +Sessions are also commonly stored in databases; just add a session dump column +to the existing session table: + +========== ================= ============= +Session Id misc session data Session dump +========== ================= ============= +6744066 ... <Session> ... +3338824 ... <Session> ... +========== ================= ============= + +Likewise sessions should be mapped to name identifiers. + +=============== ========== +Name Identifier Session Id +=============== ========== +AQWWRRS... 3338824 +=============== ========== + + +API Reference +============= + +- LassoLogin_ +- LassoLogout_ +- LassoIdentity_ +- LassoServer_ +- LassoSession_ + + +.. _SAML2 Profiles Specification: + http://docs.oasis-open.org/security/saml/v2.0/saml-profiles-2.0-os.pdf + +.. _LassoLogin: /documentation/api-reference/lassologin.html +.. _LassoLogout: /documentation/api-reference/lassologout.html +.. _LassoIdentity: /documentation/api-reference/lassoidentity.html +.. _LassoServer: /documentation/api-reference/lassoserver.html +.. _LassoSession: /documentation/api-reference/lassosession.html + |