.png)
Buenas a todos,
en capítulos anteriores, vimos como "Proteger los datos: Se un mejor cerrajero", en el que explicábamos como proteger nuestros recursos aplicando un control extra al acceso mediante OAUTH2 que nos ofrece Intersystems. Y como no hay 2 sin 3, aquí tenemos un tercer articulo, en el cual vamos a explicar como "avisar a la policía" de que alguien malicioso está intentando acceder a nuestros datos.
Al finalizar el mismo, comentábamos que si quisiéramos podríamos tener un mayor control de estos accesos incorrectos y esto nos lleva a este artículo, por lo que seguiremos el siguiente índice, en el cual podréis encontrar todos los apartados en formato tutorial, de forma que siguiendo paso a paso lograreis llegar al objetivo:
1.- Introducción
1.1.- De donde venimos
2.- Configuración del "091"
2.1.- Arquitectura de las alertas
2.2.- ¿Quién es quién?
2.2.1.- Operación
2.2.2.- Proceso
2.3.- ¿Qué es un bot de telegram?
2.3.1.- Bot
2.3.2.- Grupos
2.3.3.- Configuración de la alerta
3.- Actualización de consulta de usuario
3.1.- Usuario de nuestro token por Endpoints Relacionados con Autenticación (OAUTH)
3.2.- Endpoints útiles para OAUTH
3.3.1.- token
3.3.2.- introspection
3.3.3.- registration
3.3.4.- revocation
1.- Introducción
1.1.- De donde venimos
En el articulo anterior (Proteger los datos: Se un mejor cerrajero) explicábamos como conocer el usuario ("2.1.- ¿Cómo funciona la llave (Token)?") mediante el uso de JWT extrayendo el parámetro "aud" y en base a este valor validábamos por LookUp Table su permiso de acceso a unos recursos o no. Si un usuario no esta autorizado a acceder a determinados recursos, necesitamos saber de estos intentos maliciosos de acceso para poder actuar.
En los siguientes pasos explicaremos la configuración de las alertas por mensajería push así como mejorar la obtención del usuario y mas funciones de las URLS de OAUTH2.
Estas alertas por mensajería push las realizaremos por medio de Telegram, aplicación de mensajería instantánea que nos permite recibir mensajes de texto en tiempo real. Este es el gran punto a su favor para esta situación, pues en el mismo momento que ocurra un intento indebido de acceso a nuestros recursos, lo sabremos y podremos actuar lo antes posible para proteger nuestros datos. Al mismo tiempo, al utilizar una aplicación de este tipo, podremos recibir la notificación en cualquier lugar del mundo, pues, mientras tengamos conexión a internet, esta notificación nos va a llegar.
Con todo esto, ¡VAMOS AL LIO!
2.- Configuración del "091"
2.1.- Arquitectura de las alertas

