jdPerez Logo Header
10 minutos de lectura | 24/07/2024

Habilitar GA4 y/o TagManager en PWAStudio

Como ya sabemos, PWA Studio y Magento, se comunican gracias al recurso UPWARD, separando por completo lo que es Magento, dejándolo como servidor, de la PWA como Headless para el cliente, con lo cual, las restricciones de librerías externas y la seguridad correrá por cuenta de la PWA.

¿Que ocurre?

Para ello, la PWA Studio, dispone de una extensión, insertada en su propio package, llamada upward-security-headers. En la que podemos ver el siguiente fichero upward.yml

En pocas palabras, aquí lo importante en este caso sería esto:

veniaSecurityHeaders:
  resolver: inline
  inline:
    content-security-policy:
      template:
        resolver: conditional
        when:
          - matches: env.SCRIPT_NAME
            pattern: '.*\.php$'
            use:
              inline: "
                script-src http: https: {{ backend }}{{#pageTypeNonce}} 'nonce-{{ pageTypeNonce }}'{{/pageTypeNonce}};
                style-src 'self' blob: https: 'unsafe-inline' {{ backend }};
                img-src data: http: https:;
                object-src 'none';
                base-uri 'none';
                child-src 'self';
                font-src 'self' fonts.gstatic.com;
                frame-src assets.braintreegateway.com *.google.com *.youtube.com *.youtu.be *.vimeo.com
                "
        default:
          inline: "
            script-src http: https: {{ backend }};
            style-src 'self' blob: https: 'unsafe-inline' {{ backend }};
            img-src data: http: https:;
            object-src 'none';
            base-uri 'none';
            child-src 'self';
            font-src 'self' fonts.gstatic.com;
            frame-src assets.braintreegateway.com *.google.com *.youtube.com *.youtu.be *.vimeo.com

Bien podemos ver aquí, que dan permisos a la inserción de script-src solo a nuestro Backend (al magento2).

¿Cómo solucionamos esto?

En nuestro proyecto debemos de tener un local-intercept.js, si no es el caso, os recomiendo echar un vistazo a esta parte de la doc de Magento Targets and Targetables.

Pero, lo normal es tener un interceptor en nuestro proyecto, podemos ver la ruta de este fichero en el package.json, que es donde se tuvo que haber definido.

En el local-intercept.js debemos añadir la siguiente función js:

...
function addCspSecurity(targets) {
  const builtins = targets.of('@magento/pwa-buildpack');
 
  builtins.transformUpward.tapPromise(async definitions => {
    if (!definitions['customSecurityHeaders']) {
      throw new Error(
        `${
          targets.name
        } could not find its own definition in the emitted upward.yml`
      );
    }
 
    const veniaSecurityHeaders = definitions.veniaSecurityHeaders.inline;
    const securityHeaders = definitions['customSecurityHeaders'].inline;
 
    for (const name of Object.keys(securityHeaders)) {
      veniaSecurityHeaders[name] = `${'customSecurityHeaders'}.${name}`;
    }
  });
}
...
 
module.exports = (targets) => {
...
  addCspSecurity(targets);
...
};

Este método, básicamente, lo que estaría haciendo es, sustituir el veniaSecurityHeaders de la extensión de la PWAStudio por nuestros propios parámetros de seguridad.

Después de esto, actualizaremos el upward.yml de nuestro proyecto, añadiendo justamente, el nombre que le hayamos dado anteriormente, en mi caso lo he llamado customSecurityHeaders:

...
customSecurityHeaders:
  resolver: inline
  inline:
    content-security-policy:
      resolver: template
      engine: mustache
      provide:
        backend: env.MAGENTO_BACKEND_URL
        pageTypeNonce: veniaPageTypeNonce.nonce
      template:
        resolver: conditional
        when:
          - matches: env.NODE_ENV
            pattern: development
            use:
              inline: ""
          - matches: env.SCRIPT_NAME
            pattern: '.*\.php$'
            use:
              inline: "
                script-src http: https: 'unsafe-inline' *.googletagmanager.com *.google-analytics.com {{ backend }}{{#pageTypeNonce}} 'nonce-{{ pageTypeNonce }}'{{/pageTypeNonce}};
                style-src 'self' blob: https: 'unsafe-inline' {{ backend }};
                object-src 'none';
                base-uri 'none';
                child-src 'self';
                font-src 'self' fonts.gstatic.com;
                frame-src assets.braintreegateway.com *.google.com *.youtube.com *.youtu.be *.vimeo.com;
                img-src http: https: *.googletagmanager.com *.google-analytics.com;
                connect-src 'self' *.googletagmanager.com *.google-analytics.com *.analytics.google.com
                "
        default:
          inline: "
            script-src http: https: 'unsafe-inline' *.googletagmanager.com *.google-analytics.com {{ backend }};
            style-src 'self' blob: https: 'unsafe-inline' {{ backend }};
            object-src 'none';
            base-uri 'none';
            child-src 'self';
            font-src 'self' fonts.gstatic.com;
            frame-src assets.braintreegateway.com *.google.com *.youtube.com *.youtu.be *.vimeo.com;
            img-src http: https: *.googletagmanager.com *.google-analytics.com;
            connect-src 'self' *.googletagmanager.com *.google-analytics.com *.analytics.google.com
            "
    strict-transport-security:
      inline: max-age=31536000
    x-content-type-options:
      inline: nosniff
    x-frame-options:
      inline: SAMEORIGIN
    x-xss-protection:
      inline: '1; mode=block'
...

Resultado

Después de hacer esto, el resultado que deberíamos ver, es cómo se ejecutan los scripts de analítica y gtm en el proyecto, así como poder tener acceso a los datos de navegación.

Gracias por leer este artículo!