Problems with Redirect JWT role-gated pages on edge

Hi @slim, do you have a fallback redirect rule after that Role-based one? It will likely remain in that loading state due to the request being rejected with a 401 but there is no fallback redirect rule so it’s got nowhere to go. Likely if you add the following, things would work:

/rdm/* / 401!

Let me know if that works for you.

1 Like

Thanks Dennis

I’ll give that a go but would much rather the the same behaviour as the deployed site - ie 404 if not the right user or else letting access the page.

As of right now I don’t have a good conceptual model for how the redirect rules work. Some doc would help. Perhaps just the algorythm used?

Sadly not. If I do not have he role (eg not logged in) it just spins.

I’m assuming the Netlify Dev must “phone home” to check the user roles so is that what is not working? Mind you what if i’m not logged in?

my mistake - the 401 redirect is working locally. I need

/rdm/* /404.html 401!

though for my custom 404 page, so is the netlify dev not performing the same auto 404 routing that the edge does? Could it for consistency?

Sadly though I’m still not getting access when I am logged in as a user with the rdm role :frowning:

Hey @slim,
Here’s an example of a netlify.toml for the kind of use case you’re talking about:

# access auth page regardless of whether you're authenticated
[[redirects]]
	from = "/auth.html"
	to = "/auth.html"
	status = 200

# if authenticated, access 404 page
[[redirects]]
	from = "/*"
	to = "/404.html"
	status = 404
	conditions = {Role = ["admin"]}

# if authenticated, access everything
[[redirects]]
	from = "/*"
	status = 200
	force = true
	conditions = {Role = ["admin"]}

# for everything else, you're pushed to the auth page with 401
[[redirects]]
	from = "/*"
	to = "/auth.html"
	status = 401
	force = true

Our toml and redirect files are parsed and applied top to bottom, so the final rule acts as a catch-all when the first three rules don’t apply. The number of rules needed can be a bit confusing, so wanted to share in case this is helpful.

Greetings, can someone verify or refute @slim’s statement:

  1. The documentation inconsistently indicates in some places that redirection based Role gating is a Business team tier only feature. However I find it works on the following site.

From the forums and docs and pricing plans that I reviewed, it does seem that 3rd Party (External providers) JWT tokens containing a nf_jwt to define redirects by roles in netlify.toml will only work with the Business plan.

Perhaps it used to work on Pro plan, perhaps the Business plan only is a new change?

In a past blog: “If you aren’t already on a Teams Plan, sign up today to get access to this incredibly powerful feature!If you aren’t already on a Teams Plan, sign up today to get access to this incredibly powerful feature!”
Per https://www.netlify.com/blog/2019/01/31/restrict-access-to-your-sites-with-role-based-redirects/

Role-based redirects in docs
Per https://docs.netlify.com/visitor-access/role-based-access-control/#redirect-visitors-based-on-roles
Page links to the plans which shows Visitor access control (JWT auth) as only available in Business:

Please clarify so I can understand which plan I will need to start auth development on Netlify.

Thank you

@jen @Dennis in this thread

thanks

Hey @i_a,
Using Netlify Identity to gate pages based on role is available at every plan level, including the Starter level. Using third-party authentication (like Okta) to gate pages is only available at the Business plan level or above. Let us know if that answers your question or if there’s anything else we can help with.

@jen Thanks for your reply.

So External provider JWT is only available on Business Plan. Thank you for clarifying.

So to summarize, to truly protect routes via server-side redirects, either Identity or Business Plan is required. (Because front-end SPA routers are not actually secure).

Other than using Netlify Identity or selecting the Business Plan, there is no gated user authorization / secure area for a Netlify hosted JamStack site.

Thank you

Hey @i_a :wave:t2:

That is true, but do understand that Netlify Identity is itself a secure Auth provider (like Okta, Auth0, etc.) and has deep integration with Netlify’s Redirects engine and Netlify Functions (which are super powerful when combined with Identity).

That’s exactly what Netlify Identity was built to do. I’m not sure why you would phrase your statement in that way: “Other than by using the thing made to accomplish the task, we can’t accomplish the task” …well, yes.


Jon

@jonsully Thanks for your question.

We’ll if you’re developing / experimenting with an Auth system using JWT, starting out / testing at $99/month is not going to cut it.

And if you use Identity, you’re not going to be able to use JWTs.

And if you just use Identity overall, after the first 1,000 you’re pretty much locked in at $99/month. Hopefully those first 1,000 users would be paying $10/month sooner than later.

So there’s lots of pressure to create an early price-module for userland, versus creating a cool service for userland that can just gain traction slowly over time.

I’m still confused here. Identity is built on JWTs… and free to use, including Netlify’s deeply-integrated visitor access control, for up to a thousand users. It’s a great bootstrapping value proposition.


Jon

Let me clarify:

It’s a great proposition, but it also could get you locked-in. Not sure what would happen if at 1,000 users or whenever, you would like to switch to a different JWT Auth system.

Well migrating from one auth provider to another is always a tricky task… that’s a fairly large piece of architecture to swap. That said, you can migrate off of Netlify identity if you’d like to or need to. A bit of an older thread and doesn’t give all the details per se (I can if you need)…

But the point is that Identity’s API absolutely offers endpoint access to your full instance data that you could use to migrate / hydrate a different provider’s user DB with. Your users would obviously need to reset their passwords since you can’t export password data (for good reason), but I don’t think there’s a “locked in” vibe here. Nothing at Netlify aims to lock folks in and take their money :slight_smile:


Jon

Also I think every service charges money at some point :stuck_out_tongue: Auth0 looks like you can get up to 7k users for free, but do remember that all of these companies are hosting, maintaining, and paying for running cloud hardware 24/7 to keep their services up. Nothing is free :grin:

Awesome, will look into the exporting. Thanks for that link.

When you’re using FaunaDB which already has user auth, it’s kind of redundant to use another auth system.

I can generate my own JWTs (in functions) based on the authorized FaunaDB user/role, and just pass that role information on to the host, but in this case, Netlify wouldn’t do the redirects based on the JWT unless using Business Plan.

Going the Identity route, I suppose I would dynamically create users/roles in FaunaDB based on the credentials generated by Identity.

Either way, I have to write code to keep the two identity systems in sync.

That’s correct, since Netlify would need to know the signing key used in your JWTs to truly verify the role object’s contents.

For what it’s worth, I’ve done this on production sites before - not with Fauna and Identity but actually with Identity and Contentful - where I keep a synced clone of each User so that the site admins can create content bound for each user specifically. Then when the end-user uses the website and gets to the ‘my-account’ sort of page, the front-end reaches out to a Netlify Function which verifies that the user is legit, then uses some ENV keys to reach out to Contentful and grab the content for that specific user.

There’s a few moving parts but it works surprisingly well. What helps is that Netlify has event-based Functions that can kick off from Identity events (a user logging in, a user signing up, etc.) and those Functions can be configured to run as ‘background functions’. Great for when you don’t want the sync process to prevent a user from signing in, but rather you want a background job to just make sure that user’s data is in sync with your second system :slight_smile:

Anyway, all that to say too, role-gated _redirects can be …touchy. Since they’re (httpOnly) cookie-based and can’t interface directly with javascript, the interop and workflow can be difficult. I encourage folks to go with the above route instead - using Identity JWTs to hit Functions (which get special powers when your site has Identity enabled) to serve private content. I think it’s more flexible and works better than creating static content that’s just role-gated. My 2c.


Jon

Ah so you’re serving SSR html layouts via Functions, based on Identity. That’s got to be slower than the host just serving static content.

To be able to redirect say /admin to /login at the server level, before any Functions run or html is served, is what I’m looking to achieve.

Also trying to avoid the synchronization routines that you’ve described above, if possible. However, good to know that that is working well for you.

I like the Event based Functions feature and background Functions features you mentioned, good work-arounds, but I would prefer just having one source of truth.

Yes, that’s the whole point, to have httpOnly secure cookie based tokens that can’t interact with javascript, that part is supposed to be difficult, strict. That is the most secure way to prevent XSS and CSRF, pass token information only in secure cookies, in the request headers between allowed domains. Letting the standard security protocols built into the browser do their thing.

Anyhow, rethinking strategies here.

Thanks for your helpful feedback. I’m sure it will be helpful to others as well.

1 Like

Ehhh the Function just proxies Contentful for security and returns JSON that React then hydrates into the DOM with various markup. Slower than a pure SSR just sending back already-marked-up HTML for that particular user? Yeah possibly, but I don’t ever have to worry about Netlify Identity scaling, Netlify Functions scaling, or Contentful scaling :stuck_out_tongue: They all also run really fast, so that entire interaction is rarely more than 300-400ms.

If the goal is to redirect before attempting to call an Authorized Function, it doesn’t matter if it happens at the server level or as part of client-side routing. The point is to push the user to authenticate before attempting to make an Authorized call. Doesn’t much matter if that comes in the form of a 302 from the server or client-side routing in a PWA just pushing the user back to the <Login> component if they’re not logged in when attempting to open a page that requires auth. (Heck, just don’t even render the button to open that page if they’re not logged in :stuck_out_tongue: )

I get that. I’ve definitely made the active choice to consider Identity the ‘source of truth’ and Contentful the clone-target in this case. Netlify just has so many connecting features when using Identity, Functions, Redirects, etc. that it doesn’t make sense to try to make my own piece-meal integration with Auth0 or Okta, etc. I’m okay with having Identity be the source of truth and it’s served me well thus far.

Ehh. Yes and no. In the JAMstack world, cookies aren’t the first-class citizen they used to be in a single-server user-to-server-back-and-forth model. Storing JWTs in localstorage exclusively also isn’t considered a security threat if you’re following some guidelines and enforcing secure connections / secure scripts. There are good discussions to be had there (security is worth it, always), but JWTs have been rocking the web world outside of httpOnly specific-domain cookies for a while :slight_smile:


Jon

Not really. Depends on webpack configuration and framework, but essentially, all affordances are in the app’s js files, and all routes to dynamically loaded components as well. Sure it may be an undertaking, but it’s possible to sift out information and see the unauthorized content in the bundles. Something to be careful with and something that blocking routes server-side can eliminate, vs just javascript routing to a different component in the PWA/SPA.

For instance, this example https://login-to-gated-site.netlify.app/ from this post: https://www.netlify.com/blog/2019/01/31/restrict-access-to-your-sites-with-role-based-redirects/

The supposedly protected text content “Congratulations, you have provided the appropriate JWT authentication” … well searching in the app.js file, you can find that text and also the path to the protected route without having to create the JWT.

Yes correct, the SSL/TLS connection is the principle measure.