Partimos de un esquema como el mostrado, donde queremos generar un aviso a nuestro dispositivo móvil en el momento en el que detectemos que hay algún problema, en este caso, un acceso indebido a nuestros recursos.
Por lo tanto, teniendo ya montada nuestra integración, lo que vamos a hacer ahora es añadir un modulo de alertas para Telegram. Este modulo consistirá en 2 componentes principales:
2.2.- ¿Quién es quién?
2.2.1.- Operación
Nuestra operación de envío de alertas a Telegram realizará notificaciones REST vía Telegram. Esta operación “simulara” ser un bot en la aplicación de Telegram y desde el ESB se controlará este bot, que emitirá las notificaciones a los grupos y/o personas según los acuerdos alcanzados para cada caso. Estas notificaciones serán inmediatas pudiendo aplicar un control temporal para no saturar los dispositivos móviles de las personas notificadas.
Estas notificaciones podrán sustituir y/o complementar a las alertas ya vigentes (correo electrónico). Ya que su objetivo es que las personas responsables de cada aplicativo sean informadas lo antes posible de problemas ocurridos.
2.2.2.- Proceso
Nuestro proceso orquestará la configuración y parametrización de las notificaciones REST vía Telegram. Las diferentes casuísticas que consideremos realizarán llamadas a este proceso enviando la información solicitada para cada caso y este proceso preparará los mensajes personalizados para cada caso.
Llegados a este punto, sabemos que vamos a utilizar una operación rest para notificar vía Telegram, pero nos queda explicar lo mas importante, ¿Qué es un bot de telegram? A continuación lo explicamos.
2.3.- ¿Qué es un bot de telegram?
Para la notificación instantánea de mensajería que se pretende implantar haremos uso de una de las principales funcionalidades de código abierto que nos da la API de Telegram como es la creación y administración de bots.
2.3.1.- Bot
Un bot es una funcionalidad de Telegram, la cual consiste en la creación de un perfil de usuario de Telegram que se controla por terceros gracias a unos parámetros de configuración definidos por la API. Esta funcionalidad simula ser un usuario de Telegram, y como todo usuario, puede enviar mensajería a través de la aplicación por medio de configuraciones del bot. En el ESB configuraremos este bot para que de forma automatizada envíe notificaciones a los diversos grupos que se hayan acordado.
Para la creación de un bot que utilizaremos para enviar notificaciones vía Telegram, será necesario hacer uso de la funcionalidad ofrecida por el @botfather de Telegram.
El @botfather es una funcionalidad de Telegram para controlar a otros bots y crearlos por cuenta propia y es una de las recomendaciones de los responsables de Telegram para crear de una forma más fácil tu propio bot. Esta funcionalidad dispone de una variedad amplia de comandos para configurar nuestro bot.
Para empezar a crear nuestro bot, deberemos de seguir los siguientes pasos (7):
1.- Abrir una conversación con @botfather
2.- Pulsar en Start/Empezar para empezar a chatear con el
3.- Escribiendo “/help” podremos visualizar los diversos comandos, y empezaremos por el comando “/newbot”

4.- Una vez introducimos el comando “/newbot” nos mostrará lo siguiente:

5.- Deberemos elegir un nombre para nuestro bot:
.png)
6.- A continuación le asignaremos un username a nuestro bot:
.png)
7.- Una vez asignado el username nos contestará que hemos conseguido crear nuestro bot, indicándonos Token único el cual deberemos guardar de forma segura y protegida ya que este Token es el que nos permitirá manejar nuestro bot de forma automatizada y sólo debe estar disponible para las personas autorizadas para el manejo del bot.
2.3.2.- Grupos
Para realizar las notificaciones se hará mediante grupos privados de Telegram, en los cuales se incluirá en cada grupo a las personas acordadas.
Para crear un grupo deberemos abrir la aplicación de Telegram y seguir los siguientes pasos:
1.- Opciones > Nuevo Grupo

