Aprendiendo WSO2 Identity Server (I): Single Sign-On

Publicado por Luis Miguel Fernández Teomiro el

WSO2SeguridadAPI ManagementSAMLSSOIdentity ServerIAMArquitectura de Soluciones

Con este post os damos la bienvenida a una secuencia de artículos basados en WSO2 Identity Server. En esta primera entrada vamos a configurar desde el inicio una instancia de este servidor que nos servirá de base para el resto de la serie. Así mismo veremos como configurar Single Sign-On entre dos aplicaciones mediante Identity Server.

Como ya hemos visto en ocasiones anteriores la suite WSO2 es una plataforma Open Source que tiene como objetivo gestionar integraciones de forma ágil.

WSO2 Identity Server (IS), es el componente de la plataforma enfocado a simplificar el IAM (Identity and Access Management) en un entorno empresarial facilitando la integración de aplicaciones, repositorios de usuarios, directorios y otros sistemas de gestión de identidades.

WSO2 Platform

Entre las funcionalidades principales de este producto está el gobierno de Single Sign-On, la federación de identidades o la gestión de la seguridad de las APIs expuestas con OAuth2.

WSO2 IS

En este artículo vamos a configurar WSO2 Identity Server desde cero con el objetivo de hacer funcionar un ejemplo que nos permita entender cómo se gestiona la funcionalidad Single Sign-On mediante SAML 2.0 Web Browser y nos sirva de introducción a la propia herramienta.

Para obtener más contexto sobre la suite WSO2 es recomendable leer también los siguientes artículos que basan sobre esta suite:

Single Sign-On

Como comentamos anteriormente SSO es una de las funcionalidades principales de WSO2 IS, el objetivo de configurar SSO en la plataforma es permitir a los usuarios acceder a diferentes aplicaciones solicitándoles sólo una vez las credenciales, cuando la sesión caduque y el usuario intente acceder a un recurso protegido de una aplicación este tendrá que logarse con sus credenciales de nuevo.

En la especificación SAML se definen, de manera formal, tres roles:

  • Principal: Se identifica habitualmente con el usuario que intenta acceder a un recurso o servicio protegido de un Service Provider.
  • Identity Provider (IdP): Es el encargado de la autenticación de los usuarios y de generar las aserciones basadas en los resultados de este proceso incluyendo información de autenticación,  autorización y atributos de los usuarios.
  • Service Provider (SP): Consume y confía en las aserciones generadas por el IdP y ofrece servicios a los sujetos de tipo Principal.
SAML 2.0 Web SSO

Como condición obligatoria en este modelo se parte de la existencia de una relación de confianza entre los Service Providers y los Identity Providers.

En el ejemplo con el que vamos a trabajar el rol de Identity Provider lo asumirá WSO2 Identity Server y el rol Service Provider se asociará con la aplicación a la que quiere acceder el usuario.

Empezando con WSO2  Identity Server

Lo primero que tenemos que hacer para empezar a trabajar con WSO2 Identity Server es descargar el producto en su versión on-premise, para ello debemos registrarnos en la página de WSO2. El primer enlace de descarga permite descargar WSO2 IS como componente independiente y el segundo como Key Manager para WSO2 API Manager:

Descargar WSO2 Identity Server

Descargar WSO2 Identity Server como Key Manager de WSO2 API Manager

Este artículo está desarrollado utilizando la versión 5.3.0 del producto preconfigurado para WSO2 API Manager ya que es muy común utilizar ambos componentes de manera integrada. En la siguiente imagen se muestra cómo descargar el binario de la versión independiente y el módulo opcional Analytics específico para WSO2 Identity Server. En este momento no necesitamos descargar el módulo analítico con lo que sólo es necesario descargar el binario.

Existen versiones posteriores a las 5.3.0 disponibles para descargar con las que también se puede seguir este artículo así que la recomendación es que cada uno trabaje con la versión que más le interese. El binario descargado es válido para trabajar tanto en entorno Windows como en entorno Linux.

Descarga WSO2 IS

Una vez descargado el archivo zip wso2is-5.x.0.zip o wso2is-km-5.x.0.zip procedemos a descomprimirlo en la ubicación que seleccionemos, en mi caso vamos a utilizar C:\wso2\wso2is-km-5.3.0 .

 Directorio de C:\wso2\wso2is-km-5.3.0

23/07/2018  08:23    <DIR>          .  
23/07/2018  08:23    <DIR>          ..  
23/07/2018  08:22    <DIR>          bin  
23/07/2018  08:22    <DIR>          dbscripts  
23/07/2018  08:22    <DIR>          droppins_bck  
23/07/2018  08:22    <DIR>          lib  
12/06/2017  07:36            78.273 LICENSE.txt  
23/07/2018  08:22    <DIR>          modules  
23/07/2018  08:22    <DIR>          other_artifacts  
19/07/2018  13:03        10.881.054 other_artifacts.zip  
12/06/2017  07:36            13.902 README.txt  
12/06/2017  07:36            11.425 release-notes.html  
24/07/2018  15:39    <DIR>          repository  
23/07/2018  08:23    <DIR>          resources  
23/07/2018  08:23    <DIR>          solr  
04/11/2018  08:48    <DIR>          tmp  
23/07/2018  08:23    <DIR>          updates  
23/07/2018  08:23    <DIR>          webapps_bck  
04/11/2018  08:45               100 wso2carbon.pid  

