Mostrando entradas con la etiqueta SSO. Mostrar todas las entradas
Mostrando entradas con la etiqueta SSO. Mostrar todas las entradas

miércoles, 6 de enero de 2016

WSO2 SSO: IS + DAS.

Hace poco un cliente me pedía revisara su configuración de SSO brindada por el WSO2 Identity Server 5.1.0 pues no le funcionaba al usar el WSO2 DAS 3.0.0.

El error se puede apreciar en la siguiente imagen:
image_thumb1
En mi ambiente los offset de las herramientas son los siguientes:
  • WSO2 DAS: 0
  • WSO2 IS: 5
La configuración del WSO2 DAS en el fichero authenticators.xml relacionada con el SSO es la siguiente:

<Authenticator name="SAML2SSOAuthenticator" disabled="false">
 <Priority>10</Priority>
 <Config>
   <Parameter name="LoginPage">/carbon/admin/login.jsp</Parameter>
   <Parameter name="ServiceProviderID">carbonServerDAS</Parameter>
   <Parameter name="IdentityProviderSSOServiceURL">https://localhost:9448/samlsso</Parameter>
   <Parameter name="NameIDPolicyFormat">urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</Parameter>
   <Parameter name="AssertionConsumerServiceURL">https://localhost:9443/acs</Parameter>

          <!-- <Parameter name="IdPCertAlias">wso2carbon</Parameter> -->
   <!-- <Parameter name="ResponseSignatureValidationEnabled">false</Parameter> -->
   <!-- <Parameter name="LoginAttributeName"></Parameter> -->
   <!-- <Parameter name="RoleClaimAttribute"></Parameter> -->
   <!-- <Parameter name="AttributeValueSeparator">,</Parameter> -->

          <!-- <Parameter name="JITUserProvisioning">true</Parameter> -->
   <!-- <Parameter name="ProvisioningDefaultUserstore">PRIMARY</Parameter> -->
   <!-- <Parameter name="ProvisioningDefaultRole">admin</Parameter> -->
   <!-- <Parameter name="IsSuperAdminRoleRequired">true</Parameter> -->
        </Config>

<!-- If this authenticator should skip any URI from authentication, specify it under "SkipAuthentication"
<SkipAuthentication>
  <UrlContains></UrlContains>
 </SkipAuthentication> -->

<!-- If this authenticator should skip any URI from session validation, specify it under "SkipAuthentication
 <SkipSessionValidation>
  <UrlContains></UrlContains>
 </SkipSessionValidation> -->
</Authenticator>

Como ven se define el service provider ID, se ajusta el puerto del WSO2 IS a 9448 y el puerto del DAS a 9443 y eso es todo.

En el WSO2 IS, en mi caso estaba usando la versión 5.0.0, no era la misma del cliente, y los ajustes fueron los siguientes:

Me cree un service provider: carbonServerDAS

image_thumb4 

image_thumb6

Y su configuración fue la siguiente:

image_thumb9

Al probar el SSO me funcionaba perfectamente, mientras que al cliente no, así que asumí era un problema de la versión del WSO2 IS y su configuración.

Levanté el WSO2 IS 5.1.0 creando un service provider tal como se muestra a continuación:

image_thumb11
image_thumb13

  Las opciones marcadas fueron las siguientes junto con la definición del endpoint del service provider:

image_thumb16

Luego de esta configuración al tratar de autenticarme en el WSO2 DAS pude reproducir el error.
La solución dada la pueden ver en esta pregunta de stackoverflow.

Espero les sea de utilidad.

viernes, 7 de marzo de 2014

Como ya habíamos visto en la entrada anterior, el WSO2 Identity Server tiene entre sus múltiples funcionalidades relacionadas con la seguridad la de actuar como un Identity Provider o IDP.
Se había dejado como pendiente la captura de los atributos contenidos en el almacén de usuarios que estuviera usando el  WSO2 Identity Server y eso será lo que haremos en esta entrada.

El escenario:
  • Digamos que tenemos inicialmente el WSO2 configurado para que use su BD interna de los usuarios, pero queremos que también use un LDAP como almacén de usuarios, pueden usar este enlace para configurar su LDAP y de esta manera se podrán autenticar en las aplicaciones usando los usuarios del LDAP.
  • El Identity server tiene un conjunto de usuarios, los cuales tienen atributos definidos a través de la funcionalidad “My Profile” y el LDAP contiene información de los usuarios la cual depende de la implementación de cada organización y la información que se desee guardar en el LDAP.
  • Se desea que las aplicaciones que participen en el escenario de Single Sign On usando el WSO2 Identity Server puedan obtener los atributos requeridos del usuario autenticado en ellas, bien sea desde el almacén por defecto de la herramienta o bien desde el LDAP.

Veamos cómo se hace:

Lo primero es ir al WSO2 Identity Server y editar la configuración del SSO para la aplicación que requiere dichos atributos. Noten el valor del “Consumer Index” pues luego hará falta.


Cuando entramos en  su configuración debemos marcar la opción que dice “Enable Attribute Profile”:






Y en la parte de Claim seleccionamos aquellas que se correspondan con los atributos que sabemos están en el LDAP y le damos al botón “Add Claim” y así veremos cómo se van agregando los atributos.

NOTA: El WSO2 Identity Server también tiene la posibilidad de gestionar estos atributos, lo cual es muy útil para saber que Claims se corresponden con qué atributos en el LDAP o para agregar nuevas en caso de ser requerido. En el caso del rol y de la URL tuve que realizar ajustes pues no se correspondían con el esquema de atributos soportados por el LDAP.

Cuando nos volvamos a autenticar en la aplicación que usamos de ejemplo veremos entonces los valores de  los atributos.

Mi primera prueba fue con un usuario interno, en este caso el admin de la herramienta y tuve que llenar en “My Profile” los datos que se querían mostrar.



Luego al volver a autenticarme con las credenciales del LDAP se cargaron mis atributos sin problema desde el LDAP. Aquí aconsejo revisar el LDAP de cada cual y ver los atributos que tienen sus usuarios, luego revisar los claims que tengan y ver si tienen para obtener esos atributos. En caso de que no los tengan bien pueden modificar los existentes o agregar nuevos.

Para capturar estos datos se usó la siguiente página JSP:


En el caso de la aplicación PHP el escenario es bastante similar. Hay que editar la configuración del SSO para dicha aplicación y seleccionar los atributos que queremos obtener.

Al revisar el código de la aplicación demo vemos que también tiene implementada la captura de los atributos, como se puede ver en la siguiente imagen:




Pero al probarla no se mostraba nada. Así que volví a repetir el mismo procedimiento de la entrada anterior y comparar los mensajes SAML intercambiados, y resulta que mientras la aplicación JAVA está enviando un atributo AttributeConsumingServiceIndex con el identificador que se muestra en su configuración de SSO en el WSO2 Identity Server, la aplicación web en PHP no lo hacía, por lo que tuve que modificar el código.

En el fichero Settings.php agregué un nuevo atributo AttributeConsumingServiceIndex a la clase OneLogin_Saml_Settings tal y como se muestra en esta imagen:




Luego  en el fichero settings.php le seteo el valor correspondiente:


Y por último en el fichero AuthRequest.php modifico la estructura del mensaje SAML a enviar para que incluya este atributo:



        $request = <<<AUTHNREQUEST
<samlp:AuthnRequest
    xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
    ID="$id"
    Version="2.0"
    IssueInstant="$issueInstant"
 Destination="{$this->_settings->idpSingleSignOnUrl}"
    ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
    AssertionConsumerServiceURL="{$this->_settings->spReturnUrl}"
 AttributeConsumingServiceIndex="{$this->_settings->AttributeConsumingServiceIndex}">
    <saml:Issuer>{$this->_settings->spIssuer}</saml:Issuer>
    <samlp:NameIDPolicy
        Format="{$this->_settings->requestedNameIdFormat}"
        AllowCreate="true"></samlp:NameIDPolicy>
    <samlp:RequestedAuthnContext Comparison="exact">
        <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
    </samlp:RequestedAuthnContext>
</samlp:AuthnRequest>
AUTHNREQUEST;


