================================================ Writing a Liberty Alliance service provider in C ================================================ :Author: Frederic Peters :Contact: fpeters@entrouvert.com :date: $Date$ :revision: $Revision$ :copyright: Copyright © 2004 Entr'ouvert Introduction to Lasso profiles ============================== .. warning:: The source code presented in this document has for sole purpose to explain the different steps necessary to implement Liberty Alliance profiles; they notably lack proper error checking. Lasso provides the necessary functions to implement Liberty Alliance profiles, as defined in the `Liberty ID-FF Bindings and Profiles Specification`_. They are: - Single Sign-On and Federation - Name Registration - Federation Termination Notification - Single Logout - Identity Provider Introduction - Name Identifier Mapping - Name Identifier Encryption Each profile maps to a Lasso object such as ``LassoLogin``, ``LassoLogout``... Those are initialized with data known about identity and service providers, available in a ``LassoServer`` object. The ``LassoServer`` object may be created as follows: :: LassoServer *server; server = lasso_server_new("sp-metadata.xml", NULL, "sp-private-key.pem", "sp-crt.pem"); lasso_server_add_provider(server, "idp-metadata.xml", "idp-public-key.pem", "ca-crt.pem"); - ``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 so ``lasso_server_add_provider`` if there are more than one identity provider. Single Sign-On and Federation Profile ===================================== .. note:: It may be helpful to look at figure 2 in the previously referred Binding and Profiles specification document. 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. ``server`` is a ``LassoServer`` and ``idpProviderId`` is a string with the identity provider Id (defined in metadata). :: LassoLogin *login; login = lasso_login_new(server); lasso_login_init_authn_request(login); lasso_lib_authn_request_set_forceAuthn( LASSO_LIB_AUTHN_REQUEST(login->request), 1); lasso_lib_authn_request_set_nameIDPolicy( LASSO_LIB_AUTHN_REQUEST(login->request), lassoLibNameIDPolicyTypeFederated); lasso_lib_authn_response_set_consent( LASSO_LIB_AUTHN_REQUEST(login->request), lassoLibConsentObtained); lasso_login_build_authn_request_msg(idpProviderId); You can now redirect the user to the URL defined in ``login->msg_url``; for example, in a CGI:: printf("Location: %s\n", login->msg_url); 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. :: LassoLogin *login; login = lasso_login_new(server); login_init_request(login, query_string, lassoHttpMethodRedirect); login_build_request_msg(login); The service provider must check this artifact using a SOAP request to the identity provider. The URL is ``login->msg_url`` while the request is ``login->msg_body``. 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:: login_process_response_msg(login, answer); The users are defined by a ``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 informations. It is now time to get them out of the database and apply them to the ``login`` object. :: if (session_dump != NULL) { login_set_session_from_dump(login, session_dump); } if (identity_dump != NULL) { login_set_identity_from_dump(login, identity_dump); } lasso_login_accept_sso(login); After ``lasso_login_accept_sso`` the session and the identity are updated (or created) and should then be saved. If the identity was not known to the service provider an account will most probably need 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:: LassoIdentity *identity; LassoSession *session; char *identity_dump = NULL, *session_dump = NULL; if (lasso_profile_is_identity_dirty(LASSO_PROFILE(login))) { identity = lasso_profile_get_identity(LASSO_PROFILE(login)); identity_dump = lasso_identity_dump(identity); lasso_identity_destroy(identity); } if (lasso_profile_is_session_dirty(LASSO_PROFILE(login))) { session = lasso_profile_get_session(LASSO_PROFILE(login)); session_dump = lasso_session_dump(session); lasso_session_destroy(session); } /* code to store identity_dump and session_dump */ Finally the ``login`` object can then be destroyed:: lasso_login_destroy(login); And a success web page 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 *logout; logout = lasso_logout_new(lassoServer, lassoProviderTypeSp); Identity and session dumps should be restored to prepare the logout request. :: if (session_dump != NULL) { login_set_session_from_dump(login, session_dump); } if (identity_dump != NULL) { login_set_identity_from_dump(login, identity_dump); } logout_init_request(logout); logout_build_request_msg(logout); The service provider must then make a SOAP request to the identity provider; ``msg_url`` and ``msg_body``. You should then pass the answer to Lasso:: logout_process_response_msg(logout, answer, lassoHttpMethodSoap) And save back session and user dump; the process is similar as the one at the end of the single sign on profile. 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 a identity dump column to the existing table: ======= ======================================== ============== User Id existing data (name, address...) Identity dump ======= ======================================== ============== 1 ... ... 2 ... ... ======= ======================================== ============== 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 ... ... 3338824 ... ... ========== ================= ============= Like users; sessions should be mapped to name identifiers. =============== ========== Name Identifier Session Id =============== ========== AQWWRRS... 3338824 =============== ========== .. _Liberty ID-FF Bindings and Profiles Specification: http://www.projectliberty.org/specs/draft-liberty-idff-bindings-profiles-1.2-errata-v1.0.pdf