Todos los casos
App iOS, web y backend

Klaaro Card

Una billetera nativa para iOS de tarjetas de presentación que cualquiera puede abrir sin la app. Lo más difícil estuvo en el backend: propiedad sin cuentas y una seguridad a nivel de fila que resiste consultas directas a la API.

Rol
PM y desarrollador principal (136 de 149 commits)
Periodo
may 2026 – sep 2026
Equipo
Equipo de 4 personas, Apple Developer Academy
30+
usuarios beta antes del lanzamiento en la App Store
26
migraciones de base de datos versionadas
SwiftSwiftUISupabasePostgreSQLEdge FunctionsSign in with AppleNext.jsCoreImage
Klaaro Card
01

El producto

Klaaro Card es una billetera de tarjetas de contacto profesionales. Diseñas tu propia tarjeta y la compartes por código QR o con un toque. El QR codifica un enlace de klaaro.cards, así que quien lo escanea no necesita la app: los Universal Links abren la app si está instalada, y una página de Next.js muestra la tarjeta si no lo está.

Fui PM y desarrollador principal en un equipo de 4 personas en la Apple Developer Academy, trabajando en la app SwiftUI, las páginas web y el backend en Supabase.

02

Propiedad sin cuenta

Nadie quiere crear una cuenta solo para entregar una tarjeta de presentación, así que por defecto no hay login. - Cada tarjeta pertenece a un token secreto del dispositivo guardado en el Llavero, sincronizado por el Llavero de iCloud cuando está disponible. - Las políticas de escritura de la seguridad a nivel de fila de Postgres comparan ese token con un header de la petición. - Iniciar sesión con Apple es opcional y solo añade sincronización. - Al reinstalar, la app restaura la tarjeta con una función de base de datos que compara el token y nunca lo devuelve.

03

El bug que restauraba la tarjeta de un desconocido

Al principio, la restauración usaba una consulta simple. Las tarjetas tenían dos políticas de lectura: cualquiera puede leer las tarjetas públicas, y los dueños pueden leer la suya con el token. Postgres combina las políticas permisivas con OR, así que en un dispositivo sin token coincidente la consulta igual devolvía todas las tarjetas públicas, y la app podía restaurar la tarjeta de un desconocido al azar.

Moví la restauración a una función SECURITY DEFINER que lee directamente el header del token y devuelve solo la tarjeta que coincide, o nada.

04

Cerrar una fuga de tokens

La seguridad a nivel de fila decide qué filas puede leer un rol, no qué columnas. Con la clave pública de la API, cualquiera podía consultar la columna del token de dispositivo de las tarjetas públicas, y ese token es justo en lo que confía cada política de escritura, así que habría permitido apoderarse de la tarjeta de otra persona. Los IDs de los dueños y los datos de pago que habían elegido ocultar quedaban expuestos de la misma forma.

Revoqué el acceso de lectura a toda la tabla y volví a conceder solo las columnas que necesitan los clientes públicos.

Ese arreglo rompió la sincronización de tarjetas. Los upserts de la app hacían que PostgREST volviera a leer la columna del token, algo que los nuevos permisos prohíben, así que cada guardado fallaba con un 401. Volver a conceder la columna habría reabierto la fuga. En su lugar, un trigger de la base de datos ahora rellena el token a partir del header de la petición, y la app dejó de enviarlo en el cuerpo. Para los clientes públicos, el token ahora es de solo escritura.

05

Trabajo que la app no debería hacer

Las Edge Functions de Supabase se encargan de las tareas que no deberían ejecutarse con una clave pública: una envía notificaciones push cuando alguien recibe una tarjeta, y otra elimina una cuenta y sus datos.

06

Resultados

  • 30+ usuarios beta antes del lanzamiento en la App Store.
  • Disponible en la App Store desde septiembre de 2026.