2.- Introducir el nombre del Grupo siguiendo la nomenclatura acordada
.png)
3.- En la Gestión del Canal, se deberá configurar el canal como privado para que solo puedan entrar en el canal las personas invitadas.
4.- El siguiente paso será añadir a las personas que deben estar en cada grupo.
5.- Una vez añadidos los invitados, ya podrán visualizar en el grupo las notificaciones que el bot envíe de forma automatizada.
2.3.2.- Configuración de la alerta
Ahora que ya tenemos nuestro bot y nuestro grupo de Telegram donde van a llegar las notificaciones, debemos orquestar en el ESB como enviarlas.
Os dejo por aquí un esqueleto de operación de envío de peticiones:
Class Operaciones.REST.Notificaciones.Telegram Extends EnsLib.REST.Operation
{
Parameter INVOCATION = "Queue";
Method notificar(pRequest As Mensajes.Request.Notificaciones.TelegramRequest, Output pResponse As Mensajes.Response.Notificaciones.TelegramResponse) As %Status
{
set pResponse = ##class(Mensajes.Response.Notificaciones.TelegramResponse).%New()
try {
set url = ##class(Util.TablasMaestras).getValorMaestra("BOT_TELEGRAM","url")
// A través de una LookUp Table, guardamos el TOKEN del bot de telegram que va a enviar el mensaje
set bot = ##class(Util.TablasMaestras).getValorMaestra("BOT_TELEGRAM","bot")
// A través de una LookUp Table, guardamos el metodo de la API Telegram para envío de notificaciones
set method = ##class(Util.TablasMaestras).getValorMaestra("BOT_TELEGRAM","method")
// A través de una LookUp Table, guardamos el ID del chat de telegram donde vamos a enviar el mensaje
set chatId = ##class(Util.TablasMaestras).getValorMaestra("BOT_TELEGRAM",pRequest.aplicacion)
set URL = url_bot_method_chatId_"&text="_pRequest.mensaje
set tSC=..Adapter.PostURL(URL,.tHttpResponse)
Set textoError = $System.Status.GetErrorText(tSC)
If $$$ISERR(tSC)
{
While (tHttpResponse.Data.AtEnd = 0) {
set linea = tHttpResponse.Data.Read()
}
} else {
While (tHttpResponse.Data.AtEnd = 0) {
set linea = tHttpResponse.Data.Read()
}
}
} catch {
Set tSC=$$$SystemError
Set tSC=$$$SystemError
set textoError = $System.Status.GetErrorText(tSC)
set pResponse.return = "010 - "_textoError
}
Quit $$$OK
}
XData MessageMap
{
<MapItems>
<MapItem MessageType="Mensajes.Request.Notificaciones.TelegramRequest">
<Method>notificar</Method>
</MapItem>
</MapItems>
}
}
En la misma podéis observar que se necesita atacar a la URL con unos parámetros determinados:
https://api.telegram.org/bot1234567890:[TOKEN]/sendMessage?chat_id=[CHATID]&text="_pRequest.mensaje
En este punto, podemos ver que hay un valor que no disponemos, que es el CHAT ID. Este valor es muy fácil de obtener, pues con reenviar un mensaje desde nuestro nuevo grupo al @userinfobot de Telegram, este nos devolverá el ID del grupo como muestro a continuación:
.png)
La producción nos debería quedar algo así:
.png)
Una vez enviamos la petición REST desde el ESB, podemos ver la notificación en el Telegram.
Esta operación nos vale para todo caso que queramos notificar, pues el Proceso anteriormente indicado, será el encargado de orquestar a que grupo llamar (CHAT ID) y que mensaje enviar (adaptado a cada caso), De forma que podamos tener N grupos y N mensajes diferenciados utilizando el mismo Bot.
A continuación os dejo un breve video explicativo del proceso completo:
Como podéis observar, siguiendo los pasos de esta guía, podréis notificar a quien queráis de cualquier cuestión que ocurra en el ESB de forma inmediata. ¡Así que espero que os sea de ayuda!
No puedo terminar este articulo sin incluir una mejora del articulo anterior, y esta es la utilización de la consulta del usuario así como el uso de algunas de las URLs que nos ofrece el servidor OAUTH2 de Intersystems. Todo esto lo explicamos a continuación.
3.- Actualización de consulta de usuario
3.1.- Usuario de nuestro token por Endpoints Relacionados con Autenticación (OAUTH)
A continuación os explico una forma mas sencilla y reutilizable de obtener este dato de forma que podamos aplicarlo para múltiples procesos.
En el primer articulo ("Como controlar el acceso a tus recursos con OAuth2") en el que explicábamos como preparar el servidor OAUTH2 de Intersystems, indicábamos que la URL del servidor era:
https://DNS/IP:57773/oauth2
Una vez creamos el servidor, en su configuración podemos ver:
Sistema > Gestión de seguridad > Cliente de OAuth 2.0 > Descripción del servidor - (Configuración de seguridad)
Como vemos, hay varias URLs que nos crea el servidor. Una de ellas es para consultar la información del usuario (userinfo) en OAuth 2.0, podemos utilizar la siguiente URL:
https://DNS/IP:57773/oauth2/userinfo
El procedimiento a seguir es enviar una solicitud GET al punto final UserInfo, incluyendo el token de acceso en el encabezado de la solicitud de autorización. El encabezado suele ser "Authorization: Bearer [token_de_acceso]". Para ello os muestro el paso a paso:
Obtener el Token para el usuario "bezkfeZoA3mU2g1dmABlvgv9k1AKHN78JtIgXgcdQeQ":
Utilizando este Token hacia la URL de UserInfo obtenemos el usuario:
Como podéis ver, con un simple GET a esa URL y el token que ya disponemos, nos devuelve "sub" que es el "aud" que obteníamos por el JWT. De forma que podemos montarnos una operación REST en el ESB que realice esta operación y tendremos la respuesta inmediata.
Os dejo por aquí una estructura de una operación REST para que podáis estructurar vuestras pruebas:
Class Operaciones.REST.OAUTH2.Gestion Extends EnsLib.REST.Operation
{
Method consultaUsuario(pRequest As Mensajes.Request.OAUTH2.consultaUsuario, Output pResponse As Mensajes.Response.OAUTH2.consultaUsuario) As %Status
{
set pResponse = ##class(Mensajes.Response.OAUTH2.consultaUsuario).%New()
try {
set url = "IP"
set port = "PUERTO"
set method = "/oauth2"
set method2 = "/userinfo"
set URL = "https://"_url_":"_port_method_method2
set tSC = ..Adapter.GetURL(URL,.res)
If $$$ISERR(tSC)
{
} else {
set linea = ""
While (res.Data.AtEnd = 0) {
set linea = linea_res.Data.Read()
}
set claseAux = ##class(%ZEN.Auxiliary.jsonProvider).%New()
set tSC= claseAux.%ConvertJSONToObject(.linea,"Mensajes.Response.OAUTH2.consultaUsuario",.objeto,1)
}
} catch {
}
Quit $$$OK
}
XData MessageMap
{
<MapItems>
<MapItem MessageType="Mensajes.Request.OAUTH2.consultaUsuario">
<Method>consultaUsuario</Method>
</MapItem>
</MapItems>
}
}
3.2.- Endpoints útiles para OAUTH
La URL del UserInfo la podemos obtener llamando a la URL:
https://DNS/IP:PUERTO/oauth2/.well-known/openid-configuration
La cual nos entrega el siguiente JSON que os sonará:
{
"issuer" : "https://DNS/IP:PUERTO/oauth2",
"authorization_endpoint" : "https://DNS/IP:PUERTO:57776/oauth2/authorize",
"token_endpoint" : "https://DNS/IP:PUERTO:57776/oauth2/token",
"userinfo_endpoint" : "https://DNS/IP:PUERTO/oauth2/userinfo",
"revocation_endpoint" : "https://DNS/IP:PUERTO/oauth2/revocation",
"introspection_endpoint" : "https://DNS/IP:PUERTO/oauth2/introspection",
"jwks_uri" : "https://DNS/IP:PUERTO/oauth2/jwks",
"registration_endpoint" : "https://DNS/IP:PUERTO/oauth2/register",
"scopes_supported" : [ "openid", "profile", "email", "address", "phone", "my/scope" ],
"response_types_supported" : [ "code" ],
"response_modes_supported" : [ "query", "fragment", "form_post" ],
"grant_types_supported" : [ "authorization_code", "client_credentials", "refresh_token" ],
"id_token_signing_alg_values_supported" : [ "HS256", "HS384", "HS512", "RS256", "RS384", "RS512" ],
"id_token_encryption_alg_values_supported" : [ "none", "RSA1_5", "RSA-OAEP", "A128KW", "A192KW", "A256KW", "dir" ],
"id_token_encryption_enc_values_supported" : [ "none", "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512" ],
"userinfo_signing_alg_values_supported" : [ "none", "HS256", "HS384", "HS512", "RS256", "RS384", "RS512" ],
"userinfo_encryption_alg_values_supported" : [ "none", "RSA1_5", "RSA-OAEP", "A128KW", "A192KW", "A256KW", "dir" ],
"userinfo_encryption_enc_values_supported" : [ "none", "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512" ],
"access_token_signing_alg_values_supported" : [ "none", "HS256", "HS384", "HS512", "RS256", "RS384", "RS512" ],
"access_token_encryption_alg_values_supported" : [ "none", "RSA1_5", "RSA-OAEP", "A128KW", "A192KW", "A256KW", "dir" ],
"access_token_encryption_enc_values_supported" : [ "none", "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512" ],
"request_object_signing_alg_values_supported" : [ "none", "HS256", "HS384", "HS512", "RS256", "RS384", "RS512" ],
"request_object_encryption_alg_values_supported" : [ "none", "RSA1_5", "RSA-OAEP", "A128KW", "A192KW", "A256KW", "dir" ],
"request_object_encryption_enc_values_supported" : [ "none", "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512" ],
"token_endpoint_auth_methods_supported" : [ "client_secret_post", "client_secret_basic", "client_secret_jwt", "private_key_jwt" ],
"token_endpoint_auth_signing_alg_values_supported" : [ "HS256", "HS384", "HS512", "RS256", "RS384", "RS512" ],
"claims_supported" : [ "preferred_username", "email", "email_verified", "name", "phone_number", "phone_number_verified", "iss", "sub", "aud", "exp", "auth_time", "jti" ],
"ui_locales_supported" : [ "de", "en", "en-us", "es", "fr", "it", "ja", "ko", "nl", "pt-br", "ru", "uk", "zh-cn" ],
"claims_parameter_supported" : true,
"request_parameter_supported" : true,
"request_uri_parameter_supported" : true
}
En esta lista de URLs vemos algunas interesantes como son:
"userinfo_endpoint" : "https://DNS/IP:PUERTO/oauth2/userinfo"
"token_endpoint" : "https://DNS/IP:PUERTO:57776/oauth2/token"
"introspection_endpoint" : "https://DNS/IP:PUERTO/oauth2/introspection"
"registration_endpoint" : "https://DNS/IP:PUERTO/oauth2/register"
"revocation_endpoint" : "https://DNS/IP:PUERTO/oauth2/revocation"
2.3.1.- token
La URL "userInfo" ya le hemos explicado en este articulo, por lo que a continuación vamos a explicar "token_endpoint"; pues es la que utilizamos en capítulos anteriores para obtener el token.
Para consumir la URL "https://DNS/IP:PUERTO:57776/oauth2/token" deberemos realizar un GET con los siguientes parámetros de configuración:
Params:
Grant_type= "client_credentials"
scope = "/my/scope"
Authorization:
Type: Basic Auth
user: el generado por el servidor de Autorización
password: generada por el servidor de Autorización
.png)
Body
Seleccionamos "x-www-form-urlencoded" e informamos con el "clientID"
La respuesta de esta llamada será el Token:
.png)
Esta llamada se podrá configurar a través de una operación REST como comentábamos anteriormente.
3.3.2.- introspection
La siguiente URL que vamos a explicar es "introspection_endpoint". Que es la que nos permite validar un token en el servidor de autorización para verificar su estado y obtener más detalles sobre él (como sus permisos, emisor, usuario asociado, etc.). Este endpoint normalmente se utiliza en arquitecturas donde un recurso protegido requiere verificar la validez de un token antes de otorgar acceso.
Para consumir la URL "https://DNS/IP:PUERTO:57776/oauth2/introspection" deberemos realizar un POST con los siguientes parámetros de configuración:
Headers
.png)
El valor del header "Authorization" se obtiene codificando en Base64 la combinación de "client_id" y "client_secret" separados por ":".
Body
.png)
En el body, selecciona la opción x-www-form-urlencoded en Postman y agrega los siguientes parámetros:
- token: El token que deseas validar.
- token_type_hint (opcional): Puedes indicar si el token es un access_token o un refresh_token
El resultado de la llamada será algo así:
{
"active": true,
"scope": "my/scope",
"client_id": "bezkfeZoA3mU2g1dmABlvgv9k1AKHN78JtIgXgcdQeQ",
"username": "",
"token_type": "bearer",
"exp": 1748778056,
"sub": "bezkfeZoA3mU2g1dmABlvgv9k1AKHN78JtIgXgcdQeQ",
"aud": "bezkfeZoA3mU2g1dmABlvgv9k1AKHN78JtIgXgcdQeQ",
"iss": "https://DNS/IP:PUERTO/oauth2"
}
Explicación de los campos:
- "active": true: El token es válido.
- "scope": "my/scope": Permisos asociados al token.
- "client_id": "bezkfeZoA3mU2g1dmABlvgv9k1AKHN78JtIgXgcdQeQ": El cliente que generó el token.
- "username": "": (opcional) Usuario asociado al token.
- "exp": Tiempo de expiración (en formato UNIX timestamp).
- "sub": Identificador único del usuario.
- "aud": Audiencias permitidas.
- "iss": Emisor del token.
3.3.3.- registration
La siguiente URL que vamos a explicar es "registration_endpoint" . Este endpoint permite que las aplicaciones cliente dinámicamente se registren en el servidor de autorización sin necesidad de configurar manualmente las credenciales en el servidor.
Para consumir la URL "https://DNS/IP:PUERTO:57776/oauth2/register" deberemos realizar un POST con los siguientes parámetros de configuración:
Headers
.png)
Informar el campo "Content-Type" con el valor "application/json".
Body
.png)
Os dejo el JSON del body en formato texto. Importante el "client_name" para poder localizar en el portal de gestión nuestro usuario recién creado:
{
"redirect_uris": [
"https://client.example.com/callback",
"https://client.example.com/auth"
],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"client_name": "MiCLIENTE",
"token_endpoint_auth_method": "client_secret_basic",
"scope": "my/scope"
}
Tras realizar la llamada veremos la siguiente respuesta con toda la información de creación del usuario:
{
"client_id": "8IFeFdbCiz7CAJ86Bxz4aZUX8s3W-TP3Litmpx4vqOM",
"client_secret": "6nWT1lL4BOcM0tYz8zTvzgtSqiGpDOXyj-3-MZCa2ei9QMWxoTMKM49LdWrk8XlQYXaZpsVxuTnzv924L52NSw",
"registration_access_token": "4v5mCm0w0MP6uA0YnZpSTD0ib-XFYnE4i0vlA9QWC9sas9BsSxH8Mipij5XOUvEv5yDRtXKkHmMMLhZjCd5LHw",
"registration_client_uri": "https://DNS/IP:PUERTO/oauth2/register?client_id=8IFeFdbCiz7CAJ86Bxz4aZUX8s3W-TP3Litmpx4vqOM",
"client_id_issued_at": 1748779129,
"client_secret_expires_at": 0,
"redirect_uris": [
"https://client.example.com/callback",
"https://client.example.com/auth"
],
"response_types": [
"code"
],
"grant_types": [
"authorization_code",
"refresh_token"
],
"application_type": "web",
"client_name": "MiCLIENTE",
"id_token_signed_response_alg": "RS256",
"token_endpoint_auth_method": "client_secret_basic"
}
En el portal de gestión, podemos confirmar la creación del usuario:
.png)
Así como confirmar el valor del "clientSecret" y el "clientID" que nos ha devuelto la llamada:
.png)
Objetivo conseguido, ¡USUARIO CREADO!
3.3.4.- revocation
La siguiente URL que vamos a explicar es "revocation_endpoint". Que es el endpoint definido en el estándar OAuth 2.0 que permite revocar tokens emitidos previamente por un servidor de autorización
Para consumir la URL "https://DNS/IP:PUERTO:57776/oauth2/revocation" deberemos realizar un POST con los siguientes parámetros de configuración:
Authorization
.png)
Informamos "Basic Auth" con nuestro "client_id" y nuestro "secret_client"
Header
.png)
Informamos el "Content-Type" con ""application/x-www-form-urlencoded".
Body
.png)
Informamos el "token" que queremos revocar y el "token_type_hint" con "access_token".
Finalmente tendremos un HTTP 200 como respuesta de la llamada:
.png)
De esta forma quedará el Token revocado.
Con esta revocación del token termina este articulo. Espero que les sea de utilidad y les sirva para ponerse al día en cuanto a aplicación de seguridad en nuestras plataformas, pues todo esto lo podemos gestionar desde nuestro portal de gestión de Intersystems.
Finalmente, espero que se conviertan ¡en el mejor equipo de seguridad posible!
Muchas gracias por el tiempo que han dedicado a esta lectura.