Arrancamos IS mediante la ejecución del script wso2server.bat:

C:\wso2\wso2is-km-5.3.0\bin>wso2server.bat  
JAVA_HOME environment variable is set to C:\Program Files\Java\jdk1.8.0_65  
CARBON_HOME environment variable is set to C:\wso2\WSO2IS~2.0\bin\..  
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0  
[2018-11-04 12:56:20,293]  INFO {es.eci.wso2.application.properties.internal.CustomApplicationPropertiesServiceComponent} -  CustomApplicationProperties bundle is activated
[2018-11-04 12:56:24,906]  INFO {org.wso2.carbon.core.internal.CarbonCoreActivator} -  Starting WSO2 Carbon...
[2018-11-04 12:56:24,919]  INFO {org.wso2.carbon.core.internal.CarbonCoreActivator} -  Operating System : Windows 10 10.0, amd64
[2018-11-04 12:56:24,920]  INFO {org.wso2.carbon.core.internal.CarbonCoreActivator} -  Java Home        : C:\Program Files\Java\jdk1.8.0_65\jre
[2018-11-04 12:56:24,921]  INFO {org.wso2.carbon.core.internal.CarbonCoreActivator} -  Java Version     : 1.8.0_65
[2018-11-04 12:56:24,921]  INFO {org.wso2.carbon.core.internal.CarbonCoreActivator} -  Java VM          : Java HotSpot(TM) 64-Bit Server VM 25.65-b01,Oracle Corporation
[2018-11-04 12:56:24,940]  INFO {org.wso2.carbon.core.internal.CarbonCoreActivator} -  Carbon Home      : C:\wso2\WSO2IS~2.0\bin\..
[2018-11-04 12:56:24,970]  INFO {org.wso2.carbon.core.internal.CarbonCoreActivator} -  Java Temp Dir    : C:\wso2\WSO2IS~2.0\bin\..\tmp
[2018-11-04 12:56:24,978]  INFO {org.wso2.carbon.core.internal.CarbonCoreActivator} -  User             : lmfernandez, es-ES, Europe/Paris

Empieza a mostrar trazas hasta que finalmente nos indica la URL de la consola de administración:

[2018-11-04 12:59:31,320]  INFO {org.wso2.carbon.ui.internal.CarbonUIServiceComponent} -  Mgt Console URL  : https://localhost:9443/carbon/
[2018-11-04 12:59:31,376]  INFO {org.wso2.carbon.identity.authenticator.x509Certificate.internal.X509CertificateServiceComponent} -  X509 Certificate Servlet activated successfully..

Sabremos que IS ha arrancado correctamente cuando nos muestre la URL de la consola de administración, como no hemos cambiado el offset del puerto podemos acceder a esta consola mediante:

https://localhost:9443/carbon/ o https://wso2is.local:9443/carbon/

Hemos configurado previamente en el archivo hosts wso2is.local como 127.0.0.1.

Consola de administración de WSO2 IS

Creamos los repositorios de usuarios secundarios en IS

WSO2 Identity Server permite trabajar con diferentes repositorios de usuarios, por cada repositorio que se quiera añadir hay que configurar un nuevo User Store.

Por defecto IS viene preconfigurado con un User Store, se trata del Primary User Store y podríamos cambiarlo en el fichero user-mgt.xml, en nuestro caso está ubicado en C:\wso2\wso2is-km-5.3.0\repository\conf, el usuario administrador por defecto está incluido en este User Store.

Para nuestro ejemplo vamos a configurar dos User Store secundarios, en este caso serán de tipo LDAP, para ello vamos a crear dos instancias de servidor ApacheDS y vamos a gestionarlas con el IDE Apache Directory Studio

En una instancia vamos tener empleados (puerto LDAP 10390 y LDAPs 10637) y en otra vamos a tener clientes (puerto LDAP 10391 y LDAPs 10638), de esta manera vamos a trabajar con dos repositorios de usuarios muy simples.

Servidores ApacheDS

Como empleados vamos a tener a Pedro y a Pablo:

Empleados ApacheDS

Y como cliente vamos a tener a Miguel:

Clientes ApacheDS

La creación de los User Stores para el LDAP de empleados y el LDAP de clientes se realiza mediante la consola de administración de IS en la opción Main - User Stores - Add.

Clientes ApacheDS

La creación de cada Secondary User Store va a generar un XML con la configuración del mismo, este XML se ubica en C:\wso2\wso2is-km-5.3.0\repository\deployment\server\userstores y contiene la misma información que la que se ha proporcionado por consola. Es posible también crear el User Store copiando el XML en esta ubicación si necesidad de utilizar la consola.

El tipo de User Store que vamos a utilizar es LDAP de escritura y lectura y está configurado mediante el User Store Manager org.wso2.carbon.user.core.ldap.ReadWriteLDAPUserStoreManager. IS incluye managers para configurar User Stores de tipo JDBC, LDAP y ActiveDirectory y permite también implementar nuevos User Store Managers personalizados.

Este es el XML del User Store de empleados:

