Skip to content

[CSP] Use the nonce from header manager or receive it as an arg in NextJS? #45774

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Grohden opened this issue Mar 31, 2025 · 6 comments · Fixed by #45794
Closed

[CSP] Use the nonce from header manager or receive it as an arg in NextJS? #45774

Grohden opened this issue Mar 31, 2025 · 6 comments · Fixed by #45794
Assignees
Labels
bug 🐛 Something doesn't work nextjs package: material-ui Specific to @mui/material

Comments

@Grohden
Copy link
Contributor

Grohden commented Mar 31, 2025

Summary

So, I'm following your docs about how to put nonces into the styles

https://mui.com/material-ui/guides/content-security-policy/
https://mui.com/material-ui/integrations/nextjs/#pages-router

I've pieced together that I have to put the the documented components and I should also configure the emotion cache with the nonce... but by doing that I'm adding a custom cache which will override what you all did here:

const cache = options?.emotionCache ?? createEmotionCache();

https://github.com/mui/material-ui/blob/dc7fcff6d406c7d41b539c6d1128300891cb4c53/packages/mui-material-nextjs/src/v13-pagesRouter/createCache.ts

So I'm wondering if its possible to make that config either receive a nonce, expose your create cache function (with the nonce option) or maybe use some approach like the one that next uses, which is the header manager context (if thats exposed)

https://github.com/vercel/next.js/blob/6c4f6be223c0e3a7281960a22744044ac4291468/packages/next/src/client/script.tsx#L212

For now since that createCache is really simple, I'll copy it to our product, but it would be nice to always rely on your impl since I don't really need a custom cache and don't know why that code is not just a direct call to createCache

Examples

This is a feature request, related to the nextjs integration, which is not really MUI, I want to maybe start a discussion?

Motivation

I guess the simplest approach is to expose the createCache function and allow it to receive a nonce for the emotion createCache call, I'm not familiar with how emotion puts itself in the app, but if it has a next js impl maybe it could use the mentioned HeadManagerContext to get the nonce?

Search keywords: nonce, nextjs, csp

@Grohden Grohden added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Mar 31, 2025
@zannager zannager added package: material-ui Specific to @mui/material nextjs labels Apr 1, 2025
@Grohden
Copy link
Contributor Author

Grohden commented Apr 1, 2025

I did setup/clone the cache FN with the nonce option, but there's additional calls to the internal createCache and there's a flush somewhere which inserts (?) stuff without the nonce, thus causing nonce issues :/ I'll make it all work and try to doc this here if that helps

@Grohden
Copy link
Contributor Author

Grohden commented Apr 1, 2025

Just gave up.. I've patched MUI create cache fn:

diff --git a/node_modules/@mui/material-nextjs/esm/v13-pagesRouter/createCache.js b/node_modules/@mui/material-nextjs/esm/v13-pagesRouter/createCache.js
index 002e4bc..210a3d6 100644
--- a/node_modules/@mui/material-nextjs/esm/v13-pagesRouter/createCache.js
+++ b/node_modules/@mui/material-nextjs/esm/v13-pagesRouter/createCache.js
@@ -4,14 +4,23 @@ const isBrowser = typeof document !== 'undefined';
 // On the client side, Create a meta tag at the top of the <head> and set it as insertionPoint.
 // This assures that MUI styles are loaded first.
 // It allows developers to easily override MUI styles with other styling solutions, like CSS modules.
-export default function createEmotionCache() {
+export default function createEmotionCache(options = {}) {
   let insertionPoint;
+  let nonce = options.nonce;
+
   if (isBrowser) {
     const emotionInsertionPoint = document.querySelector('meta[name="emotion-insertion-point"]');
     insertionPoint = emotionInsertionPoint ?? undefined;
+
+    // https://github.com/mui/material-ui/issues/45774
+    const nonceMeta = document.querySelector('meta[property="csp-nonce"]');
+    if(nonceMeta) {
+      nonce = nonceMeta.getAttribute("content")
+    }
   }
   return createCache({
     key: 'mui',
-    insertionPoint
+    insertionPoint,
+    nonce,
   });
 }
diff --git a/node_modules/@mui/material-nextjs/v13-pagesRouter/createCache.js b/node_modules/@mui/material-nextjs/v13-pagesRouter/createCache.js
index 322942b..3af170b 100644
--- a/node_modules/@mui/material-nextjs/v13-pagesRouter/createCache.js
+++ b/node_modules/@mui/material-nextjs/v13-pagesRouter/createCache.js
@@ -11,14 +11,22 @@ const isBrowser = typeof document !== 'undefined';
 // On the client side, Create a meta tag at the top of the <head> and set it as insertionPoint.
 // This assures that MUI styles are loaded first.
 // It allows developers to easily override MUI styles with other styling solutions, like CSS modules.
-function createEmotionCache() {
+function createEmotionCache(options = {}) {
   let insertionPoint;
+  let nonce = options.nonce;
   if (isBrowser) {
     const emotionInsertionPoint = document.querySelector('meta[name="emotion-insertion-point"]');
     insertionPoint = emotionInsertionPoint ?? undefined;
+
+    // https://github.com/mui/material-ui/issues/45774
+    const nonceMeta = document.querySelector('meta[property="csp-nonce"]');
+    if(nonceMeta) {
+      nonce = nonceMeta.getAttribute("content")
+    }
   }
   return (0, _cache.default)({
     key: 'mui',
-    insertionPoint
+    insertionPoint,
+    nonce
   });
 }

And also added these to the createCache call fo the dist/emotion-cache.browser.esm.js

  if(isBrowser) {
    var nonceMeta = document.querySelector('meta[property="csp-nonce"]');
    if(nonceMeta) {
      nonce = nonceMeta.getAttribute("content")
    }
  }

Which is all very specific for me, but still I get errors about CSP, which I suspect are due to SSR not catching the nonce, its a hell to debug, and patch package is broken (so can't make a patch for @emotion/cache) so I'll give up and use unsafe-inline for the time being

@Janpot
Copy link
Member

Janpot commented Apr 2, 2025

I can make it work with the following PR. After installing with

npm install https://pkg.csb.dev/mui/material-ui/commit/373ac3ce/@mui/material-nextjs

and the setup as described in that PR, I get only 1 CSP warning, I'm not quite sure yet where that comes from as both in the html source and in the DOM all inline styles now have the nonce.

@Grohden
Copy link
Contributor Author

Grohden commented Apr 2, 2025

@Janpot just tested your PR and it works! 🙏 thank you for being so quick to investigate it!

@Janpot
Copy link
Member

Janpot commented Apr 2, 2025

Just noticed, the leftover CSP warning I'm seeing comes from the typography component. I won't solve it as part of this effort. But feel free to open a new issue if you want this solved.

@Janpot Janpot added bug 🐛 Something doesn't work and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels Apr 2, 2025
Copy link

github-actions bot commented Apr 3, 2025

This issue has been closed. If you have a similar problem but not exactly the same, please open a new issue.
Now, if you have additional information related to this issue or things that could help future readers, feel free to leave a comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something doesn't work nextjs package: material-ui Specific to @mui/material
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants