JWT Tokens es un estándar abierto para la compartición de datos en un formato de objeto JSON. Este objeto contendrá información y privilegios del usuario, también conocidos como claims. El estándar establece mecanismos que nos permiten asegurar y autenticar los datos enviados.
Qué debes saber sobre JWT Token
El token JWT se encuentra diferenciado en tres partes:
- Cabecera o header: Donde se indica el algoritmo con el cual se ha codificado el token.
{"typ": "JWT", "alg": "none"}
- Contenido o payload: Donde se incluye los claims asociados al usuario representados como una pareja clave/valor cada uno.
{ "http://wso2.org/claims/role": [ "admin" ], ... "http://wso2.org/claims/subscriber": "userJwt", "http://wso2.org/claims/tier": "Unlimited", "http://wso2.org/claims/applicationid": "6", "http://wso2.org/claims/usertype": "APPLICATION", "http://wso2.org/claims/apicontext": "/contextName/v1" }
- Firma o signature: Donde se codifica el contenido concatenado de las anteriores secciones con HMAC-256. Generando un hash que nos permita validar la autenticidad del contenido y por tanto la confianza del mismo.
-Este post te interesa: WSO2 MSF4J Tutorial “Conexión con el IS y OAuth 2.0”-
Funciones de JWT Token
JWT presenta dos funcionalidades principales, las cuales nos indicarán cuándo debemos utilizarlo:
- Autorización: Los tokens JWT pueden ser utilizados como access token entre aplicaciones, incluyendo los privilegios del usuario. Estos serán incluidos en cada llamada, una vez el usuario se encuentre autenticado. . Esta suele ser la opción más usada.
- Intercambio de datos: A través de los claims incluidos en su payload.
JWT Token ejemplo práctico
En nuestro ejemplo, haremos uso de tokens de acceso OAuth2 para la autorización de llamadas a la API y JWT para la transferencia de datos de usuario entre ésta y el backend.
De esta forma podremos intercambiar información de carácter confidencial entre el API Manager y el backend (usualmente un Enterprise Integrator), de una forma automatizada y segura.
Y para ello, utilizaremos un API Manager y un Identity Server como núcleo central del mismo, donde además configuraremos al Identity Server como Key Manager, de ahora en adelante IS-KM. Así tendremos centralizada en el IS-KM la gestión de usuarios y las tareas de autorización.
Así, lograremos definir un un ejemplo práctico en el cual realicemos una llamada genérica a la API pasando únicamente como identificador el access token de usuario. Una vez allí será donde el IS-KM validará el token y proveerá al backend de las claims del usuario, permitiendo así realizar una respuesta personalizada para el usuario.
1. Comunicación entre API Manager e Identity Server
La comunicación se realizará de la siguiente forma:
1.Obtención de token de usuario:
a. El usuario se suscribirá a una API existente a través del API Store.
b. El API Store delegará el control al API Gateway, el cual se conectará con el IS-KM y obtendrá un access token.
2. Llamada al sistema con el token de usuario:
a. El usuario llamará a un recurso concreto de la API con este access token obtenido.
b. El API Gateway comprobará la existencia de dicho access token y redirigirá la llamada al IS-KM para su validación. Esta llamada se realizará a través de servicios web, tal y como veremos más adelante.
c. El IS-KM validará el access token y pasará al API Gateway el control y los claims asociados al usuario que realizó la llamada en el formato de JWT.
d. El API Gateway redirigirá la llamada al backend asociado a la API, añadiendo una cabecera de tipo ‘X-JWT-Assertion’ con el JWT codificado en Base64.
-No te pierdas: WSO2 MSF4J Tutorial “Funcionalidades avanzadas para crear microservices”-
2. Intercambio de datos entre API Manager e Identity Server
El primer paso es identificar y crear los distintos esquemas que necesitamos en este caso. Los esquemas que deben ser compartidos por ambos productos, son los siguientes:
- WSO2REG_DB: este esquema almacenará la información del registry, para compartir configuraciones internas entre productos de WSO2. En nuestro caso mantendremos la H2 por defecto, aunque debería utilizarse una para ambos productos.
- WSO2UM_DB: aquí se aglutinará la información relativa a los permisos y los roles de usuario.
- WSO2US_DB: este esquema reservará la información relacionada con los usuarios.
- WSO2AM_DB: esquema que reúne la información relativa a OAuth tokens, suscripciones, etc.
Respecto a los scripts para BBDD, estos vienen incluidos dentro de la carpeta ‘dbscripts’ en cada uno de los productos de WSO2. En nuestro ejemplo utilizaremos un esquema único para WSO2UM_DB y WSO2US_DB, denominado: WSO2UM_DB.
-Quizá te interese: WSO2 MSF4J Tutorial “WebSockets”-
1. Modificar el fichero ‘/repository/conf/datasources/master-datasources.xml’, para crear un datasource con cada una de nuestras conexiones a BBDD y tener así un mejor desempeño.
<datasource> <name>WSO2UM_DB</name> <description>The datasource used for registry and user mngr</description> <jndiConfig> <name>jdbc/WSO2UM_DB</name> </jndiConfig> <definition type="RDBMS"> <configuration> <url>jdbc:mysql://localhost:3306/JWT_UM_DB</url> <username>root</username> <password>CHANGE_ME</password> <driverClassName>com.mysql.jdbc.Driver</driverClassName> <maxActive>50</maxActive> <maxWait>60000</maxWait> <testOnBorrow>true</testOnBorrow> <validationQuery>SELECT 1</validationQuery> <validationInterval>30000</validationInterval> <defaultAutoCommit>false</defaultAutoCommit> </configuration> </definition> </datasource> <datasource> <name>WSO2AM_DB</name> <description>The datasource for token and API information</description> <jndiConfig> <name>jdbc/WSO2AM_DB</name> </jndiConfig> <definition type="RDBMS"> ... </definition> </datasource>
2. Incluir la librería con los drivers de la BBDD en caso de que no estemos usando la H2 que viene configurada por defecto y que no es recomendable para entornos productivos. El archivo se debe incluir en la ruta ‘/repository/components/lib/’.
3.Cambiamos el fichero ‘/repository/conf/user-mgnt.xml’, indicando lo siguiente:
a.Indicamos en la propiedad ‘/UserManager/Realm/Configuration/Property/@dataSource’ el datasource que contendrá la configuración de usuarios básica del sistema.
<Configuration> <AddAdmin>true</AddAdmin> <AdminRole>admin</AdminRole> <AdminUser> <UserName>admin</UserName> <Password>CHANGE_ME</Password> </AdminUser> <EveryOneRoleName>everyone</EveryOneRoleName> <Property name="isCascadeDeleteEnabled">true</Property> <Property name="initializeNewClaimManager">true</Property> <Property name="dataSource">jdbc/WSO2UM_DB</Property> </Configuration>
b. Borrar la configuración por defecto que utiliza como user store principal, donde se guardarán los usuarios y sus datos, LDAP.
c. Configurar el user store principal con nuestra BBDD. En este caso no nos servirá indicar únicamente el nombre del datasource configurado.
<UserStoreManager class="org.wso2.carbon.user.core.jdbc.JDBCUserStoreManager"> <Property name="TenantManager">org.wso2.carbon.user.core.tenant.JDBCTenantManager</Property> <Property name="url">jdbc:mysql://localhost:3306/JWT_UM_DB</Property> <Property name="userName">root</Property> <Property encrypted="false" name="password">CHANGE_ME</Property> <Property name="driverName">com.mysql.jdbc.Driver</Property> ... </UserStoreManager>
4. Modificar el fichero ‘/repository/conf/identity/embedded-ldap.xml’ para deshabilitar el LDAP configurado por defecto. Tendremos que poner ‘false’ en el valor de la propiedad ‘/EmbeddedLDAPConfig/EmbeddedLDAP/Property/@enable’.
5. Modificar el fichero ‘/repository/conf/identity/identity.xml’ y configurar la BBDD que contendrá la información sobre tokens y/o suscripciones.
<Server xmlns="http://wso2.org/projects/carbon/carbon.xml"> <JDBCPersistenceManager> <DataSource> <Name>jdbc/WSO2AM_DB</Name> </DataSource> .... </JDBCPersistenceManager> </Server>
3. Configuración de API Manager e Identity Server para el uso de JWT
El siguiente paso será configurar ambos productos para que se comuniquen correctamente entre sí y permitan, por un lado obtener los access token del IS-KM, y por otro lado, el paso de claims desde el API al backend.
En ambos productos debemos configurar:
1. Cambiar el fichero ‘/repository/conf/api-manager.xml’ y descomentar las variables que indicamos a continuación, así habilitaremos el uso de JWT en el producto.
a. ClaimsRetrieverImplClass. Clase que nos permite obtener las user claims
b. ConsumerDialectURI. Dialecto asociado a las claims.
c. SignatureAlgorithm. Algoritmo de seguridad con el que encriptar las claims. Indicaremos ‘NONE’ para el ejemplo, para más seguridad indicar ‘SHA256withRSA’.
Así será la configuración para el IS-KM:
2. Cambiar el fichero ‘/repository/conf/carbon.xml’ para indicar otro offset en el caso de que tengamos ambos productos desplegados en la misma máquina.
La configuración para el API Manager deberá ser:
3. Cambiar el fichero ‘/repository/conf/api-manager.xml’
a. Para indicar el gestor de autenticaciones a través de las propiedades ‘AuthManager’. Tal como comentamos antes, esta gestión se realizará a través de los web services de administración del propio IS-KM y esa será la URL que debemos indicar. ¡Atento al cambio de offset en el IS-KM!:
<AuthManager> <ServerURL>https://localhost:9444${carbon.context}services/</ServerURL> <Username>CHANGE_ME</Username> <Password>CHANGE_ME</Password> <CheckPermissionsRemotely>false</CheckPermissionsRemotely> </AuthManager>
b. Modificar la propiedad ‘APIKeyValidator’ con los datos anteriores e indicando que la validación se realizará a través de servicio web y no con el servidor Thrift
<APIKeyValidator> <ServerURL>https://localhost:9444${carbon.context}services/</ServerURL> <Username>CHANGE_ME</Username> <Password>CHANGE_ME</Password> <KeyValidatorClientType>WSClient</KeyValidatorClientType> <ThriftClientConnectionTimeOut>10000</ThriftClientConnectionTimeOut> <EnableThriftServer>false</EnableThriftServer> <ThriftServerHost>localhost</ThriftServerHost> <KeyValidationHandlerClassName>org.wso2.carbon.apimgt.keymgt.handlers.DefaultKeyValidationHandler</KeyValidationHandlerClassName> </APIKeyValidator>
4. Ejemplo entre API Manager e Identity Server
Tras todo lo comentado anteriormente, ya tendríamos correctamente configurados ambos productos y solo deberíamos arrancarlos para comenzar la prueba.
Los pasos a realizar para obtener los resultados del ejemplo podrían ser los siguientes:
- Crear una API a través del API Publisher
- Asociar a dicha API un Enterprise Integrator (EI) en el backend o algún mock que nos permita ver las cabeceras en la llamada a dicho backend.
- Crear un usuario y darle permisos para subscribirse a APIs a través de la consola de administración (nos vale la del IS o la del APIM ya que tenemos la misma BBDD).
- Logarse en el API Store con el usuario creado, crear una aplicación y subscribirse a la API de pruebas creada en el punto 1.
- Obtener el token para la aplicación del usuario creado.
- Llamar a la API con el token de acceso.
Si estamos ‘logando’ correctamente en el backend o utilizando un mock para el mismo, podremos ver la información de las cabeceras en la llamada al mismo. La información que obtendremos será similar a esta:
Aunque el token JWT decidimos no cifrarlo, tendrá al menos una codificación en Base64. No obstante, si queremos ver correctamente su contenido podemos usar webs del tipo: https://jwt.io/.
Tras este tutorial seguro que ya estás un poco más preparado para explotar más funcionalidades de WSO2 Identity Server y API Manager.
¡Nos leemos en el siguiente post!