Tu backend

Añade las rutas que llama el widget y recibe los webhooks. Con el paquete de Block-Auth para tu servidor es casi todo configuración: canjea el código de login, guarda la sesión y verifica los webhooks por ti. Si lo prefieres, puedes escribirlo tú mismo.

Cómo lo integras

Añade las rutas del backend

Instala el paquete de Block-Auth para tu servidor:

npm install @block-auth.io/node express express-session

Crea BlockAuthRoutes con tus claves y la URL de tu frontend, y móntalo en tu servidor:

import express from 'express'
import session from 'express-session'
import { BlockAuthRoutes } from '@block-auth.io/node'
import { blockauthRouter } from '@block-auth.io/node/express'

const { APP_URL, BLOCKAUTH_API_KEY, BLOCKAUTH_API_SECRET, SESSION_SECRET } = process.env

export const routes = new BlockAuthRoutes({
  apiKey: BLOCKAUTH_API_KEY,
  apiSecret: BLOCKAUTH_API_SECRET,
  appUrl: APP_URL,
})

export const app = express()
app.use(
  session({
    secret: SESSION_SECRET,
    resave: false,
    saveUninitialized: false,
    cookie: { httpOnly: true, sameSite: 'lax', secure: APP_URL.startsWith('https') },
  }),
)
app.use(blockauthRouter(routes))
app.use(express.json())

app.listen(4000)

Y ya está. Tu servidor responde estas rutas bajo /auth/blockauth, la ruta que el widget llama por defecto:

RutaQué hace
POST /codeCanjea el código de login en Block-Auth, crea la sesión y devuelve { address, did, roles }
GET /meDevuelve el usuario con sesión, o 401
POST /logoutCierra la sesión en tu app y en Block-Auth
GET y POST /linkVincula tus usuarios existentes (solo con la opción appUser)
POST /webhookRecibe los webhooks

Lo que el paquete ya hace por ti:

  • Solo se fía de Block-Auth. El usuario sale siempre de canjear el código con tu apiSecret, y el redirectUri tiene que estar en appUrl. Si tu frontend vive en varios orígenes, pasa allowedOrigins (una lista) en su lugar.
  • Bloquea peticiones falsificadas. Solo acepta POST con cuerpo JSON, así otra web no puede iniciar sesión a tus usuarios con un formulario.
  • Mantiene la sesión corta y limpia. Renueva el ID de sesión en cada login y la termina cuando caduca la firma del usuario en Block-Auth (signedExpiredAt). Para terminarla antes, pon maxSessionSeconds (MaxSessionDuration en .NET).
  • Cierra sesión en todas partes. POST /logout cierra también las sesiones de Block-Auth del usuario para tu app, y conserva el resto de datos de tu sesión.
  • Verifica cada webhook. Firma, hora y apiKey, antes de llamar a tu código.

Si tus rutas tienen que vivir bajo otra ruta, ponla en basePath y la misma como authEndpoint en el widget. Para desactivar una ruta que no usas, pasa routes: { link: false } (en .NET, añádela a DisabledRoutes). Las opciones van en snake_case en Python y en PascalCase en .NET.

Usa la sesión en tus rutas

A partir de aquí, la sesión es la fuente de verdad. Lee el usuario antes de hacer nada que necesite un usuario con sesión o un rol. Recibes null si no hay sesión, si caducó o si la cerró un webhook:

import { expressSessionStore } from '@block-auth.io/node/express'

const currentUser = (req) => routes.currentUser({ session: expressSessionStore(req) })

const requireRole = (slug) => async (req, res, next) => {
  const user = await currentUser(req)
  if (!user) return res.status(401).end()
  if (!user.roles.some((role) => role.slug === slug)) return res.status(403).end()
  next()
}

app.delete('/api/orders/:id', requireRole('acme-x7k2p9'), deleteOrder)

El usuario tiene address, did, roles ({ name, slug }), externalId (el ID de tu usuario, si está vinculado), loggedInAt, expiresAt (segundos Unix) y data. En .NET los roles son también claims de rol con el slug. Usa el slug del rol, no el nombre: mira Roles.

Para guardar tus propios datos en la sesión, o para rechazar un login, añade onLogin(user): devuelve un objeto para guardarlo como data, o false para rechazar al usuario (sale también de Block-Auth). En .NET, AuthorizeLogin decide y OnLogin devuelve los datos. onLogout(user) se ejecuta cuando el usuario cierra sesión.

Recibe los webhooks

Block-Auth llama a tu backend cuando un usuario cierra la sesión de tu app desde Block-Auth (signout) o desvincula tu app (account.unlinked), para que también puedas cerrar su sesión.

Así llega a tu página un cierre de sesión hecho en Block-Auth (Identity, la extensión u otro dispositivo):

  1. 1Block-AuthEl usuario cierra sesión
  2. 2Block-Auth → Tu backendPOST /webhook · signout
  3. 3Tu backendGuarda el cierre
  4. 4Tu frontend → Tu backendGET /me al volver o cada 60 s
  5. 5Tu backend → Tu frontend401
  6. 6Tu frontendMuestra al usuario sin sesión
  1. En el Dashboard, abre tu aplicación y ve a Autenticación. En el tercer campo de URL, que aparece como «Cuando falla el inicio de sesión», escribe la ruta /auth/blockauth/webhook (tu dominio ya viene puesto).
  1. Dile al paquete cómo recordar un cierre de sesión y cómo comprobarlo. POST /webhook ya existe: verifica la petición y llama a onSignout u onUnlinked. Después isRevoked cierra cada sesión que empezó antes de ese cierre:
export const routes = new BlockAuthRoutes({
  apiKey: BLOCKAUTH_API_KEY,
  apiSecret: BLOCKAUTH_API_SECRET,
  appUrl: APP_URL,
  onSignout: (event) => saveSignout(event.address, event.iat),
  onUnlinked: async (event) => {
    await saveSignout(event.address, event.iat)
    await removeLink(event.externalId)
  },
  isRevoked: async (user) => ((await lastSignout(user.address.toLowerCase())) ?? -1) >= user.loggedInAt,
})

saveSignout, lastSignout y removeLink son tuyas: guárdalo en tu base de datos o en una caché compartida como Redis, para que lo vean todas las instancias de tu servidor. address llega en minúsculas, e iat y loggedInAt son segundos Unix.

  • isRevoked se ejecuta en cada GET /me y cada vez que lees el usuario. El widget consulta /me al volver a la pestaña y cada minuto, así que la página cierra la sesión sola.
  • Para rechazar un envío repetido, añade isReplay(jti) y devuelve true si ya viste ese jti.