Cómo hacer que tu app en Heroku sea accesible desde los dominios de tus clientes

Hace un tiempo que desarrollo en Gret (cliente de Platanus), una plataforma para vender productos digitales y cobrar como tú prefieras. En esta creas portales donde publicas distintos tipos de contenido (videos, guías, podcasts) y tus “consumidores de contenido'' pueden verlos gratis, o pagar por acceder a ellos. Así, cada “portal” tiene un link de este tipo: app.gret.io/communities/contenido-platanus .

Los admins de Gret se dieron cuenta que muchos clientes querían usar la plataforma PERO mostrar los contenidos en sus propios dominios. En vez de https://app.gret.io/communities/contenido-platanus quieren que sea algo como content.platanus.tv, sin que salga Gret.

Esto tiene una serie de desafíos:

  1. ¿Cómo hacer que la conexión del dominio del cliente con Gret sea simple de entender para usuarios no técnicos?
  2. ¿Cómo hacer para que los usuarios de platanus.tv solo tengan acceso al contenido de Platanus y no a otros portales?
  3. ¿Voy a tener que meterme con NGINX? (ay no por favor).

En Platanus usamos Rails y Heroku. Con esas tecnologías, a grandes rasgos, la solución sería así:

  1. Permitir que el dueño del portal agregue su dominio y subdominio custom para su portal, y guardarlo en base de datos.
  2. Cuando alguien accede a nuestro servidor desde un dominio custom que algún usuario configuró, entonces se redirige al portal correspondiente. Si intenta entrar a otro portal, está prohibido.
  3. Agregar el dominio custom de portal a Heroku para que el nuevo dominio apunte al servidor de Heroku (content.platan.tv apunte a app.gret.io)

Si con este punteo ya tienes una idea de cómo implementar el custom domain en tu plataforma te recomiendo leer la sección de Problemas que tuvimos para que los tengas en cuenta.

Si quieres saber cómo lo implementamos, aquí va con más detalle:

Custom domain en Heroku

Voy a partir explicando bien rápido como funcionan los dominios custom en Heroku, así el diseño de la solución tendrá más sentido. Yo lo mostraré desde la interfaz de Heroku pero también se puede hacer con Heroku CLI y por API.

Nota 1: Durante el desarrollo tomamos la decisión de permitir únicamente subdominios. ¿Por qué? Porque la forma de configurar dominios “raíz” es distinta en cada proveedor DNS. Heroku da información de cómo hacerlo en los proveedores más comunes, pero preferimos evitar instrucciones tan específicas.

Nota 2: Dado que trabajamos haciendo desarrollo iterativo y queríamos salir lo más pronto posible a producción decidimos no conectarnos a la API de heroku. Sino que el administrador de la plataforma hará la conexión del dominio a mano cuando un usuario lo requiera.

Aquí van los pasos para configurar un dominio en heroku

1. Ir a settings en la app de heroku. Y bajar a la parte de domains.

Heroku settings sobre Domains

2. Click en Add domain y agregar el dominio.

Agregar Domain en Heroku

3. Al agregar el dominio aparecerá un DNS target. Este link tienes que copiarlo y agregarlo en tu proveedor DNS (goDaddy, cloudFlare ..) al crear el subdominio .

Nuevo Domain creado en Heroku con su DNS target

En resumen con estos 3 o 4 pasos tienes tu app de Heroku accesible desde un dominio custom.

Ahora quedan los siguientes 2 problemas por resolver.

Dueño del portal puede agregar su custom domain

El primer problema que abordaremos es que el dueño del portal pueda configurar su custom domain. Para esto el usuario tendrá que solicitar el custom domain y el administrador de Gret tendrá que agregar este dominio a Heroku (como vimos anteriormente), para que finalmente el dueño pueda configurar el dominio en su proveedor DNS.

1.Dueño del portal “solicita un custom domain”

Se implementó una interfaz donde se le pide al dueño del portal su dominio y el sub-dominio custom. Esta información se guarda en la base de datos, asociada al Portal.

Cuando el dueño del portal hace click en “SOLICITAR DOMINIO”, se le envía un correo al administrador de Gret para que configure este dominio en heroku.

2. Administrador de Gret configura el dominio en Heroku

El administrador de Gret tendrá que agregar el custom domain en Heroku como vimos más arriba. Esto generará un dns target que el admin tendrá que ingresar en el admin de Gret.

3. Dueño del portal recibe instrucciones para terminar la configuración

Una vez que se ingresa el Dns Target, la plataforma  le enviará un correo al dueño del portal para avisarle que su dominio está listo en Gret.

Ahora falta que el dueño del portal configure el dominio en su proveedor DNS. El dueño del portal verá esta vista luego de recibir el mail:

Una vez que el dueño del portal configure su subdominio en su proveedor DNS, en Heroku veremos que este se verificó y estará listo para usarse.

Si quieres que tus usuarios puedan configurar su dominio para ver tu plataforma completa, hasta este paso estás listx. Si además necesitas restringir a qué partes de la plataforma pueden acceder los usuarios según el dominio, lee la siguiente parte.

Restringir contenidos según el dominio

En este paso lo que queremos lograr es que los usuarios que acceden a un portal desde un custom domain, no puedan ver el contenido de otros portales. Además cuando se invite a usuarios nuevos por mail habrá que redirigirlos al dominio custom.

1. Limitar acceso a la plataforma

Aquí es importante definir qué partes de la plataforma queremos que estén restringidas según el dominio y cuáles no. En el caso de Gret nos dimos cuenta que solo necesitábamos restringir las vistas donde acceden los consumidores de contenido. En cambio que las vistas de administración de los portales no necesitan restricción del dominio.

Otro requerimiento que teníamos era que al entrar desde el custom domain el usuario fuera redirigido directamente al portal. Osea si entro a contenido.platan.tv que me redirija a https://content.platanus.tv/communities/contenido-platanus

Esto en código significa que el show de Portal y de un contenido tienen que verificar si se está accediendo desde un custom domain y confirmar si ese dominio tiene acceso a ese portal/contenido.

En el controller de Portal y de Contenido existe un before_action que setea una variable @community (en los modelos un Portal se llama community). Ahora con el custom domain tenemos que agregar set_domain antes de set_community para validar si se está ingresando desde un custom domain.

 def set_domain
   @domain = DnsConfig.find_by(domain: request.host)
 end

Con esto, al crear la variable @community podemos verificar si el portal al que queremos acceder es accesible con el @domain desde el que se está entrando, o si la persona está entrando desde la app original de Gret.

def set_community
   community = Community.friendly.find(params[:id])
   not_found unless @domain&.communities&.include?(community) ||   using_gret_host?
   @community = community.decorate
 end
 def using_gret_host?
   request.host == URI.parse(ENV.fetch('APPLICATION_HOST')).host
 end

Algunos portales tienen la opción de hacer que sus contenidos sean privados, e invitar por mail a personas a verlos. Cuando se invita a alguien se le envía un correo con un link al portal.

El problema con los custom domains es que este link ahora no tendrá que ser a la app de Gret sino que al custom domain del portal.

Aquí aparece un problema: ¿Qué pasa si un dueño de portal solicita un custom domain pero nunca lo configura en su proveedor DNS?

Si ignoramos esto solo tendríamos que redirigir al custom domain si el Portal lo solicitó. En cambio, si no ha configurado un custom domain redirigimos a la app de Gret.

Pero si el custom domain en realidad no existe porque la persona no lo ha configurado?

Solución

Agregamos un field ready donde el administrador de Gret tendrá que marcar cuando en heroku le aparezca que se validó un dominio. Así solo redirigiremos a los usuarios al custom domain cuando realmente el dominio exista.

Algunos problemas que tuvimos

Diseño UX

Dominio, url, subdominio, host, dns provider, cname son conceptos muy complicados tanto para devs como para usuarios comunes. Entonces es importante preocuparse de que el no conocer estos conceptos no sea un impedimento para completar la creación de un custom domain.

Vimeo

Usamos vimeo para mostrar los videos que suben los creadores de contenido, pero tenemos restringido que estos se vean solo para el dominio de Gret. Dado esto, nos pasó que cuando un usuario configuró su dominio los videos no se podían reproducir.

La solución manual es que cuando se crea un video nuevo en un portal con custom domain el administrador de Gret tendrá que agregar el custom domain al video en Vimeo.

La solución automatizada a esto es que cuando se cree un video, se configure a través de la API el video para que sea visible en los dominios asociados al Portal.


Eeeen fin. Viéndolo en retrospectiva no son tantos los cambios que hay que hacer para que tu app de Heroku sea accesible desde los dominios de tus usuarios.

Definitivamente al crear los dominios en Heroku por API el proceso será mucho más fácil para el admin de la plataforma y los usuarios, pero era importante validar la funcionalidad primero.

Espero que con esta implementación otrxs devs puedan agregar esta funcionalidad sin tanta investigación y dolores de cabeza.