Cómo Configurar Login con Office 365 (Microsoft Entra ID) en Authentik

Como ya vimos en la anterior entrada es bastante sencillo instalar un sistema SSO como authentik en nuestra infraestructura. De todas formas, crear un login centralizado no es más que una manera de tener que recordar menos contraseñas, pero el objetivo final es no tener que recordar más que unas pocas para poder acceder a todos los servicios, ¿qué hacemos si ya tenemos cuenta en alguno de los servicios más populares como office 365 o google? Lo suyo sería poder hacer login con estas credenciales en cualquier servicio, sea online o sea auto hospedado. Por suerte, authentik puede hacer uso de estos servicios externos de autenticación de manera «relativamente» sencilla. En este post os voy a explicar paso a paso (que sobre todo la parte a realizar con office es un poco liosa) cómo integrar el login de office 365 con authentik y así poder usarlo para acceder a nuestras aplicaciones (eso ya lo explicaremos en próximos posts).

Para poder realizar este tutorial debes tener:

  • Servidor authentik instalado y configurado (con acceso como administrador)
  • Cuenta de administrador office365 de tu organización
  • Un poco de paciencia (esto nunca viene mal)

Paso 1: registrar una aplicación de microsoft entra

Entramos como administrador en portal.office.com

Pedimos que nos muestre todas las opciones (estos de MS suelen ocultarnos cosas)

Seleccionamos la opción Identidad (nos llevará a entra)

Seleccionamos la opción Registro de aplicaciones

Y seleccionamos Nuevo registro

Y rellenamos los datos básicos (yo voy a llamar authentik a la nueva aplicación)

Hemos elegido que solo se pueda entrar con cuentas de nuestra organización, que será lo normal, aunque si queremos que cualquiera que tenga una cuenta de office365 pueda hacer logn tendremos que escoger una de las otras opciones. Atento a la ui de redirección, en principio tendrá el formato https://<direccion-de-tu-authentik>/source/oauth/callback/<nombre-de-fuente> Si no lo configuras ahora lo podrás hacer después cuando termines la configuración en authentik.

Anotamos los datos que vamos a necesitar de la aplicación y creamos un nuevo secreto:

Es muy importante que copiemos el valor del secreto que lo vamos a necesitar luego

El valor del secreto no se va a poder copiar en otro momento, mejor que lo guardes ahora.

Con esto hemos terminado (en principio) con la configuración necesaria en el portal de office. Lo que hemos hecho, básicamente, es configurar los endpoints de oauth2 que podemos consultar en la opción.

Paso 2: crear un nuevo login social

Las siguientes acciones las haremos como administrador en nuestro servidor authentik

El primer paso es crear un inicio de sesión federado

del tipo azure AD

Ahora deberemos introducir todos los datos que hemos guardado al crear la aplicación en entra:

Los endpoint serán como estos (revisar los que vimos en la pantalla del portal de office):

Cambiaremos la URL del perfil a https://graph.microsoft.com/oidc/userinfo

Y damos a terminar por ahora. Esto nos configura el método para entrar usando las credenciales de Microsoft, pero todavía nos falta un par de cosas para que sea útil del todo.

Paso 3: mapeo de atributos

Ahora necesitamos hacer la equivalencia entre los atributos que vienen de office con los que nosotros vamos a utilizar en nuestro directorio, para eso crearemos una política de expresión:

La llamamos (por ejemplo) azure-ad-mapping y ponemos este contenido:

# save existing prompt data
current_prompt_data = context.get('prompt_data', {})
# make sure we are used in an oauth flow
if 'oauth_userinfo' not in context:
  ak_logger.warning(f"Missing expected oauth_userinfo in context. Context{context}")
  return False
oauth_data = context['oauth_userinfo']
# map fields directly to user left hand are the field names provided by
# the microsoft graph api on the right the user field names as used by authentik
required_fields_map = {
  'name': 'username',
  'email': 'email',
  'given_name': 'name'
}
missing_fields = set(required_fields_map.keys()) - set(oauth_data.keys())
if missing_fields:
  ak_logger.warning(f"Missing expected fields. Missing fields {missing_fields}.")
  return False
for oauth_field, user_field in required_fields_map.items():
  current_prompt_data[user_field] = oauth_data[oauth_field]
