Documentación
superleads.mx

Contexto de usuario en apps del Marketplace (Shared Secret)

Módulos de Marketplace · ☕ 4 min de lectura
Actualizado el 19 Jun 2026

Contexto de usuario en apps del Marketplace

La plataforma ofrece un mecanismo seguro para acceder a la información del usuario autenticado mediante tokens firmados. Esta guia explica como generar y usar una llave Shared Secret para acceder a ese contexto de forma segura — útil si tu app necesita saber, por ejemplo, que persona de tu equipo esta usando la integración en ese momento.

Configurar el Shared Secret

Generar una llave Shared Secret

Primero necesitas generar una llave Shared Secret para tu aplicación:

  1. Ve a la Configuración avanzada de tu aplicación
  2. Entra a la sección de Auth
  3. En Shared Secret, da clic en Generar para crear tu llave
Generación de la llave Shared Secret

Metodos de implementacion en el frontend

Hay dos formas de acceder a estos datos desde tu frontend, según donde corra tu código:

1. Implementacion con JavaScript personalizado

Si estas usando JavaScript personalizado inyectado en páginas de la plataforma, usa el metodo exposeSessionDetails:

async function getUserData() {
  try {
    // APP_ID es el identificador unico de tu aplicacion
    const encryptedUserData = await window.exposeSessionDetails(APP_ID)

    // Envia estos datos cifrados a tu backend para descifrarlos
    const response = await fetch('your-backend-endpoint', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ encryptedData: encryptedUserData })
    })

    const userData = await response.json()
    return userData
  } catch (error) {
    console.error('Failed to fetch session details:', error)
    throw error
  }
}

2. Implementacion en páginas personalizadas (Custom Pages)

Si estas intentando obtener el contexto de usuario dentro de una página personalizada, usa el metodo postMessage para comunicarte con la ventana padre:

async function getUserData() {
  try {
    const encryptedUserData = await new Promise((resolve) => {
      // Solicita los datos del usuario a la ventana padre
      window.parent.postMessage({ message: 'REQUEST_USER_DATA' }, '*')

      // Escucha la respuesta
      const messageHandler = ({ data }) => {
        if (data.message === 'REQUEST_USER_DATA_RESPONSE') {
          window.removeEventListener('message', messageHandler)
          resolve(data.payload)
        }
      }

      window.addEventListener('message', messageHandler)
    })

    // Envia los datos cifrados a tu backend para descifrarlos
    const response = await fetch('your-backend-endpoint', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ encryptedData: encryptedUserData })
    })

    const userData = await response.json()
    return userData
  } catch (error) {
    console.error('Failed to fetch user data:', error)
    throw error
  }
}

Implementacion en el backend

Sin importar que metodo de frontend uses, el proceso de descifrado en el backend es el mismo:

const CryptoJS = require('crypto-js')

function decryptUserData(encryptedUserData, sharedSecretKey) {
  try {
    const decrypted = CryptoJS.AES.decrypt(encryptedUserData, sharedSecretKey).toString(CryptoJS.enc.Utf8)
    return JSON.parse(decrypted)
  } catch (error) {
    throw new Error('Failed to decrypt user data')
  }
}

// Ejemplo de endpoint con Express
app.post('/decrypt-user-data', (req, res) => {
  try {
    const { encryptedData } = req.body
    const userData = decryptUserData(encryptedData, process.env.GHL_APP_SHARED_SECRET)
    res.json(userData)
  } catch (error) {
    res.status(400).json({ error: 'Failed to decrypt user data' })
  }
})

Estructura de los datos descifrados

Tras descifrarlos, los datos se devuelven como un objeto JSON con la información del usuario. La estructura varia según si se accede desde el contexto de una Agencia o de una Sede.

Contexto de Agencia

{
  "userId": "MKQJ7wOVVmNOMvrnKKKK", // Identificador unico del usuario
  "companyId": "GNb7aIv4rQFVb9iwNl5K", // Identificador unico de la agencia/empresa
  "role": "admin", // Rol del usuario en el sistema
  "type": "agency", // Indica que es un usuario de agencia
  "userName": "John Doe", // Nombre completo del usuario
  "email": "johndoe@gmail.com" // Correo del usuario
}

Contexto de Sede

Al acceder desde el contexto de una sede, los datos descifrados incluyen además el campo activeLocation:

{
  "userId": "MKQJ7wOVVmNOMvrnKKKK", // Identificador unico del usuario
  "companyId": "GNb7aIv4rQFVb9iwNl5K", // Identificador unico de la agencia/empresa
  "role": "admin", // Rol del usuario en el sistema
  "type": "agency", // Indica que es un usuario de agencia
  "activeLocation": "yLKVZpNppIdYpah4RjNE", // Identificador unico de la sede activa
  "userName": "John Doe", // Nombre completo del usuario
  "email": "johndoe@gmail.com" // Correo del usuario
}

Descripcion de los campos

Campo Tipo Descripcion
userId string Identificador único del usuario
companyId string Identificador único de la agencia/empresa
role string Rol del usuario en el sistema (ej. 'admin', 'user')
type string Tipo de contexto ('agency' o 'location')
activeLocation string (Solo en contexto de sede) Identificador único de la sede activa
userName string Nombre completo del usuario
email string Correo del usuario

Implementacion de referencia

Para un ejemplo completo, puedes consultar el repositorio de plantilla de apps del Marketplace:

Plantilla de app del Marketplace de GoHighLevel

La implementacion relevante esta en el endpoint /decrypt-sso de la plantilla.

Consideraciones de seguridad

  • Nunca expongas tu llave Shared Secret en código del lado del cliente.
  • Realiza siempre el descifrado en tu backend.
  • Guarda tu llave Shared Secret de forma segura usando variables de entorno.
  • Usa HTTPS para toda comunicación entre tu frontend y tu backend.
  • Rota tus llaves Shared Secret periodicamente para mayor seguridad.