miércoles, 5 de marzo de 2014

WSO2 como proveedor de SSO en la práctica usando el Identity Server


Todos aquellos que han podido experimentar con la herramienta WSO2 Identity Server saben que esta puede actuar como un servidor de Single Sign On bien usando OpenID o SAML.

Para resumir, el SSO nos permite acceder usando una sola cuenta a diferentes sistemas autenticándonos solo en uno de ellos, el resto se comunica con un servidor de SSO que les provee las credenciales que hayamos introducido anteriormente o les transfiere un token mediante el cual es confirma que nos hemos autenticado previamente.

De esta forma se evita  que los usuarios tengan una cuenta por sistema y que se tengan que autenticar en cada sistema al que deseen entrar.

Claro que previamente todos estos sistemas deben haberse configurado para lograr este escenario y es lo que veremos en esta entrada.

En lo personal había probado el Identity Server de WSO2 para establecer SSO entre los mismos servidores de la suite y en una PoC para aplicaciones web en JAVA, pero no había probado hasta el momento con aplicaciones web en PHP. Hasta hace poco usaba el CAS para montar escenarios de SSO.

El escenario es el siguiente:
  • Se tiene una aplicación web en JAVA desplegada en un servidor tomcat y se tiene una aplicación web en PHP desplegada en un servidor Apache.
  • Se desea proporcionar un ambiente de SSO para que los usuarios una vez autenticados en cualquiera de las 2 aplicaciones tengan acceso a la otra sin tener que volverse a autenticar.

Para aplicaciones web en JAVA solo basta con seguir los pasos de este ejemplo
Lo principal a tener en cuenta es:

Tener en el pom de nuestra webapp la siguiente dependencia:

    <dependencies>
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.identity.sso.agent</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>

Y en el web.xml el siguiente filtro:
    <filter>
        <filter-name>SSOFilter</filter-name>
        <filter-class>org.wso2.carbon.identity.sso.agent.SSOAgentFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>SSOFilter</filter-name>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>/samlsso</url-pattern>
        <url-pattern>/openid</url-pattern>
        <url-pattern>/logout</url-pattern>
    </filter-mapping>

Por último deben configurar bien el fichero de propiedades para que la aplicación pueda redireccionar hacia donde tengan ubicado el WSO2 IS y se pueda establecer la configuración.

Realmente los pasos son muy sencillos y no dan motivo de pérdida.

Les dejo una imagen de la configuración hecha en el WSO2 IS para esta aplicación:


Luego de tener la webapp en JAVA funcionando me decidí a implementar lo mismo para PHP.

Estuve revisando algunas implementaciones para SAML y finalmente me opté por  ONELOGIN http://support.onelogin.com/entries/268420-saml-toolkit-for-php
Debo reconocer que de PHP no veía nada desde hace ya 9 años y que la elección fue sin muchos criterios a tener en cuenta, pero el uso de la aplicación demo fue bastante sencillo.

Descargue de github la herramienta https://codeload.github.com/onelogin/php-saml/zip/master que dentro tiene un demo.

Instalé WAMP, para así tener mi servidor APACHE con PHP y copié para la carpeta www la aplicación de ONELOGIN luego de descompactarlo.

Los cambios que hice fueron inicialmente en el fichero settings.php que queda como sigue para mi escenario:
<?php
/**
 * SAMPLE Code to demonstrate how provide SAML settings.
 *
 * The settings are contained within a OneLogin_Saml_Settings object. You need to
 * provide, at a minimum, the following things:
 *
 *  - idpSingleSignOnUrl
 *    This is the URL to forward to for auth requests.
 *    It will be provided by your IdP.
 *
 *  - idpPublicCertificate
 *    This is a certificate required to authenticate your request.
 *    This certificate should be provided by your IdP.
 * 
 *  - spReturnUrl
 *    The URL that the IdP should redirect to once the authorization is complete.
 *    You must provide this, and it should point to the consume.php script or its equivalent.
 */

define('XMLSECLIBS_DIR', './../ext/xmlseclibs/');
require_once XMLSECLIBS_DIR . 'xmlseclibs.php';