# Define fields that should be mapped as extra user attributes
attributes_map = {
  'name': 'upn',
  'sub': 'sn',
  'name': 'name'
}
missing_attributes = set(attributes_map.keys()) - set(oauth_data.keys())
if missing_attributes:
  ak_logger.warning(f"Missing attributes: {missing_attributes}.")
  return False
# again make sure not to overwrite existing data
current_attributes = current_prompt_data.get('attributes', {})
for oauth_field, user_field in attributes_map.items():
  current_attributes[user_field] = oauth_data[oauth_field]
current_prompt_data['attributes'] = current_attributes
context['prompt_data'] = current_prompt_data
return True

Paso 4: configurar el flujo de alistamiento

Queremos que cualquiera que tenga cuenta en el dominio office pueda entrar a nuestras aplicaciones, por lo que necesitamos registrar los usuarios nuevos cuando entran por primera vez, para ello vamos a crear uun flujo nuevo.

Le ponemos un nombre

Y una vez creado, editarlo, ir a la sección d Vinculaciones de Políticas / Grupos / Usuarios y ahí a Bind existing policy

Y ahí ligar defaultsource-enrollment-if-sso

Ir ahora a vinculos de etapa -> Bind Existing stage

Añadir default-source-enrollment-write (orden 0) y default-source-enrollment-login (orden 10)

Desplegar luego la etapa con orden cero y añadir una política existente azur-ad-mapping que creamos antes:

Por último editarmos la fuente que creamos al principio (Directory -> Federation ..) y le añadimos en la configuración de flujo el flujo de inscrpción a azure-ad-enrollment (o el nombre que le hayamos puesto)

Paso 5: hacer que se vea en el login

Pues ya solo nos faltaría hacer que aparezca una opción para hacer login con este proveedor, esto se hace editando el flujo default-authentication-flow

Y editas la etapa

Y añades desde las fuentes disponibles a Fuentes seleccionadas

Ahora cuando entres en tu authentik ya te aparecerá la opción nueva para acceder. La primera vez que accedas debes aprobar unos permisos especiales a Microsoft:

Ha sido un proceso un poco largo, pero ahora ya tenéis centralizada la información de seguridad y cualquier aplicación que pueda conectarse a authentik podrá usar los usuarios de nuestro dominio… Ahora queda que lo probéis vosotros.

Instala tu propio servidor SSO (Single Sing-On)

Es posible que, como nosotros, ester harto de tantas contraseñas y tantas veces que tienes que autenticarte para acceder a tus aplicaciones. Nosotros ya inventamos nomorepass para no tener que andar recordando la contraseñas, pero eso no quita a que cada vez que instalamos un nuevo sistema en nuestro laboratorio personal nos toque volver a lidiar con las credenciales de acceso. Hay veces que intentamos centralizarlo en algún proveedor externo (como google o microsoft), pero esto no es del todo seguro y corremos el riesgo de tener que andar configurando nuestras aplicaciones con credenciales que, como las de Microsoft Hello caducan cada cierto tiempo.

Por eso os propongo algo mejor, ¿porqué no hospedar nosotros nuestro propio sistema de autenticación? En este post vamos a ver como instalar y configurar uno de los más populares y potentes sistemas SSO: Authentik. La instalación es bastante sencilla y está bien explicada en su documentación. Os resumo cómo quedaría la cosa. Necesitamos:

  • Servidor donde instalarlo (2VCPU / 2Gb memoria mínimo)
  • Docker y docker compose (v2 mejor)
  • wget y openssl instalado (necesarios solo para la instalación)

Una vez que tenemos el servidor con docker y docker compose instalado nos descargamos la plantilla:

wget https://goauthentik.io/docker-compose.yml

Y generamos los elementos de seguridad necesarios y los guardamos en el archivo .env

echo "PG_PASS=$(openssl rand -base64 36 | tr -d '\n')" >> .env
echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 60 | tr -d '\n')" >> .env

Con esto ya podemos arrancar el servidor. Hay cosas que sería recomendable configurar, como la salida de email, bien con un servidor propio, o podéis utilizar cualquier servicio que os ofrezca smtp. También se puede cambiar el puerto por el que se accede, que generalmente está en el 9000/9443 para cambiarlos a los estándar 80/443. Como ejemplo os dejo mi docker-compose y mi .env (cambiando las cosas privadas, claro):

services:
  postgresql:
    image: docker.io/library/postgres:16-alpine
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
      start_period: 20s
      interval: 30s
      retries: 5
      timeout: 5s
    volumes:
      - database:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: ${PG_PASS:?database password required}
      POSTGRES_USER: ${PG_USER:-authentik}
      POSTGRES_DB: ${PG_DB:-authentik}
    env_file:
      - .env
  redis:
    image: docker.io/library/redis:alpine
    command: --save 60 1 --loglevel warning
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
      start_period: 20s
      interval: 30s
      retries: 5
      timeout: 3s
    volumes:
      - redis:/data
  server:
    image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.4}
    restart: unless-stopped
    command: server
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
      AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
      AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
    volumes:
      - ./media:/media
      - ./custom-templates:/templates
    env_file:
      - .env
    ports:
      - "${COMPOSE_PORT_HTTP:-9000}:9000"
      - "${COMPOSE_PORT_HTTPS:-9443}:9443"
    depends_on:
      postgresql:
        condition: service_healthy
      redis:
        condition: service_healthy
  worker:
    image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.3}
    restart: unless-stopped
    command: worker
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
      AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
      AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
    user: root
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./media:/media
      - ./certs:/certs
      - ./custom-templates:/templates
    env_file:
      - .env
    depends_on:
      postgresql:
        condition: service_healthy
      redis:
        condition: service_healthy

volumes:
  database:
    driver: local
  redis:
    driver: local

.env:

PG_PASS=<esto-lo-tienes-que-generar>
AUTHENTIK_SECRET_KEY=<esto-lo-generas-tambien>
AUTHENTIK_ERROR_REPORTING__ENABLED=true
# SMTP Host Emails are sent to
AUTHENTIK_EMAIL__HOST=mail.biblioeteca.net
AUTHENTIK_EMAIL__PORT=587
[email protected]
AUTHENTIK_EMAIL__PASSWORD=<el que sea>
AUTHENTIK_EMAIL__USE_TLS=false
AUTHENTIK_EMAIL__USE_SSL=false
AUTHENTIK_EMAIL__TIMEOUT=10
[email protected]
AUTHENTIK_TAG=2025.6.2

Si todo hay ido bien ya puedes arrancarlo:

docker compose up -d

Y ya puedes empezar a configurar tu nuevo SSO navegando a la página:

http://<IP servidor>:9000/if/flow/initial-setup/

Con esto ya tenemos un sistema que nos permite hacer login con los usuarios que queramos… ¿Qué más podemos hacer con este sistema?… Pues un montón de cosas. Quedaos atentos y en los siguientes posts os enseñaré como hacer cosas interesantes como conectar con sistemas externos (como office365 o google) y hacer uso de esas credenciales.

De puertas traseras y software libre

Es casi imposible que no hayas oido hablar del backdoor xz, no es que yo pueda darte más información sobre el tema, os dejo un video de alguien que os cuenta el caso completo como si de un episodio de serie negra se tratase:

El caso es que, alguien durante tres años ha ido infiltrándose en un repositorio de un elemento pequeño pero crítico de software libre llamado xz, de tal manera que consiguió, no solo quedarse como mantenedor de ese repositorio sino que fue introduciendo, poco a poco, una puerta trasera que permitía el acceso remoto (todavía hay que ver el payload real lo que llegaba a hacer) y conseguir que ese backdoor se distribuyese en algunas de las más importantes distribuciones.

Por suerte, o mejor dicho, por la misma estructura del software libre, esta versión no pasó de las versiones inestables de las distribuciones y se descubrió el pastel porque una persona notó que algo iba más lento de lo que debía después de la actualización. Esta persona (Andres Freund) no se paró en medir el tiempo de respuesta sino que terminó encontrando la causa subyacente y la puerta trasera que habían metido (aquí el aviso que dió a la comunidad) y, obviamente, la reacción de los mantenedores de la distribución, e incluso del antiguo mantenedor del repositorio fue inmediata y reliminó todas las trazas del código dañino.

Hay varias cosas que podemos destacar, pero yo me quedo con un par de ellas:

  1. La dependencia que tenemos de código que han escrito terceros y que pueden estar bien mantenidos o no (dependiendo del ánimo de esa persona o de sus circunstancias personales)
  2. La potencia del ecosistema open source para descubrir y arreglar este tipo de problemas. Todos los sistemas operativos actuales, desde mac os hasta windows usan componentes externos, no hay ninguno 100% original y tampoco es que se pueda saber qué es lo que usan exactamente. Si hay un backdoor en windows o en macos nos lo tendremos que comer con patatas porque nadie puede mirar lo que hay dentro.

