Introducción a Keycloak, OAuth 2.0 y JWT

Keycloack es un Proveedor de Identidad (IdP) Open Source y gratuito, desarrollado en Java por JBoss (división de Red Hat, adquirida por IBM en 2019) sobre el servidor de aplicaciones WildFly, con Licencia Apache 2.0 (permite su uso comercial), y con gran adopción en el mercado, existiendo una versión comercial de pago: RedHat Single-Sign-On. Keycloack proporciona Single-Sign On (tanto con SAML 2.0 como con OpenID Connect), multi tenancy (crear servidores de identidad virtuales dentro de un mismo Keycloak), Login Social (ej: Facebook, Google, Twitter, Instagram, GitHub, etc), federación de identidades con LDAP y Active Directory, y un montón de opciones más para poder personalizarlo a tu gusto, y que tú te puedas concentrar en tu aplicación, que de la identidad, ya se encargan ellos.

Existen un montón de soluciones IdP, tanto gratuitas y/o Open Source, como de pago y/o propietarias, y Keycloak es una de ellas. Aunque a vista de pájaro son todas muy parecidas y se basan en los mismos estándares, entrando en detalle hay muchas diferencias. Cuál es la solución que más nos conviene, depende mucho de nuestras necesidades concretas, así como de las funcionalidades, detalles y soporte de cada IdP (ej: opciones de Login Social, implementación de multi-tenancy, disponibilidad y SLA de un servicio de Soporte e incluso de una solución en SaaS, etc.). En este Post vamos a conocer más en detalle Keycloak, incluso como instalarlo para un entorno de laboratorio y pruebas, y cómo hacer algunas tareas con él.

Antes de entrar en más detalles de Keycloak, es importante conocer un poco OAuth 2.0, los access_token, y JWT.

Introducción a OAuth 2.0

OAuth 2.0 es un Framework que se ha convertido en el estándard para la autorización de APIs (usado por Google, Facebook, Tweeter, Microsoft, GitHub, Linkedin, etc), que permite compartir información del usuario entre diferentes Aplicaciones (cada una de la cuales puede ser un Sitio Web y/o una App móvil, etc), o dicho de otro modo, permite que el usuario delege en una Aplicación (da su consentimiento) la realización de ciertas acciones en su nombre (acceder a parte de sus datos, publicar en el murete de Facebook en nombre del usuario, o cualquier otra cosa), mediante el uso de diferentes flujos de autenticación (Grant Types), es decir, ofrece diferentes formas de obtener un token de acceso (access_token) que permitirá acceder un servicio HTTP (a una API). El acceso a las APIs se realizará siempre mediante un access_token que solicitaremos a un IdP, y que será validado por la API. Para un mayor nivel de detalle, se puede consultar:

Además, OAuth 2.0 evita tener que enviar continuamente las credenciales (ej: API Key, usuario/contraseña, etc) de forma sucesiva en cada llamada a una API (utilizando en su lugar un access_token), y en consecuencia, evitando tener que comprobarlas continuamente contra una base de datos. Esto es especialmente valorable cuando nuestra Aplicación accede a APIs de terceros (ej: generar contenido en Tweeter o Facebook desde otra Aplicación, en la que no tenemos las credenciales de Tweeter o Facebook del usuario en cuestión).

OpenID Connect (OIDC) es una capa de identidad construida sobre el protocolo OAuth 2.0, utilizado por los diferentes IdPs existentes. Para más detalle, se puede consultar la Web de OpenID Connect.

OpenID Connect proporciona muchas cosas, como por ejemplo, un mecanismo de descubrimiento (OIDC Discovery) que consiste en publicar ciertos metadatos (ej: URLs de sus Endpoints, URLs de sus claves públicas, etc.) a través de una URL conocida (OpenID Configuration Endpoint, que suele acabar en /.well-known/openid-configuration), o también proporciona el Endpoint UserInfo donde consultar la información de un usuario mediante un access_token, por poner un par de ejemplos.

OAuth 2.0 define los siguientes cuatro Roles (las partes involucradas).

  • Resource Owner (User). Se trata habitualmente del Usuario que da autorización a una Aplicación para acceder a su cuenta, de forma limitada a un scope determinado, aunque hay alguna excepción como es el caso de las comunicaciones Machine-to-Machine (M2M) en las que no hay un Usuario (ej: un cronjob). Se le denomina Resource Owner, porque aunque la API no es suya (del Usuario), los datos a los que autoriza su acceso si que lo son (ej: los datos de su cuenta de usuario).
  • Client (Application). Se trata de la Aplicación que quiere acceder a los datos del usuario, en su nombre. Para poder hacerlo, debe ser autorizada por el usuario, y dicha autorización, debe ser validada por la API. Puede ser una Aplicación Web, IOS, Android, Smart TV, un dispositivo IoT, etc. Podemos diferenciar dos tipos de Clientes, que condicionarán la forma recomendada para obtener los access_token.
    • Clientes Confidenciales (Clientes Privados). Son capaces de guardar una contraseña (Client Secret) a salvo sin que pueda ser interceptada por el usuario (ej: un Backend , una Web con Server-Side Rendering, etc).
    • Clientes Públicos. No son capaces de guardar una contraseña (Client Secret) a salvo, básicamente se trata de Aplicaciones que se ejecutan en un dispositvo bajo el control del Usuario (ej: Native Apps, JavaScript Apps – SPAs).
  • Resource Server ó Protected Resource (API). Se trata de la API a la que quiere acceder la Aplicación, para acceder a los datos del Usuario. Debe ser capaz de aceptar y validar los access_tokens.
  • Authorization Server (IdP). Se trata del Proveedor de Identidad (IdP) que gestiona las peticiones de autorización (verifica la identidad del usuario y emite el access_token), y en el cual se registran las Aplicaciones (Clientes) y los usuarios. Proporciona, entre otros, los siguientes Endpoints:
    • Autorización (/authorize). Para la interacción con usuarios, permite por ejemplo que el IdP muestre un formulario de Login, para que el usuario se autentique.
    • Obtención de access_token (/token). Para la interacción entre máquinas, obteniendo como resultado un access_token.
    • Autorización de Dispositivo (/device). Para su uso en el flujo Device Code.