De esta manera al tratar de acceder a la aplicación php vemos como el mensaje llega al WSO2 Identity Server con el atributo requerido y también podemos ver como el mensaje SAML de respuesta contiene los valores de los atributos solicitados, pero llegado este punto tampoco se muestran en la página web. De nuevo a revisar código PHP  :-(


Aquí les dejo un fragmento del mensaje obtenido desde el WSO2 Identity Server, es solo un fragmento pues el mensaje es bastante grande:
<saml2:AttributeStatement>
<saml2:Attribute Name="http://wso2.org/claims/country"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Cuba</saml2:AttributeValue></saml2:Attribute>
<saml2:Attribute Name="http://wso2.org/claims/mobile"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">+5353722213</saml2:AttributeValue></saml2:Attribute>
<saml2:Attribute Name="http://wso2.org/claims/role"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">admin,Internal/everyone</saml2:AttributeValue></saml2:Attribute>
<saml2:Attribute Name="http://wso2.org/claims/url"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">http://desarrollosoa.blogspot.com/</saml2:AttributeValue></saml2:Attribute>
</saml2:AttributeStatement>



Luego de revisar el código php noté que había un problema con el fichero Response.php que contiene la clase OneLogin_Saml_Response y es que esta clase no capturaba los datos de los mensajes SAML2, solo los SAML, así que la modifiqué de esta manera.

<?php

/**
 * Parse the SAML response and maintain the XML for it.
 */
class OneLogin_Saml_Response
{
    /**
     * @var OneLogin_Saml_Settings
     */
    protected $_settings;

    /**
     * The decoded, unprocessed XML assertion provided to the constructor.
     * @var string
     */
    public $assertion;

    /**
     * A DOMDocument class loaded from the $assertion.
     * @var DomDocument
     */
    public $document;

    /**
     * Construct the response object.
     *
     * @param OneLogin_Saml_Settings $settings Settings containing the necessary X.509 certificate to decode the XML.
     * @param string $assertion A UUEncoded SAML assertion from the IdP.
     */
    public function __construct(OneLogin_Saml_Settings $settings, $assertion)
    {
        $this->_settings = $settings;
        $this->assertion = base64_decode($assertion);
        $this->document = new DOMDocument();
        $this->document->loadXML($this->assertion);
    }

    /**
     * Determine if the SAML Response is valid using the certificate.
     *
     * @throws Exception
     * @return bool Validate the document
     */
    public function isValid()
    {
        $xmlSec = new OneLogin_Saml_XmlSec($this->_settings, $this);
        return $xmlSec->isValid();
    }

    /**
     * Get the NameID provided by the SAML response from the IdP.
     */
    public function getNameId()
    {
        $entries = $this->_queryAssertion('/saml2:Subject/saml2:NameID');
        return $entries->item(0)->nodeValue;
    }

    /**
     * Get the SessionNotOnOrAfter attribute, as Unix Epoc, from the
     * AuthnStatement element.
     * Using this attribute, the IdP suggests the local session expiration
     * time.
     * 
     * @return The SessionNotOnOrAfter as unix epoc or NULL if not present
     */
    public function getSessionNotOnOrAfter()
    {
        $entries = $this->_queryAssertion('/saml2:AuthnStatement[@SessionNotOnOrAfter]');
        if ($entries->length == 0) {
            return NULL;
        }
        $notOnOrAfter = $entries->item(0)->getAttribute('SessionNotOnOrAfter');
        return strtotime($notOnOrAfter);
    }

    public function getAttributes()
    {
        $entries = $this->_queryAssertion('/saml2:AttributeStatement/saml2:Attribute');

        $attributes = array();
        /** @var $entry DOMNode */
        foreach ($entries as $entry) {
            $attributeName = $entry->attributes->getNamedItem('Name')->nodeValue;

            $attributeValues = array();
            foreach ($entry->childNodes as $childNode) {
                if ($childNode->nodeType == XML_ELEMENT_NODE && $childNode->tagName === 'saml2:AttributeValue'){
                    $attributeValues[] = $childNode->nodeValue;
                }
            }

            $attributes[$attributeName] = $attributeValues;
        }
        return $attributes;
    }

    /**
     * @param string $assertionXpath
     * @return DOMNodeList
     */
    protected function _queryAssertion($assertionXpath)
    {
        $xpath = new DOMXPath($this->document);
        $xpath->registerNamespace('samlp'   , 'urn:oasis:names:tc:SAML:2.0:protocol');
        $xpath->registerNamespace('saml'    , 'urn:oasis:names:tc:SAML:2.0:assertion');
        $xpath->registerNamespace('ds'      , 'http://www.w3.org/2000/09/xmldsig#');
  $xpath->registerNamespace('saml2'   , 'urn:oasis:names:tc:SAML:2.0:assertion');

        $signatureQuery = '/samlp:Response/saml:Assertion/ds:Signature/ds:SignedInfo/ds:Reference';
        $assertionReferenceNode = $xpath->query($signatureQuery)->item(0);
        if (!$assertionReferenceNode) {
            throw new Exception('Unable to query assertion, no Signature Reference found?');
        }
        $id = substr($assertionReferenceNode->attributes->getNamedItem('URI')->nodeValue, 1);

        $nameQuery = "/samlp:Response/saml:Assertion[@ID='$id']" . $assertionXpath;
        return $xpath->query($nameQuery);
    }
}


Así fue entonces como desde la aplicación web en PHP pude mostrar los valores de los atributos.



Es válido aclarar que este problema no es generado por el WSO2 Identity Server, sino por el código PHP empleado en este ejemplo.

Espero les sea de utilidad.

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.

jueves, 4 de julio de 2013

WSO2 Identity Server como proveedor de SSO.



El tema de Single Sign On o SSO siempre ha sido objeto de análisis e implementación cuando vamos a diseñar la arquitectura empresarial y tecnológica para una empresa.
Que todas las aplicaciones puedan usar 1 o varios almacenes de usuarios y además implementar el SSO, permitiendo a los usuarios ir de una aplicación a otra sin necesidad de autenticarse en cada una es día a día una necesidad imperiosa.

Mientras más aplicaciones se tienen pues mayor es la necesidad.

En desarrollos que he participado la solución siempre ha sido usar CAS, por considerarla la herramienta líder en este tema en la parte Open Source, además de tener agentes para múltiples lenguajes y ejemplos probados así como una comunidad cada día más creciente.

Pero me he topado con que cuando uso la suite de WSO2 y utilizo el IS para temas de autenticación y autorización para los servicio web la funcionalidad de SSO está presente ya, por lo que de usar el CAS la estaría duplicando.

En las últimas versiones del IS viene una versión establece del SSO tanto para las herramientas de la misma suite, como para las aplicaciones web en JAVA. Y ahora revisando el blog de Prabath Siriwardena veo una entrada donde pone un ejemplo para aplicaciones en PHP lo cual incrementa el uso que se le puede dar al IS. Y para JAVA tienen esta otra entrada.

En próximas entradas estaré mostrando como usar el IS para SSO en aplicaciones web desarrolladas en JAVA y en PHP.