Estoy seguro de que esta forma de actuar, por muy inteligente y paciente que sea, no deja de ser un ataque en toda regla con unas finalidades seguramente malvadas (crear una botnet inmensa, por ejemplo) y no creo que sepamos realmente quien está detrás de ello y, posiblemente, nos de para una docuserie de Netflix un día de estos.

En fin, no ha pasado nada, todo está en orden de nuevo y lo malo es que nos deja un regusto amargo y hace bajar un peldaño la confianza que teníamos en el ecosistema (pero no mucho, oye, que seguimos estando a salvo).

Ser empresario también es tener que tratar con malnacidos

Como se diría en twitter, acompañemé a conocer esta triste historia…

Luis Carballo Rua con nuestro portatil

Llevo 19 años siendo empresario y he contratado unas cuantas decenas de personas. Antes era más sencillo dar con alguien competente, pero ahora con tanto bootcam y con tanto FP con esteroides es más complicado saber si un trabajador sabe lo que dice que sabe o no. Antes tener un título significaba un mínimo, ahora tienes que bucear para encontrar si la persona en cuestión sabe lo que es el http o le han dicho que eso es magia de Angular. En fin, que se te pueden colar inútiles sin comerlo ni beberlo.

Uno de esos casos me pasó recientemente, una persona respondió a un anuncio de trabajo que pusimos en LinkedIn. La plaza que pedíamos de desarrollador front la terminamos ocupando con otra persona, pero me pareció interesante el cv que nos pasó, lleno de dibujos hechos por él mismo.

El caso es que en la siguiente plaza para full stack se volvió a presentar, decidimos entrevistarle y, quitando que hablaba muy deprisa, nos dijo que si a todo lo que le preguntamos, no parecía perdido en ningún área de los que preguntamos y su CV era molón, así que le hicimos una oferta a los dos días… Oferta que rechazó porque dijo que ya estaba trabajando en otra empresa que le llamó después de nosotros… En fin, cosas que pasan, a seguir entrevistando.

Esta vez el proceso se hizo un poco más largo y a eso de las dos semanas me contacta de nuevo diciendo que las condiciones de la nueva empresa no le interesan y que si le mantengo la oferta. Yo, que soy un hombre de palabra, le digo que le mantengo exactamente la misma oferta que le hice… Y, dicho y hecho, le preparamos el contrato y quedamos ya para que empiece a trabajar en remoto, básicamente, aunque esperábamos que al menos el primer día se quedase en la oficina para que le explicásemos las cosas.

Primer día: quedamos a las 9:30 porque yo tenía reuniones antes… Espero, se hacen las 10, las 11.., le mando un whatsapp, no me contesta, a las 12 ya le llamo por teléfono y me dice que está en Alcorcón, pero llevando su madre al médico, que luego se pasa por aquí (no me parece lo mejor para el primer día y, además, sin avisar), en fin, que a las 13 se pasa por la oficina, firma el contrato, le enseño a entrar en el portatil y en su cuenta de correo; y… Se va. ¡¡!! Que si, que es un trabajo remoto, pero leche, no solo llegas 4 horas tarde sino que ni siquiera te quedas a que te expliquemos nuestra forma de trabajar…

La primera semana de trabajo se perdió intentando que arrancase solo en ubuntu el portatil y que no usase windows (windows=kk), le compramos hasta una docking station para que pudiese enchufar sus dos monitores (y se lo tuvimos que mandar a casa, que él pasar por l oficina ya le daba pereza), la segunda semana ya empieza a hacer algo productivo pero sin hacer puto caso de lo que le decimos. Nos llena el código de librerías que luego tenemos que borrar y nos quita más tiempo del que ahorramos. En la cuarta semana le ponemos a hacer algo para despliegue inmediato (tampoco era muy complicado, pero dediqué yo más horas que él) y nos dice que se va a vivir a la costa que, total, es remoto y no nos vamos a enterar… Y el pavo se va de mudanza el mismo día que pasamos a producción grrrrr. Obviamente la cosa peta y tengo que arreglarlo yo solo.