Para poder utilizar OAuth 2.0 en nuestra Aplicación, tendremos que registrar nuestra Aplicación (Cliente) en el Proveedor de Identidad (IdP), donde habitualmente tendremos que especificar entre otras cosas el valor de Redirect URI ó Callback URL (necesario en los Flujos que utilizan redirecciones, de tal modo que una vez el IdP ha realizado la autorización, redireccionará a la Aplicación, donde se gestionará el código de autorización y/o el access_token, y así limitar las redirecciones sólo a las URI o URLs de confianza), y de donde obtendremos el Client ID (identificador de nuestra Aplicación en el IdP) y el Client Secret (es una contraseña, sólo necesaria en algunos Flujos).

OAuth 2.0 define varios Flujos ó Grant Types (algunos ya han quedado obsoletos y se recomienda no usarlos) los cuales relacionan las anteriores cuatro partes, con el fin de obtener el access_token que permita el acceso al recurso protegido (API) de forma segura y con el consentimiento del Usuario en los casos en que aplica. El Flujo a elegir dependerá también del tipo de Aplicación (Clientes Públicos ó Clientes Privados/Confidenciales), siendo posible extenderlo para añadir Flujos nuevos. Por generalizarlo, y exponerlo de una forma didáctica, podríamos resumirlo en el siguiente Flujo Abstracto: Una Aplicación (Cliente) quiere acceder a un recurso protegido (API) para lo cual solicita autorización al Usuario a través de del IdP (el Usuario tendrá que logarse o identificarse, si no lo ha hecho antes). Al concederla, el IdP devuelve un access_token, que permitirá a la aplicación el acceso al recurso protegido (API) utilizando el access_token en una cabecera, que la API validará.

Un access_token suele tener una longitud de vida corta, ya que es compartido con las APIs a las que se quiere obtener acceso (está expuesto). Por otro lado, en ocasiones es posible utilizar un refresh_token, sólo son intercambiados entre el Cliente y el IdP (no se expone a la API), por lo que tienen una longitud de vida mucho mayor, permitiendo implementar el Flujo de Refresh Token para obtener un nuevo access_token cuando éste ha expirado, sin intervención del Usuario (sin que el Usuario tenga que volver a identificarse e iniciar sesión, etc), con un simple POST.

Flujos de autenticación (Grant Types) en OAuth 2.0

La siguiente tabla resume los principales Flujos (Grant Types) de OAuth 2.0, aunque hay algunos más.

Flujos OAuth2Tipos de ClientesResumen del Flujo
Implicit FlowClientes Públicos: Native Apps & JavaScript Apps (SPAs)Legacy, no recomendado.
Después de la autenticación del usuario, devuelve el access_token en la URL de respuesta, visible al Usuario final, y no usa ningún código extra de autorización.
En muchos IdP está deshabilitado por defecto.
Usar Authorization Code with PKCE en su lugar.
Consiste en una única llamada GET al Endpoint /authorize
No usa el Client Secret
Authorization Code FlowClientes Privados (ej: Server Side Rendereing)Consiste en una llamada GET al Endpoint /authorize que devuelve el código en la URL de respuesta, que utilizaremos en una siguiente llamada POST al Endpoint /token con el Client Secret
El código es válido sólo 60s y es de un sólo uso
Requiere el Client Secret
Authorization Code Flow with PKCEClientes PúblicosConsiste en una llamada GET al Endpoint /authorize que devuelve el código en la URL de respuesta, que utilizaremos en una siguiente llamada POST al Endpoint /token
El código es válido sólo 60s y es de un sólo uso
No usa el Client Secret
Client Credentials FlowClientes Privados: aplicaciones Machine-to-Machine (M2M), CLIs, Demonios, Servicios de un Backend (ej: cronjob), etc.Permite obtener un access_token fuera del contexto de un Usuario, accediendo a su propia cuenta de servicio.
Identifica a la propia Aplicación en su nombre, no a un Usuario.
Envía las credenciales de la Aplicación (Client ID y Client Secret).
Al no haber Usuario, no permite MFA ni cuentas delegadas.
No usa redirecciones.
Consiste en una única llamada POST al Endpoint /token
Requiere el Client Secret
Device Code FlowClientes PúblicosEs una extensión de OAuth 2.0 para dispositivos sin navegador o con capacidades de entrada reducidas (ej: sin teclado), como una SmartTV, una consola, una aplicación de línea de comandos, etc.
El disposito hace una llamada POST al Endpoint /device que devuelve un código de dispositivo, un código de usuario y URL para que el Usuario valide el código de usuario desde otro dispositivo (iniciando sesión si no lo ha hecho antes) y acepte los consentimientos, mientras el dispositivo llama iterativamente con POST al Endpoint /token hasta obtener el access_token con éxito.
No usa el Client Secret
Resource Owner Password Credentials (ROPC) FlowClientes Públicos o PrivadosLegacy, no recomendado, especialmente si la API es ajena al IdP.
Envía las credenciales del Usuario (usuario y contraseña) a una WebApp, y la WebApp se los envía al IdP para obtener el access_token, que permitirá a la WebApp acceder a la API.
No permite MFA ni cuentas delegadas.
No usa redirecciones.
Consiste en una única llamada POST al Endpoint /token
Puede requerir o no el Client Secret, depende de la configuración de la Aplicación en el IdP
Refresh TokenClientes Públicos o PrivadosPermite obtener un nuevo access_token, cuando ya ha expirado el actual (Error 401), sin intervención del Usuario (es decir, sin que tenga que volver a autenticarse), utilizando el Refresh Token obtenido previamente (suele ser de un sólo uso, aunque depende de cada IdP)
Por defecto, no todos los Flujos OAuth2 devuelven un Refresh Token.
No usa redirecciones.
Consiste en una única llamada POST al Endpoint /token
Puede requerir o no el Client Secret, depende de la configuración de la Aplicación en el IdP

Para más información sobre estos flujos OAuth 2.0, además de los enlaces que ya comentamos antes, os paso alguno más que a mi me ha resultado de ayuda:

