Saltar al contenido principal

DNN · Embebido del widget

El widget se carga desde el CDN como un único <script> con data-token apuntando al Widget Token (24 h) emitido por Neuroon. La pieza específica de DNN es:

  1. Emisión del token server-to-server desde DNN, con cache en memoria.
  2. Render del <script> en un .ascx (Skin o SkinObject) imprimiendo el token desde code-behind.
  3. SRI fija para asegurar la integridad del binario del widget.

El token NO lo firma el cliente

El Widget Token es opaco y emitido por el backend Neuroon. Tu integración no firma nada; solicita el token con tu Shop API Key y lo inyecta en data-token. Ver Authentication · Widget Token.

Endpoint usado:

POST/api/shops/{id}/widget-token

Cache server-side (24 h con margen)

DNN no debería pegarse al endpoint /widget-token en cada request. El patrón estándar:

public static class WidgetTokenProvider
{
private static readonly object Lock = new();
private static string _token;
private static DateTime _expires;

public static string Get()
{
lock (Lock)
{
if (_token != null && DateTime.UtcNow < _expires.AddMinutes(-5))
return _token;

var fresh = new NeuroonClient().IssueWidgetTokenAsync().GetAwaiter().GetResult();
_token = fresh;
_expires = DateTime.UtcNow.AddHours(24);
return _token;
}
}
}

Notas:

  • Margen de 5 minutos antes de expirar para evitar 401 en peticiones de borde.
  • En entornos multi-AppPool / web farm, sustituye el static por una cache distribuida (Redis, OutputCache). - En .NET 8, prefiere SemaphoreSlim o IMemoryCache con Lazy<Task<string>>.

Render en .ascx

Skin / SkinObject mínimo. Coloca un placeholder donde quieras la barra de búsqueda:

<%@ Control Language="C#" AutoEventWireup="true" %>
<%@ Register TagPrefix="dnn" Namespace="DotNetNuke.UI.Skins.Controls" Assembly="DotNetNuke" %>
<asp:PlaceHolder runat="server">
<div id="neuroon-search"></div>
<script
src="https://cdn.neuroon.ai/widget@0.9.10/widget.js"
integrity="sha384-JTaG/IN0Jj/ImfUj2x5QVMG4HkbFHzui7fTpLtwl1hsP+kY9W8OODeSJRFWN1ZP5"
data-token='<%= WidgetTokenProvider.Get() %>'
data-container="#neuroon-search"
data-theme="auto"
data-locale='<%= System.Threading.Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName %>'
data-api-url='<%= NeuroonSettings.ApiUrl %>'
crossorigin="anonymous"
async></script>
</asp:PlaceHolder>

El SRI hash y la versión del widget proceden del manifest oficial: https://cdn.neuroon.ai/widget@VERSION/sri-manifest.json. La versión 0.9.10 y su hash provienen del plugin WordPress .

CSP (Content Security Policy)

Si tu Skin aplica CSP estricta, añade los hosts mínimos:

Content-Security-Policy:
script-src 'self' https://cdn.neuroon.ai;
connect-src 'self' https://api.neuroon.ai https://dev-api.neuroon.ai;
img-src 'self' data: https:;
style-src 'self' 'unsafe-inline';

Para Production-only, omite dev-api.neuroon.ai. El widget no requiere unsafe-eval.

Locale

data-locale acepta cualquier código ISO 639-1 (es, en, de, etc.). En DNN, lo natural es:

data-locale='<%= System.Threading.Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName %>'

Si tu site es multi-portal, usa PortalSettings.Current.PortalAlias.CultureCode. Lee también Widget · i18n para el comportamiento de fallback en el cliente.

Verificar el embebido

  1. Abre la página pública con DevTools.
  2. Network → Filter widget: deberías ver una petición a https://cdn.neuroon.ai/widget@0.9.10/widget.js con status 200 y header X-Content-Type-Options: nosniff.
  3. Console: ningún Failed to find a valid digest in the 'integrity' attribute. Si aparece, el SRI o la versión no coinciden.
  4. Application → Cookies: el widget no debería poner cookies de tracking en este dominio.

Embebido condicional

Para renderizar el widget solo en ciertas páginas / portales:

protected void Page_PreRender(object sender, EventArgs e)
{
var portal = PortalSettings.Current;
if (portal != null && portal.HomeTabId == this.TabId)
{
widgetPlaceholder.Visible = true;
}
}

Próximos pasos