Lo primero que hace nada más llegar a su destino es pedirme un anticipo (estábamos a día 7 de mes), cosa que me extraña pero que procedemos a hacer sin ningún problema. A partir de ese momento su rendimiento fue cero. La lista de problemas (en orden cronológico):

  • Que el proveedor de internet que tenía no le permitía usar el puerto del ssh y no podía subir nada al servidor
  • Que los que han venido a repararlo no tenían idea y ha terminado tirando el router por la ventana
  • Que me he quedado dormido (excusa para no aparecer por el daily) y el movil estaba descargado
  • Que se me acaban los datos del móvil
  • Que me van a poner la fibra (ese día desapareció tras el daily y apareció después de haber terminado la jornada diciendo que se había dejado el movil en casa mientras le instalaban la fibra)
  • Que me he cargado el móvil (caída fortuita al suelo, dice)
  • Que sigue sin fibra y tiene que hacer el daily desde un bar
  • Que el del bar ha abierto más tarde hoy y no he podido hacer el daily

Y mientras el trabajo que tenía asignado ahí pudriéndose y teniendo que hacerlo entre sus compañeros y yo…

Llegados a este punto decido que es suficiente y que, obviamente, no ha superado el periodo de prueba. Se lo comunico y le indico que empaquete el portatil, la mochila y la docking station y que alguien pasará a recogerla.

Y ahora salta la sorpresa en las gaunas… Esta persona decide que va a quedarse con el material de la empresa, material que vale más que lo que él ha generado para la empresa. Esto no solo está tipificado como una apropiación indebida sino que dado el valor de lo apropiado puede ponerle en un grave aprieto. Mi empresa, como no puede ser de otra manera, le ha pagado finiquito y liquidación (muchas tentaciones de quedarnos con ese dinero en compensación, pero mi abogado me recomendó pagarlo de todas formas) y este desalmado malnacido no ha dado señales de vida.

Ya es duro mantener y gestionar una empresa como para tener que lidiar con gentuza de esta calaña. No solo ha perjudicado a sus compañeros sino que te deja una mala leche que terminas pagando con el primero que pasa… En fin, solo espero que el karma haga su trabajo. Si alguien quiere contratar a un full-stack developer, que me pregunte a quien no contratar nunca.

¿Qué hacer cuando Autofirma no te muestra ningún certificado?

En mi día a día, y en el de cada vez más personas, el uso de la firma digital se ha convertido en algo habitual. No solo es la obligación legal de las administraciones al relacionarnos con ellas, sino que también abre un nuevo abanico de posibilidades para usos particulares de lo más variopinto.

Una de las cosas que la administración lleva haciendo, digamos, bien, estos años es proporcionarnos herramientas para que nuestras penas burocráticas sean un poco menores (ojo, esto que digo es muuuuuy discutible), o al menos debería serlo. Para mi, que uso Linux en mi día a día y que solo arranco Windows por obligación, tener una manera de hacer firma digital era fundamental. Y resulta que la administración creó una aplicación en Java que podemos utilizar también los de LInux ¡Bien!

La herramienta se llama AutoFirma y podéis descargarla del enlace que os he pasado antes. No es que sea una maravilla de la técnica, pero puedes usarla de manera bastante sencilla ya que te coge los certificados del almacén de Firefox… O lo hacía.

El caso es que, de un tiempo a esta parte el diálogo para escoger el certificado me aparecía vacío y tenía que cargar el archivo .p12 donde tengo la copia de seguridad del certificado que quería utilizar… Y eso era muy penoso dada la cantidad de archivos que tengo habitualmente. La verdad es que me sorprendió que dejase de funcionar, pero creí que sería algo pasajero… Pero no lo fue.

Si a vosotros os pasa algo similar, os doy la receta para que vuelva a funcionar:

Revisad los perfiles de firefox

Si tenéis más de un perfil en firefox (en mi caso tenía tres porque instalé versiones beta de Firefox 100) AutoFirma se confunde y no coge el correcto… Podéis ver los perfiles que tenéis escribiendo about:profiles en Firefox.

En mi caso, y dado que no usaba los otros perfiles para nada, me bastó ir al directorio .mozilla/firefox de mi máquina y eliminar los directorios que no eran mi perfil principal y luego editar el archivo profiles.ini para eliminar todo rastro de esos perfiles.

Una vez hecho esto (no es necesario reiniciar Firefox si no queréis) ya tendréis disponibles de nuevo los certificados para hacer la firma…