Los access_token y JWT

Como acabamos de ver, OAuth 2.0 nos ofrece diferentes formas (flujos, grant types, o como lo queramos llamar) de obtener un access_token. Por verlo un poco mejor, la respuesta que obtendremos al solicitar un access_token será similar al siguiente JSON, pudiendo variar dependiendo del IdP que estemos usando y de cómo esté configurado (tanto el IdP como el registro de nuestra Aplicación o Cliente), del flujo que estemos utilizando, etc.

En la respuesta podemos ver el access_token, que se trata de un Token JWT (JSON Web Token), es un standard abierto (RFC 7519) que permite crear tokens de acceso para las aplicaciones, proporcionando una forma segura de transmitir Claims (propiedades o información en general) en formato JSON entre dos partes, ya que está firmado digitalmente (Hash) con una clave (sea un clave privada como en nuestro caso, o bien con una clave secreta), por lo que se puede verificar fácilmente su integridad (con la correspondiente clave pública como en nuestro caso, o con una clave secreta): es decir, si alguien modifica un Token JWT, se volverá inválido, ya que la firma no se podrá validar correctamente. En la respuesta también podemos ver el refresh_token, y otros detalles.

El uso de Tokens JWT, a su vez permite en algunos casos aumentar la seguridad, por ejemplo enviando dentro del Token JWT la IP Pública de la máquina para la que se ha emitido el access_token, por lo que esta será firmada digitalmente, y al acceder a una API, podrá no sólo comprobar que se trata de un access_token válido, sino también que la IP Pública de la petición coincide con incluida en el cuerpo del Token JWT.

Si nos fijamos en el access_token de la imagen anterior (que es un Token JWT), está formado por tres partes separadas por puntos (aaa.bbb.ccc), cada una de las cuales está codificada en Base64:

  • Cabecera. Objeto JSON que principalmente proporciona información sobre cómo generar la firma digital del Token KWT, como el algoritmo utilizado para firmar el Token (ej: RS256 que sería RSA de 256 bits, HS256 que sería HMAC SHA256, etc.) así como el tipo de Token (ej: JWT), aunque también puede incluir más propiedades como el ID de la clave pública (kid) que deberemos utilizar para comprobar la firma del Token (en el caso de utilizar clave pública y privada, como ocurre con los access_token y los IdP).
  • Payload. Objeto JSON que consisten en el cuerpo del mensaje, formado por un conjunto de Claims (propiedades de nuestro Token, que suelen ayudar a describir a qué nos da derecho el mismo, o información en general que se desea transmitir en el Token JWT), que pueden variar según el IdP y su configuración, el registro de Aplicación y su configuración, etc., ya que básicamente podemos incluir aquí la información que deseemos. Algunos Claims comunes son los siguientes:
    • exp. Fecha y hora de expiración del Token, en formato Unix Timestamp.
    • iat. Fecha y hora en la que el Token fué emitido, en formato Unix Timestamp.
    • jti. Identificador del Token
    • iss. Identifica el emisor del Token (la URL del IdP).
    • aud. Audiencia para la que ha sido emitido el Token.
    • sub. Es el asunto (subject), suele consistir en el ID del Usuario que identifica el Token.
  • Firma. Consiste en la firma digital (Hash) de la cabecera y cuerpo (payload) del Token utilizando el algoritmo especificado en la propia cabecera, de tal modo que nos permita validar que se trata de un Token JWT válido, y que no ha sido modificado en tránsito. Para validar la firma, obtendremos la clave pública directamente del IdP (en el caso de OAuth 2.0 con un IdP) o la clave secreta utilizada (depende del caso).

A continuación se muestra el anterior Token JWT decodificado.

De la configuración OIDC de nuestro IdP, podemos obtener la propiedad jwks_uri, que contiene una URL que muestra en un JSON las claves públicas para comprobrar las firmas de nuestro Token JWT. En el siguiente pantallazo, se muestra el contenido de dicha URL (para un Tenant o Realm concreto, denominado willie, en una instalación de pruebas de Keycloak), en formato amigable. Podemos ver que hay dos claves (cada una con su kid), y una de ellas es la que deberemos utilizar para comprobar el Token JWT que acabamos de ver en los párrafos anteriores.

Otro detalle importante, y que podremos ver en más profundidad en este Post más abajo, es que se pueden personalizar los access_tokens para incluir la información (Claims) que necesitemos, por ejemplo, en base al valor de un Atributo de Usuario, etc.

Aunque se podría profundizar más, con esto debería ser más que suficiente para que tengamos un poco de base para empezar a conocer un poco más a Keycloak. Al fin y al cabo, en nuestro código acabaremos utilizando librerías existentes que implementarán muchas de estas cosas por nosotros, por lo que no hace falta que de entrada profundicemos mucho más. Continuamos.

Introducción a Keycloak

Keycloak proporciona Single-Sign On (SSO) de tal modo que tu aplicación no tiene que preocuparse de pantallas de Login, autenticación, o almacenamiento de usuarios… tú, como desarrollador, sólo tienes que preocuparte de desarrollar la funcionalidad de tu aplicación, e integrarte con tu Proveedor de Identidad (en este caso, con Keycloak). Además, un usuario, una vez logado en Keycloak, podrá acceder al resto de aplicaciones sin tener que volver a iniciar sesión. Para ello, Keycloak soporta tanto SAML 2.0 como OpenID Connect (OIDC), los dos principales protocolos de autenticación, y está certificado como proveedor OpenID.

Es posible utilizar identidades externas (Identity Federation), como sería el caso del Login Social (ej: Facebook, Google, Twitter, Instagram, GitHub, etc) que puede añadirse de forma sencilla desde la consola de administración (sin necesidad de cambios en tu aplicación), así como también conectar Keycloak con LDAP, Active Directory, o una base de datos relacional donde se almacenen los usuarios (en este último caso, tendríamos que implementar nosotros nuestro propio proveedor, siendo LDAP y Active Directory soluciones out-of-the-box).

Keycloak es Multi Tenant, es decir, permite crear servidores de identidad virtuales (que en Keycloak se denominan Realms, en otros IdP se denominan Tenants o Directorios) dentro de un mismo Keycloak (cada uno con su propia configuración y sus propios usuarios, grupos, aplicaciones, etc.). De hecho, Keycloak viene de serie con un Realm llamado master, que se debe utilizar sólo para la administración de Keycloak, por lo que lo primero que deberemos hacer es crear un Realm para nuestros usuarios y aplicaciones (ej: myrealm), pudiendo llegar a tener múltiples Realms conviviendo de forma independiente. Además, con el usuario Admin podemos administrar todos los realms (no es necesario tener y logarse con un usuario Admin diferente para cada realm, como ocurre en otros IdP y que es algo bastante tedioso en entornos en los que se trabaja con múltiples realms/tenants). Es muy sencillo cambiar de un Realm a otro para administrarlo, o crear uno nuevo.

Hay muchas cosas que podemos configurar, a nivel de Realm/Tenant, de Aplicación/Cliente, etc. Por ejemplo, podemos personalizar el branding o Look & Feel de las páginas de Login (temas), podemos configurar nuestro Tenant para que permita el auto-registro de usuarios, podemos añadir Atributos personalizados a nuestros usuarios (directamente o heredados de su pertenencia a grupos), podemos incluir en los access_tokens los Claims que necesitemos (incluso se pueden obtener en base a los Atributos, Roles, etc.), que el usuario pueda o no recuperar su contraseña de forma autónoma, requerir a los usuarios cambiar su contraseña de forma regular, requerir a los usuarios aceptar nuestros Terms & Conditions, que el usuario pueda mantener la sesión en el navegador entre reinicios, ajustar diferentes configuraciones de los access_tokens (ej: lifespan), etc. Keycloak es un mundo, donde tenemos mucho terreno para investigar, configurar y personalizar.

Además de la Consola de Administración, Keycloak también proporciona una Admin CLI y una Admin REST API para poder realizar tareas de forma programática (ej: desde una sesión Bash), proporciona la Account Console para que los usuarios puedan de forma autónoma configurar su perfil y otras tareas, etc.

Para ampliar información sobre Keycloak, se puede consultar:

Instalar Keycloak (en modo Dev)

Instalar Keycloak para un entorno de pruebas (en modo Dev) es muy sencillo, vamos a ver tres formas de hacerlo (Docker, Minikube, y en una Máquina Virtual), todas ellas arrancando Keycloak en modo desarrollo (sin HTTPS utilizando HTTP sobre el puerto tcp-8080, con una base de datos embebida H2, y especificando las credenciales de Admin mediante variables de entorno) y descritas en la documentación oficial de Keycloak, pero siempre pensando que se trata de un entorno de laboratorio y pruebas (suficiente para trastear y probar el producto), para una instalacción de Producción habría que tener más detalles en cuenta.

Instalar Keycloak en Docker

Es la forma más rápida y sencilla de arrancar un servidor de Keycloack (al menos, si tienes Docker). Básicamente consiste en arrancar un contenedor con una imagen Docker, indicando el puerto que deseamos utilizar (por defecto la recomendación es el tcp-8080), y las credenciales del usuario admin pasadas como variables de entorno: Keycloack – Docker

docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:20.0.2 start-dev

Con esto ya podremos acceder a Keycloak en localhost:8080.

Instalar Keycloak en Kubernetes (MiniKube)

También podemos arrancar Keycloak de forma sencilla en Kubernetes si tenemos MiniKube, como describimos a continuación, siguiendo la documentación (Keycloak – Kubernetes) y con los ejemplos del repo GitHub – Keycloak Quickstarts

Lo más sencillo, es instalar Keycloak en MiniKube sin el Ingress, y acceder mediante el tunnel de MiniKube, que para hacer pruebas es más que suficiente. Para ello podemos utilizar los siguientes comandos, para crear el deployment y service de Keycloak en MiniKube, comprobar que se han creado con éxito (puede tardar un par de minutos), y seguidamente abrir el tunnel de MiniKube.

kubectl create -f https://raw.githubusercontent.com/keycloak/keycloak-quickstarts/latest/kubernetes-examples/keycloak.yaml
kubectl get all -l app=keycloak
minikube tunnel

Con esto ya podremos acceder a Keycloak en localhost:8080.

Instalar Keycloak en Ubuntu 22 con OpenJDK

Podemos instalar Keycloak en una máquina virtual de forma sencilla con OpenJDK, como describimos a continuación, siguiendo la documentación de Keycloak – OpenJDK y algún otro detalle adicional. Sigue siendo una instalación para un entorno de laboratorio y pruebas, aunque en este caso, un poco más avanzada que en los casos anteriores con Docker y Kubernetes, ya que vamos a utilizar una base de datos PostreSQL en lugar de H2, un hostname personalizado, y HTTPS (aunque utilizando un certificado auto-firmado y el puerto tcp-8443).

En nuestro caso de ejemplo, vamos a instalar Keycloak 20.0.2.

Lo primero que vamos a hacer es actualizar el sistema operativo, y reiniciar si fuera necesario.

sudo apt-get update && sudo apt-get upgrade -y
sudo reboot

Keycloak 20.0.2 requiere Java 11 o susperior. Instalaremos la máquina virtual de Java, en particular OpenJDK 11, que es la versión que incluye Ubuntu 22, por lo que instalaremos JRE y JDK de OpenJDK

sudo apt install default-jre -y
java -version
sudo apt install default-jdk -y
javac -version

Utilizaremos PostgreSQL como base de datos. Aunque lo ideal es instalar PostgreSQL en un servidor dedicado, en nuestro caso al tratarse de un entorno de laboratorio y pruebas, instalaremos PostgreSQL v13 en la misma máquina de Keycloak. Para ello:

  • Añadiremos la clave GPG
  • Añadiremos el repo
  • Actualizaremos el sistema
  • Instalaremos PostgreSQL v13 y comprobaremos que el servicio está arrancado
curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/apt.postgresql.org.gpg >/dev/null
echo "deb [arch=amd64] http://apt.postgresql.org/pub/repos/apt/ jammy-pgdg main" | sudo tee /etc/apt/sources.list.d/postgresql.list
sudo apt update
sudo apt install postgresql-13 -y
systemctl status postgresql

Ahora que ya tenemos instalado Postgres, necesitamos crear un usuario y base de datos para Keycloak. Para ello lo primero es abrir una shell de Posgres con psql.

sudo -u postgres psql

Dentro de la shell de Postgres (psql) crearemos un usuario de base de datos, una base de datos, y le daremos permisos al usuario sobre la base de datos.

CREATE USER keycloak WITH PASSWORD 'P@ssword';
CREATE DATABASE keycloak OWNER keycloak;
GRANT ALL PRIVILEGES ON DATABASE keycloak TO keycloak;
\l
\du
\q

Crearemos un usuario y grupo en Linux para el demonio de Keycloak.

sudo useradd -c "User to run Keycloak" -b /opt/keycloak -s /bin/bash keycloak --system
cat /etc/passwd | grep keycloak
cat /etc/group | grep keycloak

Descargaremos Keycloak y lo descomprimiremos.

wget https://github.com/keycloak/keycloak/releases/download/20.0.2/keycloak-20.0.2.tar.gz
tar -xvzf keycloak-20.0.2.tar.gz
sudo mv keycloak-20.0.2 /opt/keycloak
sudo chown -R keycloak:keycloak /opt/keycloak

Crearemos un certificado auto-firmado con openssl.

sudo openssl req -x509 -nodes -sha256 -days 358000 -newkey rsa:4096 -keyout /opt/keycloak/keycloak.willie.lan.key -out /opt/keycloak/keycloak.willie.lan.pem -subj "/CN=keycloak.willie.lan" -addext "subjectAltName=DNS:keycloak.willie.lan,IP:192.168.10.216"

sudo chown keycloak /opt/keycloak/keycloak.willie.lan.*

Editaremos el fichero de configuración de Keycloak.

sudo vi /opt/keycloak/conf/keycloak.conf

Configuraremos las siguientes líneas, para indicar los datos de conexión a PostgreSQL (durante el primer arranque, creará los objetos de base de datos necesarios en Posgres, como las tablas), los ficheros del certificado que deseamos utilizar, y el hostname con el que deseamos acceder (en nuestro caso es keycloak.willie.lan).

db=postgres
db-username=keycloak
db-password=P@ssword
db-url=jdbc:postgresql://localhost/keycloak

https-certificate-file=/opt/keycloak/keycloak.willie.lan.pem
https-certificate-key-file=/opt/keycloak/keycloak.willie.lan.key

hostname=keycloak.willie.lan

Ahora que tenemos Keycloak instalado y configurado, tenemos que crear el fichero de servicio de systemd. Lo crearemos y editaremos con el siguiente comando.

sudo vi /etc/systemd/system/keycloak.service

Añadiremos el siguiente contenido. Las variables de entorno KEYCLOAK_ADMIN y KEYCLOAK_ADMIN_PASSWORD especificarán las credenciales del usuario administrador de Keycloak.

[Unit]
Description=Keycloak service
After=syslog.target network.target

[Service]
Environment=KEYCLOAK_ADMIN=admin
Environment=KEYCLOAK_ADMIN_PASSWORD=admin
ExecStart=/opt/keycloak/bin/kc.sh start-dev
User=keycloak
Group=keycloak
LimitNOFILE=65536
LimitNPROC=8192

[Install]
WantedBy=multi-user.target

Guardaremos los cambios, y seguidamente actualizaremos systemd para que tenga en cuenta el nuevo servicio de Keycloak, lo arrancaremos, configuraremos con inicio automático, y comprobaremos que está arrancado correctamente.

sudo systemctl daemon-reload
sudo systemctl start keycloak
sudo systemctl enable keycloak
sudo systemctl status keycloak

Hecho todo esto, podremos acceder a nuestra instalación de Keycloak, utilizando el nombre DNS que hemos configurado. Como hemos utilizado un certificado auto-firmado que no procede de una Root CA de confianza, es detectado como un sitio no seguro. Para solucionarlo, click en Show certificate.

En la ventana de diálogo del certificado, lo exportaremos a un fichero.

Abriremos el fichero del certificado que acabamos de exportar, y lo instalaremos (click Install Certificate).

Seleccionaremos que deseamos instalar el certificado a nivel de máquina, y click Next.

Eligiremos instalar el certificado en el almacén de las CA Root de confianza, y finalizaremos la importación del certificado.

Realizado esto, ya podremos acceder a Keycloak por HTTPS sin que se muestre ningún error de seguridad (quizás tengas que borrar cookies y reiniciar el navegador, en función de lo que hubieras estado trasteando antes).

Para continuar, vamos a crear un Realm (Tenant o Directorio), donde crearemos un Usuario y registraremos un Cliente (Aplicación), para luego hacer una prueba con la aplicación de ejemplo que ofrece Keycloak en su Web, que es una SPA que implementa el flujo de Authotization Code con PKCE, para así tener una visión más completa. Veremos algunos detalles más, que si bien no son necesarios para este ejercicio, está bien comentarlos para conocer un poco más Keycloak.

Crear un Realm (Tenant o Directorio) en Keycloak

Keycloak viene de serie con un Realm llamado master, que se debe utilizar sólo para la administración de Keycloak (principalmente para crear otros Realms), por lo que lo primero que deberemos hacer es crear un Realm (un Tenant, Directorio o como lo queramos llamar) para nuestros usuarios y aplicaciones. En muchos casos trabajaremos con varios Realms (ej: un Realm para los Empleados y otros para los Clientes), más aún, si desarrollamos una aplicación que deseamos que pueda dar servicio a múltiples Clientes.

Para crear un nuevo Realm, accederemos a la Admin Console de Keycloak con el usuario admin (veremos que estamos conectados a master), y crearemos un nuevo Realm.

La Admin Console va a ser nuestro compañero de viaje, ya que aunque tenemos una Admin CLI y una Admin REST API, al final muchas tareas de configuración las vamos a realizar directamente desde la Admin Console. Para acceder a la Admin Console, en la instalación de este ejemplo, la URL es: https://keycloak.willie.lan:8443/admin/master/console

Especificaremos el nombre que le queremos dar a nuestro Realm, y click en Create.

Con esto, ya habremos creado un nuevo Realm o Teant, que inicialmente estará vacío.

Una vez creado nuestro Realm, hay un montón de opciones que podemos configurar. Podemos echarlas un ojo, para hacernos una idea.

Una de las configuraciones que nos puede interesar realizar es la configuración SMTP para el envío de mail. A continuación se muestra a modo de ejemplo, la configuración SMTP utilizando gmail con una cuenta personal y una App Password. Si no sabes como configurar una App Password en tu cuenta de gmail, lo explico en el Post Prometheus – Gestión de alertas con AlertManager.

También puede ser interesante habilitar la opción «Declarative User Profile«, actualmente aún en Technology Preview (por eso no está disponible por defecto), mediante la cual es posible definir o estandarizar qué Atributos deseamos poder utilizar en nuestros usuarios, si queremos que los usuarios los puedan ver ó modificar, asignarles validadores, etc. Se puede encontrar más detalle en la documentación: Keycloak – Server Administration – Managing users – Defining a user profile

Una vez habilitado, se habilitará una nueva pestaña «User profile», donde podemos añadir los Atributos, en el orden que deseemos, organizarlos en grupos y configurarlos (ej: validadores), etc., de forma muy sencilla.

Crear un Usuario en un Realm de Keycloak

Lo siguiente que vamos a hacer es crear un Usuario en nuestro Realm (el que acabamos de crear, recordemos que el Realm de master es sólo para tareas administrativas como la creación de nuevos Realms). Para ello, click en Users y luego click en Create new user.

Introduciremos los datos del usuario que queremos crear (username, email, nombre y apellidos), y click en Create.

La opción de «Require user actions«, aunque no la vamos a utilizar para este ejemplo, es interesante comentarla, ya que permite «obligar» al usuario a realizar alguna tarea, como por ejemplo, verificar su email la próxima vez que inicie sesión, requerir que tenga que aceptar los términos y condiciones de nuestro servicio (Terms & Conditions) que podemos personalizar, requerir que configure un sistema de OTP (ej: FreeOTP o Google Authenticator) la próxima vez que inicie sesión, requerir actualizar el perfil la próxima vez que inicie sesión, etc.

Una vez creado el Usuario, podríamos añadirle a Grupos, asignarle Roles, o añadirle Atributos adicionales (los datos básicos del usuario puede que nos resulten insuficientes, pero podemos complementarlos mediante Atributos). En caso de necesitarlo, la recomendación es apoyarse en Grupos siempre que sea posible, y minimizar la asignación de Roles y Atributos usuario a usuario, con el fin de facilitar la gestión.

Para este ejemplo, no necesitamos nada de esto, pero si que necesitaremos crear una contraseña para el usuario, para ello en la pestaña Credentials, click en Set password.

Especificaremos la contraseña, indicando que no es temporal, y la guardaremos (click Save).

Hecho esto la contraseña habrá quedado ya establecida para el usuario.

Si queremos probar el nuevo usuario que acabamos de crear, podemos utilizar la Account Console, iniciando sesión con el nuevo usuario (podemos utilizar una ventana de incógnito). A continuación se muestra es aspecto de la página de Login por defecto, de nuestro nuevo Realm. En el caso de nuestro ejemplo, la URL de la Account Console es la siguiente: https://keycloak.willie.lan:8443/realms/willie/account

La URL de la Account Console también la podemos obtener de Keycloak, en las propiedades del Cliente account-console, como se muestra en la siguiente imagen.

Keycloak proporciona la Account Console, una consola Web donde los usuarios de un Realm o Tenant, pueden ver y modificar su perfil, ver las sesiones que tienen iniciadas (y cerrar las que deseen), cambiar su contraseña, etc. En la siguiente pantalla, se muestra el acceso a la Account Console del Realm que acabamos de crear (Willie), iniciando sesión con el Usuario que acabamos de crear.

Otra forma de poder probar un Usuario, es mediante la Impersonación. Es muy útil, ya que podemos impersonarnos (si tenemos permisos suficientes,) sin tener las credenciales del Usuario, para analizar una incidencia, o por cualquier otro motivo. Para impersonarnos, podemos seleccionar el Usuario correspondiente en la Admin Console, y en el menú Action, seleccionar la opción Impersonate, como se muestra en la siguiente imagen.

En la configuración por defecto, los Usuarios no se puede auto-registrar ellos mismos, ni existe una opción para recuperar la contraseña, ni es posible mantener la sesión entre reinicios del navegador (es decir, que la cookie de Login, se mantenga a nivel de la sesión del navegador o que pueda persistir entre reinicios del mismo). Dependiendo de nuestro caso de uso, nos puede interesar o no activar cada una de estas opciones, algo que podemos hacer en la configuración de nuestro Realm, desde la pestaña de Login.

Si activásemos las tres opciones anteriores, la página de Login pasaría a ser así. Además, es posible personalizar la página de auto-registro de usuarios (añadiendo y quitando campos, además de aplicando temas).

Si habilitamos el auto-registro de Usuarios, podemos configurar el Google reCaptcha en el formulario de Registro de Usuarios, con lo que conseguiremos un aspecto similar al siguiente (al margen de que además lo podremos personalizar, mediante el uso de temas, etc). Para conseguirlo, lo primero es registrar nuestro Sitio en Google reCaptcha Admin Console para obtener el reCaptcha Site Key y el reCaptcha Secret.

Además deberemos añadir la URL de Google en los encabezados X-Frame-Options y Content-Security-Policy, algo que deberemos configurar a nivel de Realm, como se muestra en la siguiente pantalla.

También tendremos que editar el flujo de registro de nuestro Realm, haciendo click sobre dicho flujo, como se representa en la siguiente imagen.

Seguidamente al editar el flujo, especificaremos como requerido el reCaptcha y especificaremos su Alias, reCaptcha Site Key, y reCaptcha Secret, de forma similar a como se muestra a continuación. Con esto ya estaría.

Otro detalle interesante, es que podemos especificar en nuestro Realm o Tenant, qué acciones son requeridas antes del primer inicio de sesión (Login) de un Usuario, como por ejemplo, aceptar nuestros Terms & Conditions o configurar un OTP como doble factor de autenticación. Para habilitar estas acciones, en la pestaña «Required actions» de la sección Authentication, podemos habilitar la opción «Set as default action» para la acción que deseemos. A continuación se muestra esta página, a modo ilustrativo.

Crear un Grupo en un Realm de Keycloak

Aunque para el ejercicio que queremos realizar ahora, no es necesario, vamos a aprovechar para comentar brevemente los Grupos, ya que es bastante interesante. Básicamente un Grupo es una colección de Usuarios a los que podemos aplicar Roles y Atributos. Podemos ver o modificar los Grupos existentes, así como crear nuevos, desde el menú Groups. Mediante la creación de Grupos podemos:

  • Añadir Atributos al Grupo (pestaña «Attributes«), que serán heredados por todos sus miembros.
  • Asignar Roles al Grupo (pestaña «Role mapping«), que serán heredados por todos sus miembros.

Podemos crear Sub-Grupos formando jerarquías, a través de la pestaña «Child groups«, teniendo en cuenta que un Sub-Grupo sólo puede tener un único Grupo Padre (no se pueden anidar los Grupos, sólo crear Sub-Grupos para formar jerarquías). De este modo, un Usuario que sea miembro de un Grupo hijo, heredará los Atributos y asignación de Roles de dicho Grupo y de todos sus Grupos padres (en caso de haber varios niveles en la jerarquí). En la siguiente pantalla capturada, se muestra un Grupo padre con otro hijo (puede haber más niveles de profundidad), y además podemos observar como se puede visualizar gráficamente la jerarquía de grupos (en la izquierda).

Incluso podemos configurar para cada Realm o Tenant, qué Grupos deseamos asignar por defecto a los usuarios, por el mero hecho de crearlos.

Crear un Global Role en un Realm de Keycloak

Tampoco es necesario para el ejercicio que queremos realizar ahora, pero es interesante comentarlo, ya que los Roles son una pieza muy importante, debido a que los Roles se incluyen en los access_tokens como Claims, permitiendo que los recursos a los que deseamos acceder (es decir, las APIs) puedan decidir si dar o no acceso a las peticiones que reciben, en base a los Claims que incluyen los access_tokens.

Lo primero que hay que aclarar, es que existen Roles a nivel de Realm (Global Roles ó Realm Roles) y a nivel de Cliente o Aplicación (Client Roles).

Podemos ver, modificar, o crear Global Roles (es decir, Roles a nivel de nuestro Realm, Tenant, Directorio, o como lo queramos llamar), desde el menú «Realm roles«.

Una vez que hemos creado un Global Role, podemos asignarle usuarios, aunque en muchos casos no lo haremos así (en su lugar lo haremos mediante Grupos), y también Atributos. Podemos añadir uno o varios Roles a nuestro Role, es decir, anidarlos, lo que se denominan «Composite Roles«, lo que permitirá que al asignar el Role a un Usuario, heredará el resto de Roles que tiene asociados.

Para hacerlo basta con utilizar la opción «Add associated roles«, y con esto ya se mostrará la pestaña «Associated roles» que sólo se muestra para los Composite Roles. Puede ser muy útil la anidación de Roles, pero debemos utilizarlo con precaución (es recursivo y puede haber varios niveles de profundidad, lo que puede complicar mucho la administración e incluso impactar en rendimiento, así que, con cuidado).

Otro detalles interesante, es que se pueden utilizar variables de sustitución en la descripción de los Roles. A continuación se muetra a modo de ejemplo, el Global Role de admin en el Realm master.

Incluso podemos configurar para cada Realm o Tenant, qué Roles deseamos asignar por defecto a los usuarios, por el mero hecho de crearlos. De hecho, por defecto podemos ver que se asignan algunos Roles.

Registrar una Aplicación en un Realm de Keycloak

Ahora vamos a registrar una Aplicación en Keycloak. En Keycloak, a las Aplicaciones las llaman Clientes, que es el término habitual de OAuth para referirse a una Aplicación, y además en cierto modo son Clientes de Keycloak (no humanos, claro), por lo que iremos a Clients, y seguidamente click en Create client.

Especificaremos el ClientID y el Client Type (OpenID Connect), y click Next para continuar.

La siguiente pantalla la dejaremos por defecto, asegurando que la opción Standard flow queda activada, y click en Save. Lo que pongamos aquí, luego se puede cambiar editando el registro de la Aplicación, por lo que no es algo especialmente trascendental, pero si es importante porque en esas opciones se representan los Flujos OAuth 2.0 que deseamos permitir utilizar en nuestra aplicación, por ejemplo:

  • Standard flow representa el flujo de Authorization Code.
  • Direct access grants representa el flujo de Resource Owner Password Credentials.
  • Implicit flow representa el flujo Implict (este es el más evidente).
  • Service accounts roles representa el flujo de Client Credentials. Mostrará la pestaña Service accounts roles.
  • OAuth 2.0 Device Authorization Grant representa el flujo de Device Code.
  • OIDC CIBA Grant representa el flujo CIBA (Client Initiated Backchannel Authentication).

Hay más opciones interesantes en esta pantalla, como por ejemplo:

  • Client authentication permite configurar un Client Secret asociado al registro de Aplicación, por lo que lo necesitaremos para implementar flujos de Clientes Confidenciales que usen el Client Secret. Habilitarlo mostrará las pestañas Keys y Credentials.
  • Authorization. Mostrará la pestaña Authorization al editar el registro de Aplicación, donde poder ver la configuración de políticas y permisos.

Para probar esta configuración, vamos a utilizar una Aplicación Web de ejemplo (una SPA) de Keycloak, que permite autenticarse contra el Realm y Cliente (registro de Aplicación) que deseemos, especificando sus datos en un formulario, utilizando el flujo de Authorization Code con PKCE: https://www.keycloak.org/app/

Para ello, una vez registrada la Aplicación o Cliente en Keycloak, la editaremos, para especificar los siguientes valores.

  • Valid redirect URIs: https://www.keycloak.org/app/*
  • Web origins: https://www.keycloak.org

Role scope mappings

Esto tampoco es necesario para el ejercicio que queremos realizar ahora, pero es interesante comentarlo, para comprender mejor el funcionamiento de los Roles y access_token, que es fundamental en Keycloak o en cualquier otro IdP.

Por defecto, cuando una Aplicación (Cliente) obtiene un access_token de un Usuario, se incluyen como Claims todos los Roles que tiene asignado el Usuario (relacionados con cualquier Aplicación o Cliente). Esto puede implicar un riesgo, debido a que un atacante que obtenga dicho access_token (obtenido para acceder a una Aplicación o Cliente específico) podría utilizarlo para acceder a cualquier otra Aplicación o Cliente a la que tuviera acceso el usuario, conforme a los Roles que tiene asignado, lo que podría ser una vulnerabilidad innecesaria.

Para evitar esto, se puede utilizar la opción Role scope mapping (viene deshabilitada por defecto), que permite limitar los Roles incluidos en el access_token del Usuario, a sólo los especificados explícitamente en la Aplicación o Cliente, y en consecuencia, limitando los permisos que concedería, y por lo tanto, conteniendo el riesgo.

Para habilitar la opción Role scope mapping, accederemos a la pestaña «Client scopes» de las propiedades de la Aplicación o Cliente sobre la que lo deseamos habilitar, y haremos click sobre la primera línea, como se muestra en la siguiente pantalla capturada.

Esto nos mostrará la siguiente pantalla, donde en la pestaña Scope deberemos deshabilitar la opción «Full scope allowed», tras lo cual, podremos añadir los Roles que deseamos mediante el botón «Assign role» que aparecerá, y a través del cual, se mostrará un diálogo para que podamos seleccionar por qué Roles queremos filtrar, sean Global Roles (Realm Roles) ó Client Roles.

Por ejemplo, si seleccionamos role-prueba, lo que conseguiremos es que en el access_token en lugar de incluirse todos los Roles que tiene asignado el Usuario, sólo incluira el Role de role-prueba, suponiendo que el Usuario tuviera asignado dicho Role, ya que en caso contrario el access_token no incluiría ningún Role. Es decir, con esta opción podemos filtrar qué Roles incluir en el access_token, pero filtrando sobre los Roles que tiene asignado el Usuario, en base a los que especifiquemos en la configuración de la Aplicación o Cliente. Para verlo de forma más gráfica:

Este es el Payload de un access_token obtenido por una Aplicación sin la opción de Role scope mapping. En este caso, el access_toke incluye todos los Roles que tiene asignado el Usuario.

Este es el Payload de un access_token obtenido por la misma Aplicación, pero esta vez, con la opción Role scope mapping habilitada, filtrando por role-prueba. Como se puede observar, sólo se incluye dicho Role en el access_token.

Del mismo modo que podemos limitar los Roles incluidos en el access_token del Usuario, también podemos añadir al access_token los valores que necesitemos, ya sean valores literales (hardcodeados), procedentes de un Atributo, etc. Esto lo podemos hacer configurando los Mappers de un Cliente o Aplicación.

Por ejemplo, para añadir un Atributo, añadiríamos un Mapper mediante la opción «By configuration», y en la siguiente pantalla seleccionaríamos la opción «User Attribute».

Luego tan sólo tendremos que especificar qué Atributo deseamos añadir al Token, cómo llamar al Claim que lo almacenará, y poco más.

Con esto sería suficiente, y en caso de que el Usuario para el que se solicita un access_token tuviera ese Atributo informado, se incluiría en el Token, tal y como se muestra en la siguiente pantalla.

Probando Keycloak con una aplicación de ejemplo (SPA)

Ahora vamos a probar la Aplicación Cliente que acabamos de registrar en Keycloak, que utilizará el flujo de Authorization Code con PKCE. Para ello vamos a utilizar la aplicación SPA de ejemplo que incluye Keycloack en su Web, accedemos a ella, y especificamos los datos de nuestro Tenant ó Realm y de nuestra Aplicación ó Cliente, y click en Save: https://www.keycloak.org/app/

Veremos que los datos introducidos en la pantalla anterior, aparecen como parámetros en la URL. Seguidamente, click en Sign in.

Nos redirigirá a la pantalla de Login de nuestro Tenant o Realm, para que podamos logarnos, ya que nunca antes hemos iniciado sesión y tenemos que identificarnos. Introducimos los datos de nuestro usuario (el que hemos creado más arriba), y click en Sign in.

Y volvemos de nuevo a la aplicación SPA, donde podemos observar que ya hemos iniciado sesión con éxito, y que muestra nuestros datos de nombre y apellidos que le ha proporcionado el IdP.

Si hacemos todo esto con las herramientas del desarrollador del navegador, podremos ver el detalle de todas las peticiones, incluido el POST al Endpoint /token con su Payload (podemos ver que el grant_type es authorization_code) y su respuesta (que incluye el access_token).

Despedida y Cierre

Hasta aquí llega este Post, en el que he intentado hacer una introducción a Keycloak como solución IdP, mostrando con ejemplos su instalación y configuración más básica, probándolo con la Aplicación SPA de ejemplo que proporciona la propia Web de Keycloak implementando el flujo de Authentication Code con PKCE, y como además era necesario, con una introducción previa a OAuth 2.0, los access_token y JWT, ya que sin ese contexto, cuesta mucho comprender un IdP como Keycloak.

Keycloak es una solución IdP bastante interesante, Open Source, gratuita y personalizable, con RedHat detrás como compañía. Hay muchas más alternativas, como WSO2 Identity Server, o si preferimos una solución cloud (IDaaS: Identity as a Service), podemos recurrir a auth0, Okta, Azure AD, Azure AD B2C, etc.

Poco más por hoy. Como siempre, confío que la lectura resulte de interés.