-
Notifications
You must be signed in to change notification settings - Fork 10.3k
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
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
Gatsby Plugin: Asset Manifest (for Server-Side Authentication in Front of Built Assets without client-side routes) #20745
Comments
Thanks for taking the time to track this request @karlhorky 👍 |
I have created a repo with my setup for the secure Express server-side authentication of Gatsby static files (no client-only routes or open static content!) here: https://github.com/karlhorky/gatsby-serverside-auth0 This also includes my above-mentioned regular expressions for rudimentary access control on a per-user and per-page basis, which is what this issue hopes to get a better solution for! |
Interesting issue, @karlhorky and thank you for taking the time to write this up.
|
Ok great! So I just need to I'll see if I can make something work in the repo: https://github.com/karlhorky/gatsby-serverside-auth0 Edit: Done: Updated the proof of concept repo: Here's the difference: Old Solution// Regular expression to match allowed assets related
// to src/pages/index.mdx in the Gatsby website.
//
// Trying to navigate to assets related to src/pages/page-2.mdx
// will return an "Access denied."
const allowedGatsbyWebsiteUrls = /^(\/|\/(webpack-runtime|app|styles|commons|component---src-pages-index-mdx)-[a-z0-9]+\.js|\/page-data\/(index\/)?(page|app)-data.json|\/(static|icons)\/.+\.png(\?v=[a-z0-9]+)?)(\?[^/]+)?$/; New Solution// Require the Gatsby asset manifest from the build
// to get paths to all assets that are required by
// each "named chunk group" (each named chunk group
// corresponds to a page).
//
// Ref: https://github.com/gatsbyjs/gatsby/issues/20745#issuecomment-577685950
const {
namedChunkGroups,
} = require('../gatsby-website/public/webpack.stats.json');
function pageToWebpackFormat(page) {
// Replace slashes and periods with hyphens
return page.replace(/(\/|\.)/g, '-');
}
function pageToGatsbyPageDataPath(page) {
// Strip the /index.mdx at the end of the page
// If it's the index, just strip the .mdx
return page.replace(/(\/index)?\.mdx$/, '');
}
function pageToWebPaths(page) {
// Strip the index.mdx at the end of the page
let pageWithoutIndex = page.replace(/((\/)?index)?\.mdx$/, '');
// Add a slash, but only for non-root paths
if (pageWithoutIndex !== '') pageWithoutIndex += '/';
return [pageWithoutIndex, pageWithoutIndex + 'index.html'];
}
function getPathsForPages(pages) {
return (
pages
.map(page => {
return [
// All asset paths from the webpack manifest
...namedChunkGroups[
`component---src-pages-${pageToWebpackFormat(page)}`
].assets,
// All of the Gatsby page-data.json files
`page-data/${pageToGatsbyPageDataPath(page)}/page-data.json`,
...pageToWebPaths(page),
];
})
// Flatten out the extra level of array nesting
.flat()
.concat(
// Everything general for the app
...namedChunkGroups.app.assets,
'page-data/app-data.json',
)
.filter(
assetPath =>
// Root
assetPath === '' ||
// Only paths ending with js, json, html and slashes
assetPath.match(/(\.(html|js|json)|\/)$/),
)
// Add a leading slash to make a root-relative path
// (to match Express' req.url)
.map(assetPath => '/' + assetPath)
);
}
const allowedWebpackAssetPaths = getPathsForPages([
'index.mdx',
]);
function isAllowedPath(path) {
const pathWithoutQuery = path.replace(/^([^?]+).*$/, '$1');
// Allow access to the manifest
if (pathWithoutQuery === '/manifest.webmanifest') return true;
// Allow access to images within static and icons
if (pathWithoutQuery.endsWith('png')) {
if (
pathWithoutQuery.startsWith('/static/') ||
pathWithoutQuery.startsWith('/icons/')
) {
return true;
}
}
return allowedWebpackAssetPaths.includes(pathWithoutQuery);
} |
So the Files in
|
@sidharthachatterjee would the Gatsby team be open to creating a separate Asset Manifest for these files? Maybe in the same format as the webpack stats? It would allow for my new solution above to be further simplified. |
@karlhorky Yup, absolutely. I think this could be a pretty cool gatsby plugin which could use
Hmm, this is interesting. @pieh Do we keep a dependency graph of these per page entry point?
Could list these like you said in
These names can be hard coded because they will always be called these (by design) but I'd consider them internal implementation details which we might break in a minor version
This should be okay to hardcode |
Hiya! This issue has gone quiet. Spooky quiet. 👻 We get a lot of issues, so we currently close issues after 30 days of inactivity. It’s been at least 20 days since the last update here. Thanks for being a part of the Gatsby community! 💪💜 |
Update: I've added some features and fixed some things in the proof of concept:
|
This is really awesome work. I am trying to deal with something similar (cloudfront+lambda@edge+s3, block unauthenticated requests to /blog/private/* with the lambda) One concern I have for the approach you are taking: I notice that my in my website generated by gatsby-transformer-remark, the entire site's content is contained in my Have you run into/investigated this problem @karlhorky? (it could be specific to the plugin I'm using) (I suspect it might be due to my plugin, the allPages GQL query is what is contained in the main js bundle, which obviously contains all the page data) |
No, my It only contains a mapping to each of the pages (so if the page titles are secret, that could be an issue). |
Saw that @sidharthachatterjee added some new paths on These cause all JavaScript on the page to break if these pre-fetch requests do not succeed, because of how the requests are handled (no So this caused the solution above to break (understandable, when using undocumented internals). I've published a fix here: |
This behavior of causing all JavaScript on the page to break if pre-fetching fails seems like it could be improved though. Maybe it could be addressed as part of #25330 |
Another change to the static query paths by Sidhartha in #26242 ... |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
Summary
Gatsby static sites are very fast and optimized. But some content may not be suitable to deliver to all audiences. There are often use cases for including an authentication layer.
Currently, Gatsby promotes using client-only routes for authentication, which negate many of the benefits of the static site generation.
It is possible to set up a Node.js server to achieve server-side authentication (example with Auth0 here: https://github.com/karlhorky/auth0-node-heroku). This example can be extended to serve the Gatsby static assets via
express.static
or similar - if the user is authenticated, they get the static content back; if not, they receive a 403 Forbidden.This almost achieves what we want! But it is an all-or-nothing solution - there is no way to restrict access to specific Gatsby assets (for example, based on pages), without multiple crazy, error-prone regexes like this:
Spoiler: I'm using this crazy regular expression option right now 😅
Proposals
I propose offering and documenting one or more tools to support a server-side authentication flow for completely static sites (without using client-only routes), similar to @pieh's comment on #1100 (comment):
1. An Asset Manifest would be a great start!
This would allow for simpler configuration of user-level and page-level access-control:
Of course, I'm not fixed on the API for the manifest. I'd be open to having helpers to extend this too!
2. Configurable Pre-fetching
One thing that these solutions cause is a lot of failed pre-fetching requests for users without full access:
Maybe there could be a way to configure pre-fetching client-side? So that different resources could be pre-fetched per user?
Basic example
Examples in Proposals section above.
Motivation
Gatsby users will commonly want authentication flows in their apps, and they should also want performant applications, which can be achieved with static site generation.
Alternatives Considered
Existing boilerplates, articles and blog posts, such as those below:
https://github.com/auth0-blog/gatsby-auth0
From @rwieruch in Authentication support #1100 (comment):
This doesn't really protect the static content (@sarneeh in #1100 (comment)):
Ref ("Authentication support"): #1100
cc @simoneb @pieh @samjulien
The text was updated successfully, but these errors were encountered: