Closed sedubois closed 7 years ago
These are two separate points in the dev/deploy cycle. The 1.0 (that uses graphql) branch has a way to make plugins so support for graphql can be added there. Someone is working on a Contentful plugin for example. The plugin will need to authenticate to grab any of the content that requires, both during develop and once you build the site (ie just before deploy).
The consideration here is anything on the client side they can technically see. So if you have a paywall on your site, you would need a server to deliver that content as you don't want to build all of that content into your main site. You could technically make it work with ajax and services such as auth0.
His comment is directed more at the pure web app. The power behind gatsby is to quickly build a group of files that you can drop anywhere, and are public. It is perfect for marketing sites, blogs, etc. where all of the content is public. 1.0 brings it a little closer to the web app end of the spectrum, and Everything as a Service brings the web app more towards the static content end of the spectrum. The big kicker with a web app is when it makes sense to control your webpack, react, redux, etc. during your development. The closer to the web app end of the spectrum, the more that makes sense.
Hopefully that helped!
Thanks for the details! I discovered the site https://meetfabric.com (was linked somewhere here) which is made with Gatsby 1 and has authentication. How was it done and, especially, with which platform is it deployed if I may ask? I'd be quite interested. Does Netlify allow that kind of system?
As long as authenticated pages are client only then authentication with Gatsby is easy — just don't let unauthenticated people send requests to your API. For non-authenticated people then pages needing info from your API will show up as blank. You can also add client side checks if the user is authenticated and redirect people away from those pages.
If you want to authenticate access to static HTML pages Gatsby generates it's a bit trickier as then you need to check if a user is authenticated or not on the server.
Thanks @KyleAMathews for taking the time to reply. I wish to have SSR or pre-rendering also for authenticated pages for better performance (the client should receive the proper HTML right away). How is it done in meetfabric.com, are the authenticated pages "client only", or are they pre-rendered, or is there a dedicated server which does SSR? (NB: I can't check myself because my phone number required for authentication isn't recognised.)
So it's still unclear to me how to have such pre-rendered pages only going to the client if they are logged in (i.e. not leaked in the client code if they're not authenticated).
I guess that this is one of the main questions which could hold people back from using such a framework and I think it would help to have an example of how to do so. Do you plan to share that? Of course I understand if some things are not possible, it would just be great to understand what is possible and what isn't.
Thanks so much for the excellent work and looking forward to the v1 release :)
Server rendering authenticated pages isn't as important as you have to login first so you'll already have JS code loaded by the time you hit a logged-in page.
If you really want to serve static HTML only to authenticated users, then you just need to authenticate people on the server as well with cookies.
That's true, but the user only has to see the login page once. Afterwards when returning every day, their auth info is already known and they'll hit an authenticated page directly.
So yes, I need to authenticate users on the server, but I have no idea how to do this whole flow with Gatsby. A minimal example would be wonderful :) Or at least documentation with your recommendations on how to handle the common need of rendering user-authenticated pages.
@sedubois I think this is out of Gatsby scope. It depends on the server you choose to serve your static gatsby files. For example you could look into Node.js Express examples. I'm sure there are plenty tutorials about authorising a request.
When you serve the files with your own server, you have full control over what to do on each request. So for specific (secured) routes, you check the cookie / authenticate the user, and then choose to serve either the Gatsby generated file, or redirect to a url with a login page.
If you use a CDN like Netlify, I guess you have to make sure that specific urls are directed to your server instead of serving files directly from the CDN cache.
@sedubois You can start here reactjs authentication 1 reactjsauthentication 2
Once you catch the concept it won't be hard to recreate the workflow with Gatsby.
Thanks. I know more or less how authentication works. I got something working with Next.js in this demo (and developing it further in another project): https://github.com/relatenow/relate
My question was whether there could be a demo/explanation of Gatsby + auth which preserves proper CDN world coverage. In my mind, the main advantage of static pages is that it's one click to deploy on Netlify etc, but that's not as simple when auth is involved, which is why further documentation would have been awesome.
Anyway hopefully I'll investigate further 🙂
@0x80 said: If you use a CDN like Netlify, I guess you have to make sure that specific urls are directed to your server instead of serving files directly from the CDN cache.
Just wanted to add that Netlify appears to have a purpose-built system in place for visitor access control, either by means of password-protected content or user authentication as already described here.
https://www.netlify.com/docs/visitor-access-control/
This stuff is only doable on the paid plan however, so in response to
@sedubois said: My question was whether there could be a demo/explanation of Gatsby + auth which preserves proper CDN world coverage
Maybe @biilmann & co would be interested in setting up a demo page for this functionality using Gatsby? It would be pretty good advertisement. I'm also interested in this type of functionality, and if Netlify can make it super easy, I'd happily pay up.
Yes I got a reply from Netlify support on July 21st who mentioned there's indeed now a JWT role-based access control which allows to do more things, but very unfortunately only at the Gold pricing tier.
Apparently the authentication is cached at the CDN edge, avoiding re-authenticating at origin for every request.
Unclear how much more documentation/announcements/demos are on the way regarding this, so it's worth contacting them to notify your interest if any. Considering they just got $12 M of extra funding, I suppose that more capacity could be coming to properly communicate this.
I'm still puzzled by the Gold pricing tier. How many websites can function without any access control?
@sedubois did you get anywhere with this? I'm specing out tech for a project and would like to do something similar.
@erichodges I haven’t progressed much. Been migrating the static marketing pages which is a start! https://imagineclarity.com
I plan on adding auth when all those are done. I’m thinking to simply go for client-side authentication and loading the content asynchronously. I still need to figure out how to keep a live GraphQL client in my code even after the site has been built by Gatsby.
Any ideas on your side?
@sedubois Your site looks good! I just starting looking at this static approach, which seems really cool. It's weird that the auth piece does not seem to be well worked out yet. I sent email today to the Auth0 people asking about working with Gatsby. Trying to find all the pieces needed to build a basic site with auth and a shopping cart. I like the super fast load times, of course.
I'm a long time meditator, so it's nice to see a site dedicated to that!
@sedubois Ok, I put a question to their community, I'll let you know if anything interesting comes up. Thanks for the invite to ping! I will do that...cheers.
Here's a comment on my question via the Auth0 community:
I confess that I don't have practical experience with Gatsby, but from what I understood from reading the documentation any integration with an authentication service (Auth0 or other) would be mostly independent of Gatsby itself. In other words, after having used it to generate the static content the authentication requirement that you would want to apply to a subset of that static content would depend only on the server-side technology stack that you would decide to use to serve up that content.
For example, going with Node.js and an Express server you would have configure Express to require authentication on the routes associated with the protected static content. This would then cause anonymous users to be redirected to a login page where they could authenticate. Upon successful authentication the Node.js server would receive a token/assertion proving that the user authenticated and using this token/assertion it could start a server-side session based on cookies. Subsequent request would be allowed due to the cookie.
In conclusion, you may want to take Gatsby out of the equation when researching the authentication approach and instead focus on the server-side technology that would be serving the protected content.
@erichodges I think that answer is just on point. I forgot to mention, but one way to preserve the benefits of a CDN without requiring Netlify’s expensive ($290) Business tier should be CloudFlare Workers, but it’s still in beta (and I still didn’t get access).
Otherwise yes, deploying a Node server but I don’t know of any affordable PaaS which deploys Node servers on a proper global CDN (as of this writing, and as far as I remember), Zeit Now deploys on only something like 3 locations (or maybe 5, but more to come) and is not free (i can’t find the doc on the instance locations, it was in their Zeit Day Berlin talk). Using a « CDN » of Node servers also adds complexity that I would be happy to avoid.
I still have some work ahead of me and, as per the above, a nice solution might just be around the corner, so I’ll start with a static CDN + client-side auth and take it from there.
@sedubois Yeah, this is what I was wondering. I don't understand what is so expensive about hosting Node etc. on a CDN. Interesting about the complexity, I have no experience with that. Static + Auth would be pretty good. I'm now looking at just using regular React on Firebase. They have dev auth for free and the next tier is $25. I'm not far into it though.
I'd definitely look into Graphcool as alternative to Firebase, combined with Apollo 2.0. Comparison from a few months ago.
@sedubois Ah, ok cool, I will check it out, thank you.
I implemented a quick MVP this morning to checkout a whole Firebase authentication flow in Gatsby. Turns out it works. In case anyone comes across this thread :)
Edit: It works and got an update to Gatsby 2.0 recently.
Take a look here, a react app hosted on AWS S3 and doing client side authentication using AWS services. It uses AWS lambda, Cognito and Dynamodb. I thought it was pretty cool.
@rwieruch this is a great starter, thank you for that.
@Jaikant But this has nothing to do with Gatsby, because it's just a client-side rendered React app. Gatsby is all about hosting static files - in this case you don't want to render anything at client-side because you lose all the benefits that static website hosting gives us.
@rwieruch Alright, but I think this is not how you should block website content. In your case you just have client-side authentication logic on your page - anyone with a link to your authorised content will still be able to get it (because it's just a static file somewhere on your host). To make it reliable you still need to authorise the user on the server which serves the content.
I may miss something because I'm fresh in static site generators, so correct me if I'm wrong but I think my arguments are valid :)
@sedubois, @erlend-sh Bunch of other issues occurs with Netlify JWT based access control feature. We have activated the business plan especially to use JWT based access control. The relationship wit Netlify and Gatsby are very tricky. Even if role-based access control works great with just static HTML pages, it is not working properly when we came across with Gatsby's generated pages. That pages are pulling the content based on the URL, which means when we try and proxy different content based on the redirect rules, it still renders the content based on the path in the URL. In Gatsby, all contents loading based on URL. I assume we should somehow find a solution to disable prefetching/preloading. Here is some discussion about that.
@sedubois It looks like you got auth working on your site. Did you end up using Firebase alongside Gatsby?
@m2mathew the Gatsby landing page just points to a separate web app when clicking login, so the auth had nothing to do with Gatsby right now. I still need to figure out how to integrate the auth part but there was some promising info in this thread:
https://spectrum.chat/gatsby-js?thread=6e346606-f660-42ee-86b0-2a585471c20a
I saw @KyleAMathews give a talk here in Dallas last night. He pulled up this package as a demo of including some client-only routes behind authentication. It might be helpful to some of the goals of this discussion. Much better solution than how I was trying to approach it. I am implementing these ideas in my Gatsby web app now! 😎
Ok @m2mathew, this demo is interesting. It stores the JWT in local storage and I would like to store the JWT in a secure cookie. I have not found an example of this yet, but I'm on the lookout. Thanks for posting this though. I have it running but I have not looked through all the code yet.
This demo mentioned by @m2mathew has been added to the examples section:
https://github.com/gatsbyjs/gatsby/tree/master/examples/simple-auth
Sorry to revamp this old issue but server side authentication in Gatsby is still something that's not easy to solve, if possible at all!
I am familiar with authentication and gatsby client only routes, but that still doesn't solve the problem. Let's say that I generate the gatsby application to be hosted on AWS Cloudfront and I want the /login page alone to be accessible by unauthenticated users. What I would need to do is for instance use cookies to carry auth information around and check that on the server (i.e. lambda@edge).
The problem is, how do I know which requests to let through and which ones to block (i.e. redirect to /login) when the request is unauthenticated? I would need to let through the request to the /login page but also all other assets that it requires. Where I'm stuck at is, gatsby generates those assets at paths that can't be predicted, so it's not possible to know whether an asset being loaded belongs to the login page (hence doesn't contain any protected information) or to any other page.
The only solution I'm thinking of is to simply not use gastby for the login page, or in other words to create two different applications: one for the login page, which is public, and one for the rest of the site, which is protected. But that doesn't make it very convenient to use gatsby in the first place. Ideally I would use exactly the same gatsby configuration and simply have two different subsections of the application in order to achieve a predictable file system structure, which will allow applying path-based authentication rules, but gastby doesn't make this scenario easy either.
Ideas?
@rwieruch Alright, but I think this is not how you should block website content. In your case you just have client-side authentication logic on your page - anyone with a link to your authorised content will still be able to get it (because it's just a static file somewhere on your host). To make it reliable you still need to authorise the user on the server which serves the content.
@sarneeh What about having everything that should stay protected (e.g. authenticated user) still served as static pages, but its sensible content is loaded dynamically afterward? Then, in the case of Firebase, you could handle it the same as a client-side application with Firebase Security Rules.
Everything that's behind a login shouldn't be relevant for SEO after all, so it would be fine to load the content dynamically, shouldn't it?
Ideas?
I have dynamic routes in my Gatsby app too:
const PrivateRoute = ({ component: Component, location, ...rest }) => {
if (!auth.isLoggedIn()) {
return null;
}
return (<Component {...rest} />);
}
const App = () => (
<Router>
<PrivateRoute path="/app" component={PrivateApp} />
<LoginPage path="/login" />
<NotFoundPage default />
</Router>
);
I also have auth status change listeners which navigate to appropriate routes on a user's auth status change. You should not store any secrets client side. Dynamic Gatsby routes will be served to the client when he navigates to them even unauthenticated, but as @rwieruch suggested for these pages you should load data dynamically after user login.
NB: loading dynamically client-side requires Javascript to work, so this approach has its own limitations (depending on your audience).
@sarneeh What about having everything that should stay protected (e.g. authenticated user) still served as static pages, but its sensible content is loaded dynamically afterward? Then, in the case of Firebase, you could handle it the same as a client-side application with Firebase Security Rules.
Everything that's behind a login shouldn't be relevant for SEO after all, so it would be fine to load the content dynamically, shouldn't it?
Yeah! I think that would work. The only downside here is that you lose the speed of a static site but as you've already mentioned: SEO is not relevant on the private pages so you don't have to be instant for web crawlers.
If I'm using gatsby to build a side whose content is exclusively protected, except for the login pages, and there is no solution to protect it except for loading data dynamically using client only routes - which would hence be used to serve the entire application - why would you use gatsby in the first place then? Isn't it just a normal React application? Instead, I would like gatsby to be able to provide a means to protect content server-side, so I can still use it to generate a static website whose content is mostly protected.
If I'm using gatsby to build a side whose content is exclusively protected, except for the login pages, and there is no solution to protect it except for loading data dynamically using client only routes - which would hence be used to serve the entire application - why would you use gatsby in the first place then? Isn't it just a normal React application? Instead, I would like gatsby to be able to provide a means to protect content server-side, so I can still use it to generate a static website whose content is mostly protected.
Gatsby is a static-site generator built on the basis of the JAM Stack architecture. It gives you the J (Javascript) and M (Markup). Any server-side logic is based on your A (API). Gatsby is not a server-side framework and does not provide you any options to build server logic. That's not how JAMStacks work.
And if you don't know why would you use Gatsby as it doesn't provide you any server-side authorization options then I guess you need to read a little bit more about it :smile:
If you want a framework to build fast server-side apps, go check out Next.js.
@sarneeh ego check please.
I know quite well what Gatsby provides and I'm well aware that it doesn't provide any server side authorization logic, which I mentioned explicitly I am carrying out using AWS lambda@edge. What I'm pointing out is that Gatsby doesn't make it easy, or even possible, to protect certain parts of the application because generated assets paths are not deterministic. This leaves you with the only option of using client only routes, which, if your application is exclusively private, pretty much defeats the purpose of using Gatsby in the first place.
@simoneb Sorry, didn't mean to be rude.
If your app is exclusively private then there are still a few cool benefits you could get from Gatsby. Check out this blog post about Gatsby for Apps. If these features aren't super important to you then yes, probably there's no purpose to use Gatsby in your use case.
Additionally, I guess that Gatsby's asset paths are not deterministic for the sake of caching, but I agree that it would be cool to be able to just spit out some custom-named html+js page and handle caching stuff manually.
@simoneb If gatsby would generate asset manifest on builds detailing what assets are used for given urls/pathnames - would that help? Or do you need to statically define those protected routes for your setup?
I.e.
"/very-secret-page": [
// that's result of page queries
"/static/d/123/path---very-secret-123-123456789abcd.json",
// that's react component / page template code splitted chunk
"/component---src-templates-secret-page-js-123456789abcd.js"
]
We are also working on changing how page queries are generated and loaded, which actually will be deterministic ( https://github.com/gatsbyjs/gatsby/issues/11982 ). So page results will end up in file that is sibling to index.html
for given page - so that will become deterministic ( another ref: https://github.com/gatsbyjs/gatsby/pull/12891#issuecomment-477364348 )
@sarneeh, it's alright, no worries.
Hi @pieh, to answer your question let me take a slightly longer route. The scenario is very simple.
The app is totally static and I would benefit a lot from it being static, hence using Gatsby.
The app is also completely private except for the login page, which unauthenticated users need to be able to access in order to authenticate.
I'm hosting it on AWS and using lambda@edge to restrict access to the app. Hence, I'm faced with the question: "how do I allow access to the login page?". That would be relatively simple if the login page was only /login/index.html
, but as we know it isn't, because it's importing other assets (.js, .json, ...), whose paths are non-deterministic, hence I can't encode in the lambda@edge code the logic which either allows or denies requests.
So, on to your question, I guess that both solutions would work, with the second (co-located assets) being the simplest to use in my scenario.
Also, thanks for those links, I hadn't come across those issues when searching for a solution to this problem.
Those links aren't exactly addressing non deterministic asset locations - this is side-effect of approach we are trying, so not really discoverable when searching for that.
Also the issue/PR I linked to is only related to query results. JS chunks will be unchanged - this is separate thing.
This is long project, so if you are interested in hacky way - this is doable right now to read some internals (which is prone for breaking, because we can and most likely will change those internals). You can check https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/cache-dir/static-entry.js file which is file used to generate .html
files where we do need to get urls for assets:
@pieh , that's useful, thanks. How shall we track this feature request then? Either a manifest or co-located assets would be really great to have.
Setting up a Node.js server to properly secure static Gatsby content is also interesting to me. Server-side authentication and doing user-level + page-level access control on static content (no client-only routes) is an additional requirement for my project - and I think that if it was more visible / easy then a lot more people would be doing this too.
I have a hacked-together solution (with regular expressions 😵) - documented at the issue below, but it's not great for scalability.
@pieh I really like what your comment here could open up:
@simoneb If gatsby would generate asset manifest on builds detailing what assets are used for given urls/pathnames - would that help?
I've opened a request to track and increase visibility on this:
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 #20745 hopes to get a better solution for!
So glad to have found this discussion. There's nothing out there, in the docs or blog articles, even discussing this need. Strange.
@pieh wrote:
So page results will end up in file that is sibling to
index.html
for given page - so that will become deterministic ( another ref: #12891 (comment) )
I'm more interested in the above solution: grouping of all assets next to the index.html. The reason this solution works better for me than the creation of an assset manifest is that I want to implement the backend authorization check using Apache's [mod_auth_opendic](https://github.com/zmartzone/mod_auth_openidc) module and, with Apache, auth scopes can be done simply with paths. In my case, all sensitive static content would be under a
priv/` directory, which would easy to configure with Apache.
@pieh what's the current status of the effort of bundling assets together? It looks like since your message, at least the JSON is now situated under page-data/..../page-data.json
, which would solve my biggest roadblock. But what about embedded images, for example?
Server rendering authenticated pages isn't as important as you have to login first so you'll already have JS code loaded by the time you hit a logged-in page.
@KyleAMathews @pieh is this still true given that Gatsby v4 has SSR with getServerData
? Will the Gatsby team write a guide about serving SSR data after authentication?
If this is a use case that should be supported, should this issue be reopened? (or a new issue created?)
@karlhorky late here but an issue for this would be great
Hi! Gatsby looks super interesting, I first heard about it due to its support of GraphQL. (Any plans to demo integration with Graphcool?)
But I'm not familiar at all with static sites (both to generate them and to deploy them). My main concern is, how are authenticated pages handled if there is no custom server? Is it possible at all and is there an example? The readme states "Proper web apps should probably remain as normal web apps", but I'm not quite sure why that is. It sounds like static sites allow for the best performance (and easy worldwide deployment), so should be used if at all possible. Any clarification would be very appreciated.
My use-case is data which is stored behind a GraphQL API and does not change too frequently, however some pages do require the user to be logged in and to have a subscription. (Later, there might be more dynamic content.)
Thanks so much and keep up the good work!