<?xml version="1.0" encoding="UTF-8"?>  
<UserStoreManager class="org.wso2.carbon.user.core.ldap.ReadWriteLDAPUserStoreManager">  
   <Property name="ConnectionURL">ldap://localhost:10390</Property>
   <Property name="ConnectionName">uid=admin,ou=system</Property>
   <Property encrypted="true" name="ConnectionPassword">dhbmDZ2nsIqy0SDpF9UJgbXHqlteUIp0HSCWoqILNsl9FhSQ6UpQY5smoYdljhdZ0wl8U0pY5tvaLCOuafc6uBMALtq+nDsBhATtC2S6prgK+izi1bXnZGSUmYeCiGrc5GRM5+WKUPfAsh48oIyrXCae6UFwmYaQx9wTFu5FTBo=</Property>
   <Property name="UserSearchBase">ou=users,ou=system</Property>
   <Property name="UserEntryObjectClass">inetOrgPerson</Property>
   <Property name="UserNameAttribute">uid</Property>
   <Property name="UserNameSearchFilter">(&amp;(objectClass=person)(uid=?))</Property>
   <Property name="UserNameListFilter">(objectClass=person)</Property>
   <Property name="UserDNPattern"/>
   <Property name="DisplayNameAttribute"/>
   <Property name="Disabled">false</Property>
   <Property name="ReadGroups">true</Property>
   <Property name="WriteGroups">true</Property>
   <Property name="GroupSearchBase">ou=groups,ou=system</Property>
   <Property name="GroupEntryObjectClass">groupOfNames</Property>
   <Property name="GroupNameAttribute">cn</Property>
   <Property name="GroupNameSearchFilter">(&amp;(objectClass=groupOfNames)(cn=?))</Property>
   <Property name="GroupNameListFilter">(objectClass=groupOfNames)</Property>
   <Property name="RoleDNPattern"/>
   <Property name="MembershipAttribute">member</Property>
   <Property name="MemberOfAttribute"/>
   <Property name="BackLinksEnabled">false</Property>
   <Property name="UserNameJavaRegEx">[a-zA-Z0-9._-|//]{3,30}$</Property>
   <Property name="UserNameJavaScriptRegEx">^[\S]{3,30}$</Property>
   <Property name="UsernameJavaRegExViolationErrorMsg">Username pattern policy violated.</Property>
   <Property name="PasswordJavaRegEx">^[\S]{5,30}$</Property>
   <Property name="PasswordJavaScriptRegEx">^[\S]{5,30}$</Property>
   <Property name="PasswordJavaRegExViolationErrorMsg">Password pattern policy violated.</Property>
   <Property name="RoleNameJavaRegEx">[a-zA-Z0-9._-|//]{3,30}$</Property>
   <Property name="RoleNameJavaScriptRegEx">^[\S]{3,30}$</Property>
   <Property name="SCIMEnabled">false</Property>
   <Property name="BulkImportSupported">true</Property>
   <Property name="EmptyRolesAllowed">true</Property>
   <Property name="PasswordHashMethod">PLAIN_TEXT</Property>
   <Property name="MultiAttributeSeparator">,</Property>
   <Property name="MaxUserNameListLength">100</Property>
   <Property name="MaxRoleNameListLength">100</Property>
   <Property name="kdcEnabled">false</Property>
   <Property name="defaultRealmName">WSO2.ORG</Property>
   <Property name="UserRolesCacheEnabled">true</Property>
   <Property name="ConnectionPoolingEnabled">false</Property>
   <Property name="LDAPConnectionTimeout">5000</Property>
   <Property name="ReadTimeout">5000</Property>
   <Property name="RetryAttempts">0</Property>
   <Property name="CountRetrieverClass"/>
   <Property name="java.naming.ldap.attributes.binary"> </Property>
   <Property name="DomainName">EMPLOYEES</Property>
   <Property name="Description"/>
</UserStoreManager>  

Este es el XML del User Store de clientes:

<?xml version="1.0" encoding="UTF-8"?>  
<UserStoreManager class="org.wso2.carbon.user.core.ldap.ReadWriteLDAPUserStoreManager">  
   <Property name="ConnectionURL">ldap://localhost:10391</Property>
   <Property name="ConnectionName">uid=admin,ou=system</Property>
   <Property encrypted="true" name="ConnectionPassword">dhbmDZ2nsIqy0SDpF9UJgbXHqlteUIp0HSCWoqILNsl9FhSQ6UpQY5smoYdljhdZ0wl8U0pY5tvaLCOuafc6uBMALtq+nDsBhATtC2S6prgK+izi1bXnZGSUmYeCiGrc5GRM5+WKUPfAsh48oIyrXCae6UFwmYaQx9wTFu5FTBo=</Property>
   <Property name="UserSearchBase">ou=users,ou=system</Property>
   <Property name="UserEntryObjectClass">inetOrgPerson </Property>
   <Property name="UserNameAttribute">uid</Property>
   <Property name="UserNameSearchFilter">(&amp;(objectClass=person)(uid=?))</Property>
   <Property name="UserNameListFilter">(objectClass=person)</Property>
   <Property name="UserDNPattern"/>
   <Property name="DisplayNameAttribute"/>
   <Property name="Disabled">false</Property>
   <Property name="ReadGroups">true</Property>
   <Property name="WriteGroups">true</Property>
   <Property name="GroupSearchBase"> ou=groups,ou=system</Property>
   <Property name="GroupEntryObjectClass">groupOfNames</Property>
   <Property name="GroupNameAttribute">cn</Property>
   <Property name="GroupNameSearchFilter">(&amp;(objectClass=groupOfNames)(cn=?))</Property>
   <Property name="GroupNameListFilter">(objectClass=groupOfNames)</Property>
   <Property name="RoleDNPattern"/>
   <Property name="MembershipAttribute">member</Property>
   <Property name="MemberOfAttribute"/>
   <Property name="BackLinksEnabled">false</Property>
   <Property name="UserNameJavaRegEx">[a-zA-Z0-9._-|//]{3,30}$</Property>
   <Property name="UserNameJavaScriptRegEx">^[\S]{3,30}$</Property>
   <Property name="UsernameJavaRegExViolationErrorMsg">Username pattern policy violated.</Property>
   <Property name="PasswordJavaRegEx">^[\S]{5,30}$</Property>
   <Property name="PasswordJavaScriptRegEx">^[\S]{5,30}$</Property>
   <Property name="PasswordJavaRegExViolationErrorMsg">Password pattern policy violated.</Property>
   <Property name="RoleNameJavaRegEx">[a-zA-Z0-9._-|//]{3,30}$</Property>
   <Property name="RoleNameJavaScriptRegEx">^[\S]{3,30}$</Property>
   <Property name="SCIMEnabled">false</Property>
   <Property name="BulkImportSupported">true</Property>
   <Property name="EmptyRolesAllowed">true</Property>
   <Property name="PasswordHashMethod">PLAIN_TEXT</Property>
   <Property name="MultiAttributeSeparator">,</Property>
   <Property name="MaxUserNameListLength">100</Property>
   <Property name="MaxRoleNameListLength">100</Property>
   <Property name="kdcEnabled">false</Property>
   <Property name="defaultRealmName">WSO2.ORG</Property>
   <Property name="UserRolesCacheEnabled">true</Property>
   <Property name="ConnectionPoolingEnabled">false</Property>
   <Property name="LDAPConnectionTimeout">5000</Property>
   <Property name="ReadTimeout">5000</Property>
   <Property name="RetryAttempts">0</Property>
   <Property name="CountRetrieverClass"/>
   <Property name="java.naming.ldap.attributes.binary"> </Property>
   <Property name="DomainName">CUSTOMERS</Property>
   <Property name="Description"/>
</UserStoreManager>  

Si hacemos una búsqueda de usuarios en IS en el apartado Users and Roles nos aparecen los usuarios de todos los User Stores que tenemos configurados.

Usuarios

La aplicación web que actuará como Service Provider

WSO2 publica ejemplos en Git para sus productos, en este caso vamos a utilizar una aplicación web de ejemplo que nos va a permitir probar la funcionalidad SSO de Identity Server. La aplicación que vamos a utilizar tiene el nombre de Travelocity.

La guía de descarga de ejemplos de IS se puede consultar en el siguiente enlace:

Guía de descarga de ejemplos para IS

Procedemos a descargar el ejemplo desde el repositorio, en mi caso he utilizado Git BASH, los pasos son los siguientes:

lmfernandez@ATLMFERNANDEZP MINGW64 ~  
$ cd /c/wso2/is-samples/

lmfernandez@ATLMFERNANDEZP MINGW64 /c/wso2/is-samples  
$ git init
Initialized empty Git repository in C:/wso2/is-samples/.git/

lmfernandez@ATLMFERNANDEZP MINGW64 /c/wso2/is-samples (master)  
$ git remote add -f origin https://github.com/wso2/product-is.git
Updating origin  
remote: Enumerating objects: 420, done.  
remote: Counting objects: 100% (420/420), done.  
remote: Compressing objects: 100% (275/275), done.  
remote: Total 69397 (delta 144), reused 328 (delta 91), pack-reused 68977  
Receiving objects: 100% (69397/69397), 100.84 MiB | 474.00 KiB/s, done.  
Resolving deltas: 100% (30785/30785), done.  
From https://github.com/wso2/product-is  
 * [new branch]          5.3.0                  -> origin/5.3.0
 * [new branch]          5.4.0-kernel-upgrade   -> origin/5.4.0-kernel-upgrade
 * [new branch]          6.0.0                  -> origin/6.0.0
 * [new branch]          6.0.x-C5               -> origin/6.0.x-C5
 * [new branch]          6.0.x-C5_m3            -> origin/6.0.x-C5_m3
 * [new branch]          6.0.x-C5_m3_updated    -> origin/6.0.x-C5_m3_updated
 * [new branch]          6.0.x-C5_m4            -> origin/6.0.x-C5_m4
 * [new branch]          6.0.x-C5_m5            -> origin/6.0.x-C5_m5
 * [new branch]          IDENTITY-6924          -> origin/IDENTITY-6924
 * [new branch]          deprecated-c5-implementation -> origin/deprecated-c5-implementation
 * [new branch]          feature-OIDC-enh-5.3.x -> origin/feature-OIDC-enh-5.3.x
 * [new branch]          feature-oidc-backchannel-logout -> origin/feature-oidc-backchannel-logout
 * [new branch]          keystore-update        -> origin/keystore-update
 * [new branch]          madawas-patch-1        -> origin/madawas-patch-1
 * [new branch]          master                 -> origin/master
 * [new branch]          pr-integration-test    -> origin/pr-integration-test
 * [new branch]          release-5.6.0          -> origin/release-5.6.0
 * [new branch]          release-5.6.0-m7-s18   -> origin/release-5.6.0-m7-s18
 * [new branch]          release-5.7.0-beta-r17 -> origin/release-5.7.0-beta-r17
 * [new branch]          release-5.7.0-beta2-t747 -> origin/release-5.7.0-beta2-t747
 * [new branch]          release-5.7.0-m3-c938  -> origin/release-5.7.0-m3-c938
 * [new branch]          release-5.7.0-m3-m90   -> origin/release-5.7.0-m3-m90
 * [new branch]          release-5.7.0-m3-t617  -> origin/release-5.7.0-m3-t617
 * [new branch]          release-5.7.0-m3-v437  -> origin/release-5.7.0-m3-v437
 * [new branch]          release-5.8.0-m2-c756  -> origin/release-5.8.0-m2-c756
 * [new branch]          release-5.8.0-m2-h735  -> origin/release-5.8.0-m2-h735
 * [new branch]          release-5.8.0-m4-j425  -> origin/release-5.8.0-m4-j425
 * [new branch]          revert-3088-scim-identiy -> origin/revert-3088-scim-identiy
 * [new branch]          revert-3235-crl_ocsp   -> origin/revert-3235-crl_ocsp
 * [new branch]          revert-3326-feature/rrt/filterUsersGroups -> origin/revert-3326-feature/rrt/filterUsersGroups
 * [new branch]          revert-3676-master     -> origin/revert-3676-master
 * [new branch]          siddhi-integration     -> origin/siddhi-integration
 * [new branch]          v5.7.0                 -> origin/v5.7.0
 * [new tag]             v5.6.0                 -> v5.6.0
 * [new tag]             v5.6.0-rc3             -> v5.6.0-rc3
 * [new tag]             v5.7.0                 -> v5.7.0
 * [new tag]             v5.7.0-rc3             -> v5.7.0-rc3
 * [new tag]             product-is-5.1.0-M1    -> product-is-5.1.0-M1
 * [new tag]             v5.1.0                 -> v5.1.0
 * [new tag]             v5.1.0-m2              -> v5.1.0-m2
 * [new tag]             v5.1.0-m3              -> v5.1.0-m3
 * [new tag]             v5.1.0-m4              -> v5.1.0-m4
 * [new tag]             v5.1.0-m5              -> v5.1.0-m5
 * [new tag]             v5.1.0-m6              -> v5.1.0-m6
 * [new tag]             v5.1.0-rc2             -> v5.1.0-rc2
 * [new tag]             v5.2.0-alpha           -> v5.2.0-alpha
 * [new tag]             v5.2.0-beta            -> v5.2.0-beta
 * [new tag]             v5.2.0-m1              -> v5.2.0-m1
 * [new tag]             v5.3.0                 -> v5.3.0
 * [new tag]             v5.3.0-alpha           -> v5.3.0-alpha
 * [new tag]             v5.3.0-m5              -> v5.3.0-m5
 * [new tag]             v5.3.0-rc3             -> v5.3.0-rc3
 * [new tag]             v5.4.0                 -> v5.4.0
 * [new tag]             v5.4.0-alpha           -> v5.4.0-alpha
 * [new tag]             v5.4.0-alpha10         -> v5.4.0-alpha10
 * [new tag]             v5.4.0-alpha2          -> v5.4.0-alpha2
 * [new tag]             v5.4.0-alpha3          -> v5.4.0-alpha3
 * [new tag]             v5.4.0-alpha4          -> v5.4.0-alpha4
 * [new tag]             v5.4.0-alpha5          -> v5.4.0-alpha5
 * [new tag]             v5.4.0-alpha6          -> v5.4.0-alpha6
 * [new tag]             v5.4.0-alpha7          -> v5.4.0-alpha7
 * [new tag]             v5.4.0-alpha8          -> v5.4.0-alpha8
 * [new tag]             v5.4.0-alpha9          -> v5.4.0-alpha9
 * [new tag]             v5.4.0-beta            -> v5.4.0-beta
 * [new tag]             v5.4.0-m1              -> v5.4.0-m1
 * [new tag]             v5.4.0-m2              -> v5.4.0-m2
 * [new tag]             v5.4.0-m3              -> v5.4.0-m3
 * [new tag]             v5.4.0-update1         -> v5.4.0-update1
 * [new tag]             v5.4.0-update2         -> v5.4.0-update2
 * [new tag]             v5.4.0-update3         -> v5.4.0-update3
 * [new tag]             v5.4.0-update4         -> v5.4.0-update4
 * [new tag]             v5.4.0-update5         -> v5.4.0-update5
 * [new tag]             v5.4.0-update6         -> v5.4.0-update6
 * [new tag]             v5.4.1                 -> v5.4.1
 * [new tag]             v5.4.1-update1         -> v5.4.1-update1
 * [new tag]             v5.4.1-update2         -> v5.4.1-update2
 * [new tag]             v5.4.1-update3         -> v5.4.1-update3
 * [new tag]             v5.4.1-update4         -> v5.4.1-update4
 * [new tag]             v5.4.1-update5         -> v5.4.1-update5
 * [new tag]             v5.5.0                 -> v5.5.0
 * [new tag]             v5.5.0-alpha           -> v5.5.0-alpha
 * [new tag]             v5.5.0-alpha2          -> v5.5.0-alpha2
 * [new tag]             v5.5.0-alpha3          -> v5.5.0-alpha3
 * [new tag]             v5.5.0-beta            -> v5.5.0-beta
 * [new tag]             v5.5.0-beta2           -> v5.5.0-beta2
 * [new tag]             v5.5.0-m1              -> v5.5.0-m1
 * [new tag]             v5.5.0-m2              -> v5.5.0-m2
 * [new tag]             v5.5.0-m3              -> v5.5.0-m3
 * [new tag]             v5.5.0-m4              -> v5.5.0-m4
 * [new tag]             v5.5.0-rc1             -> v5.5.0-rc1
 * [new tag]             v5.5.0-rc2             -> v5.5.0-rc2
 * [new tag]             v5.6.0-alpha           -> v5.6.0-alpha
 * [new tag]             v5.6.0-alpha2          -> v5.6.0-alpha2
 * [new tag]             v5.6.0-beta            -> v5.6.0-beta
 * [new tag]             v5.6.0-m1              -> v5.6.0-m1
 * [new tag]             v5.6.0-m2              -> v5.6.0-m2
 * [new tag]             v5.6.0-m3              -> v5.6.0-m3
 * [new tag]             v5.6.0-m4              -> v5.6.0-m4
 * [new tag]             v5.6.0-m5              -> v5.6.0-m5
 * [new tag]             v5.6.0-m6              -> v5.6.0-m6
 * [new tag]             v5.6.0-m7              -> v5.6.0-m7
 * [new tag]             v5.6.0-rc1             -> v5.6.0-rc1
 * [new tag]             v5.6.0-rc2             -> v5.6.0-rc2
 * [new tag]             v5.7.0-alpha           -> v5.7.0-alpha
 * [new tag]             v5.7.0-alpha2          -> v5.7.0-alpha2
 * [new tag]             v5.7.0-alpha3          -> v5.7.0-alpha3
 * [new tag]             v5.7.0-beta            -> v5.7.0-beta
 * [new tag]             v5.7.0-beta2           -> v5.7.0-beta2
 * [new tag]             v5.7.0-beta3           -> v5.7.0-beta3
 * [new tag]             v5.7.0-m1              -> v5.7.0-m1
 * [new tag]             v5.7.0-m2              -> v5.7.0-m2
 * [new tag]             v5.7.0-m3              -> v5.7.0-m3
 * [new tag]             v5.7.0-m4              -> v5.7.0-m4
 * [new tag]             v5.7.0-m5              -> v5.7.0-m5
 * [new tag]             v5.7.0-rc1             -> v5.7.0-rc1
 * [new tag]             v5.7.0-rc2             -> v5.7.0-rc2
 * [new tag]             v5.8.0-m1              -> v5.8.0-m1
 * [new tag]             v5.8.0-m2              -> v5.8.0-m2
 * [new tag]             v5.8.0-m3              -> v5.8.0-m3
 * [new tag]             v6.0.0-m1              -> v6.0.0-m1

lmfernandez@ATLMFERNANDEZP MINGW64 /c/wso2/is-samples (master)  
$ git config core.sparseCheckout true

lmfernandez@ATLMFERNANDEZP MINGW64 /c/wso2/is-samples (master)  
$ cd .git/info

lmfernandez@ATLMFERNANDEZP MINGW64 /c/wso2/is-samples/.git/info (GIT_DIR!)  
$ echo "modules/samples/" >> sparse-checkout

lmfernandez@ATLMFERNANDEZP MINGW64 /c/wso2/is-samples/.git/info (GIT_DIR!)  
$ cd ../../

lmfernandez@ATLMFERNANDEZP MINGW64 /c/wso2/is-samples (master)  
$ git checkout -b v5.3.0 v5.3.0
Switched to a new branch 'v5.3.0'  

En mi caso voy a desplegar posteriormente en Tomcat 9.0.7 la aplicación, por defecto esta viene configurada para el puerto 8080, cómo mi Tomcat escucha en el 8090 procedo a cambiar el puerto en el siguiente fichero de properties de la aplicación de ejemplo:

C:\wso2\is-samples\modules\samples\sso\sso-agent-sample\src\main\resources\travelocity.properties

Llegado a este punto ya podemos construir el ejemplo con Maven:

C:\wso2\is-samples\modules\samples\sso\sso-agent-sample> mvn clean install  
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Travelocity.COM ServiceProvider / RelyingParty Webapp 5.3.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.6.1:clean (default-clean) @ org.wso2.sample.is.sso.agent ---
[INFO] Deleting C:\wso2\is-samples\modules\samples\sso\sso-agent-sample\target
[INFO]
[INFO] --- maven-remote-resources-plugin:1.5:process (default) @ org.wso2.sample.is.sso.agent ---
[INFO]
[INFO] --- maven-resources-plugin:2.7:resources (default-resources) @ org.wso2.sample.is.sso.agent ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 3 resources
[INFO] Copying 3 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.2:compile (default-compile) @ org.wso2.sample.is.sso.agent ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 4 source files to C:\wso2\is-samples\modules\samples\sso\sso-agent-sample\target\classes
[INFO]
[INFO] --- maven-resources-plugin:2.7:testResources (default-testResources) @ org.wso2.sample.is.sso.agent ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory C:\wso2\is-samples\modules\samples\sso\sso-agent-sample\src\test\resources
[INFO] Copying 3 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.2:testCompile (default-testCompile) @ org.wso2.sample.is.sso.agent ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-surefire-plugin:2.18:test (default-test) @ org.wso2.sample.is.sso.agent ---
[INFO]
[INFO] --- maven-war-plugin:2.2:war (default-war) @ org.wso2.sample.is.sso.agent ---
[INFO] Packaging webapp
[INFO] Assembling webapp [org.wso2.sample.is.sso.agent] in [C:\wso2\is-samples\modules\samples\sso\sso-agent-sample\target\travelocity.com]
[INFO] Processing war project
[INFO] Copying webapp resources [C:\wso2\is-samples\modules\samples\sso\sso-agent-sample\src\main\webapp]
[INFO] Webapp assembled in [1816 msecs]
[INFO] Building war: C:\wso2\is-samples\modules\samples\sso\sso-agent-sample\target\travelocity.com.war
[INFO] WEB-INF\web.xml already added, skipping
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ org.wso2.sample.is.sso.agent ---
[INFO] Installing C:\wso2\is-samples\modules\samples\sso\sso-agent-sample\target\travelocity.com.war to C:\Users\lmfernandez\.m2\repository\org\wso2\is\org.wso2.sample.is.sso.agent\5.3.0\org.wso2.sample.is.sso.agent-5.3.0.war
[INFO] Installing C:\wso2\is-samples\modules\samples\sso\sso-agent-sample\pom.xml to C:\Users\lmfernandez\.m2\repository\org\wso2\is\org.wso2.sample.is.sso.agent\5.3.0\org.wso2.sample.is.sso.agent-5.3.0.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 25.701 s
[INFO] Finished at: 2018-11-10T09:13:31+01:00
[INFO] Final Memory: 28M/248M
[INFO] ------------------------------------------------------------------------

En este momento ya tenemos el artefacto WAR generado, para desplegarlo lo copiamos de la carpeta target:

C:\wso2\is-samples\modules\samples\sso\sso-agent-sample\target\travelocity.com.war

Y lo pegamos en el directorio webapps de Tomcat:

C:\Programas\apache-tomcat-9.0.7\webapps

La aplicación web estaría disponible en:

http://localhost:9080/travelocity.com/index.jsp

Web Application Index

La configuración del Identity Provider y del Service Provider

Ahora tenemos disponible WSO2 Identity Server con las dos instancias externas de LDAP configuradas y la aplicación de ejemplo que se va a apoyar en IS para utilizar el procedimiento de autenticación basado en SSO. Cuando configuremos la aplicación web como Service Provider en IS podremos probar el flujo SSO completo.

Nos dirigimos a la consola de administración de IS y examinamos la configuración Resident Identity Provider:

Resident IdP

WSO2 IS actuará como IdP y esta es la información que hay que proporcionar al SP para que pueda ocurrir la interactuación entre ambos. En el caso de que el SP acepte la importación de ficheros de tipo SAML Metadata, para configurar el IdP con el que va a trabajar, en esta consola se podría exportar el mismo.

A continuación nos dirigimos a crear el nuevo Service Provider, para ello vamos a a Main - Service Providers - Add y seleccionamos la configuración manual. Si la aplicación que va a actuar como SP nos facilita un fichero Metadata podríamos importarlo y ahorrarnos esta configuración manual.

La creación del SP en Identity Server se apoya en varias secciones de configuración, en la siguiente imagen WSO2 describe la parametrización disponible en cada una de ellas.

Secciones de configuración del SP
  • En la configuración básica indicamos el nombre que le vamos a dar al SP.
  • En la sección de configuración de Claims vamos a indicar los claims que va a proporcionar el IdP al SP cuando se autentique un usuario, de esta forma la aplicación web en este caso contará con información adicional del usuario como su rol, su nombre completo, su número de teléfono y su dirección de correo electrónico. Si se marca algún claim como obligatorio y no está informado en el User Store se le solicitará al usuario en el momento de autenticarse.
  • En el apartado Inbound Authentication vamos a configurar SAML2 Web SSO para gestionar las peticiones de autenticación entrantes en Identity Server.
  • En la configuración Local & Outbound Authentication vamos a dejar el tipo de autenticación por defecto para este SP y vamos a marcar Enable Authorization para dejar abierta la posibilidad de asociar otras políticas de autenticación a este SP.
Creando el SP

Ahora tendríamos que configurar el Single Sign-On en la sección Inbound Authentication, para ello accedemos a SAML2 Web SSO Configuration - Configure y cumplimentamos el formulario:

SAML SP

En esta sección vamos a configurar lo siguientes campos:

Campo Valor Detalle
Issuer travelocity.com Es el identificador para el SAML2 SP
Assertion Consumer URLs http://wso2is.local:9080/travelocity.com/home.jsp Es la Assertion Consumer Service (ACS) URL del SP, la respuesta SAML2 generada en el IdP se redirigirá a esta URL de la aplicación web
Default Assertion Consumer URLs http://wso2is.local:9080/travelocity.com/home.jsp Si hay configuradas varias ACS URLs, se utilizará la seleccionada en este campo siempre y cuando no se pueda obtener desde la petición de autenticación
NameID format urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress Formato definido por la especificación SAML2, en este caso dejamos el valor por defecto
Certificate Alias wso2carbon.cert Certificado utilizado para validar la firma de las peticiones SAML2 y para encriptar información en IS, para este ejemplo utilizamos el certificado que viene de fábrica en IS
Response Signing Algorithm http://www.w3.org/2000/09/xmldsig#rsasha1 Algoritmo de firma que se utiliza en el elemento Signature del POST de respuesta. Dejamos el valor por defecto.
Response Digest Algorithm http://www.w3.org/2000/09/xmldsig#sha1 Especifica el algoritmo Digest que se utiliza en el elemento Signature del POST de respuesta. No cambiamos el valor por defecto.
Enable Response Signing True Indica si se firma o no las respuestas SAML2 después de que se ha completado el proceso de autenticación. Indicamos que sí queremos firmar las respuestas.
Enable Single Logout True Indica si se deben cerrar todas las sesiones cuando un usuario cierra sesión en un SP.
Enable Attribute Profile / Include Attributes in the Response Always True En la respuesta SAML se añaden los valores de los atributos asociados a los claims selecciones en la configuración del SP. Fijamos el valor a true para comprobar esta funcionalidad.
Enable IdP Initiated SSO True Habilita la posibilidad de iniciar el flujo de autenticación mediante el envío de una petición GET al Identity Server. Habilitamos esta funcionalidad.
Enable IdP Initiated SLO True Permite realizar el log out en aplicación que que no gestionen el índice de sesión.

Para ver mas detalle de la tabla anterior y de la configuración SSO de la aplicación de ejemplo de WSO2 que estamos utilizando podemos consultar el siguiente enlace de la documentación:

https://docs.wso2.com/display/IS530/Configuring+Single+Sign-On

Configuramos una segunda aplicación y su Service Provider

Par probar el SSO además de la aplicación Travelocity que hemos configurado anteriormente vamos a necesitar otra aplicación que también actúe como SP, para ello vamos a utilizar un clon de  Travelocity que WSO2 facilita, se trata de la aplicación Avis.

Vamos a utilizar la misma configuración para el Service Provider exceptuando:

Creando el segundo SP

El WAR se construiría como explicamos anteriormente para Travelocity pero para abreviar lo podemos descargar ya generado desde
WAR Avis

Una vez desplegado en Tomcat sería accesible desde:
http://wso2is.local:9080/avis.com/index.jsp

Como podemos ver la aplicación es un clon.

Aplicación Avis

Probamos la configuración de SSO

Para probar el trabajo que hemos realizado hasta ahora volvemos a entrar en la aplicación web mediante la URL:

http://localhost:9080/travelocity.com/index.jsp

Y seleccionamos la opción Click here to login with SAML (Redirect Binding) from WSO2 Identity Server o la opción Click here to login with SAML (Post Binding) from Identity Server.

En este momento la aplicación envía la solicitud de autenticación al servidor WSO2 IS, este recibe la petición y muestra la pantalla de login:

Pantalla de login

Tras introducir las credenciales de Pedro vemos cómo se produce la autenticación y en la página se muestran los atributos correspondientes a los claims que hemos configurado en el primer Service Provider en el Identity Server.

usuario autenticado tras login

Ahora, si abrimos otra pestaña del navegador y entramos en http://wso2is.local:9080/avis.com/index.jsp y seleccionamos cualquiera de los enlaces  que comienzan por Click here to login with SAML... podremos ver como no se muestra la pantalla de login y el usuario Pedro aparece como autenticado directamente en la aplicación Avis. Este comportamiento se debe a que ya teníamos una sesión abierta para el usuario en el Identity Provider con la aplicación Travelocity. El SSO ha funcionado correctamente.

usuario autenticado automáticamente

Logout

Para deslogarnos, hacemos click en el enlace Logout (HTTPRedirect) o Logout (HTTP POST) de Travelocity, esto va a desencadenar una petición de Logout contra el Identity Provider. Si los Service Providers se han configurado habilitando Single Logout (SLO) al cerrar la sesión en un Service Provider se propaga el Logout a todos los demás Service Providers que comparten sesión.

Por ese motivo al dejar de estar logados en Travelocity también dejamos de estarlo en Avis.

Conclusión

En este artículo hemos visto como descargar y configurar WSO2 Identity Server para dotar de seguridad a dos aplicaciones, para ello se han registrado dos User Stores que se integran con dos servidores LDAP con diferentes universos de usuarios y se han dado de alta dos Service Providers que reflejan el rol de las dos aplicaciones contra el Identity Provider en la configuración Single Sign-On.

Por último se ha mostrado a alto nivel como funcionarían los flujos de SSO basados en SAML2, en próximas entradas de esta serie profundizaremos en los diferentes mensajes SAML intercambiados entre los Service Providers y el Identity Provider.

Si te ha gustado, ¡síguenos en Twitter para estar al día de nuevos posts de la serie!