define('ONELOGIN_SAML_DIR', './../src/OneLogin/Saml/');
require_once ONELOGIN_SAML_DIR . 'AuthRequest.php';
require_once ONELOGIN_SAML_DIR . 'Response.php';
require_once ONELOGIN_SAML_DIR . 'Settings.php';
require_once ONELOGIN_SAML_DIR . 'XmlSec.php';

$settings = new OneLogin_Saml_Settings();

// When using Service Provider Initiated SSO (starting at index.php), this URL asks the IdP to authenticate the user.
//$settings->idpSingleSignOnUrl = 'https://app.onelogin.com/saml/signon/6171';
$settings->idpSingleSignOnUrl = 'https://localhost:9443/samlsso';

// The certificate for the users account in the IdP
$settings->idpPublicCertificate = <<<CERTIFICATE
-----BEGIN CERTIFICATE-----
MIICNTCCAZ6gAwIBAgIES343gjANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJVUzELMAkGA1UE
CAwCQ0ExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxDTALBgNVBAoMBFdTTzIxEjAQBgNVBAMMCWxv
Y2FsaG9zdDAeFw0xMDAyMTkwNzAyMjZaFw0zNTAyMTMwNzAyMjZaMFUxCzAJBgNVBAYTAlVTMQsw
CQYDVQQIDAJDQTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzENMAsGA1UECgwEV1NPMjESMBAGA1UE
AwwJbG9jYWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCUp/oV1vWc8/TkQSiAvTou
sMzOM4asB2iltr2QKozni5aVFu818MpOLZIr8LMnTzWllJvvaA5RAAdpbECb+48FjbBe0hseUdN5
HpwvnH/DW8ZccGvk53I6Orq7hLCv1ZHtuOCokghz/ATrhyPq+QktMfXnRS4HrKGJTzxaCcU7OQID
AQABoxIwEDAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADgYEAW5wPR7cr1LAdq+IrR44i
QlRG5ITCZXY9hI0PygLP2rHANh+PYfTmxbuOnykNGyhM6FjFLbW2uZHQTY1jMrPprjOrmyK5sjJR
O4d1DeGHT/YnIjs9JogRKv4XHECwLtIVdAbIdWHEtVZJyMSktcyysFcvuhPQK8Qc/E/Wq8uHSCo=
-----END CERTIFICATE-----
CERTIFICATE;

// The URL where to the SAML Response/SAML Assertion will be posted
$settings->spReturnUrl = 'http://localhost/php-saml/demo/consume.php';

// Name of this application
$settings->spIssuer = 'php-saml';

// Mio
$settings->destination = 'https://localhost:9443/samlsso';

// Tells the IdP to return the email address of the current user
$settings->requestedNameIdFormat = OneLogin_Saml_Settings::NAMEID_EMAIL_ADDRESS;

return $settings;

Lo siguiente que hice fue ir al WSO2 IS y configurar esta aplicación web de la misma forma en que ya había configurado la aplicación para JAVA, cambiando claro los nombres de algunos campos.

Al intentar autenticarme en la aplicación PHP la redirección funcionó sin problemas, el usuario se autenticó en el IS pero me dio un error en la misma página de autenticación del WSO2 IS.
El error era que me faltaba un atributo en el SAML2 request que estaba enviando la aplicación web al WSO2 IS. En StackOverflow pueden ver mi pregunta y los avances que hice hasta llegar a la solución http://stackoverflow.com/questions/22182354/sso-for-php-webapp-with-wso2-identity-server-authentication-request-failed/

Finalmente cuando pude pasar este error me dio otro relacionado con la validación de la firma y que pueden ver al final del enlace anterior. Para evitarlo tuve que desmarcar la opción de validación y por eso mi configuración para la webapp en PHP dentro del WSO2 IS queda de la siguiente manera:


Para finalizar probé autenticarme en una aplicación y luego acceder a la otra, pude comprobar que no me pedía autenticación. Que era lo que estaba buscando.

Como elementos pendientes quedan:
  • Obtener atributos de los usuarios desde el IS para propagarlos hacia las aplicaciones web.
  • Configurar el single sing out en la aplicación web en PHP y probar este mecanismo. La idea es que si me des autentico de una aplicación lo mismo debe pasar en el resto.
En otras entradas estaremos viendo ambos elementos.
Espero les sea de utilidad.

0 comentarios:

Publicar un comentario