Reputation: 1844
Coming from Umbraco where we have the concept of a User/UserGroup (backoffice) and a Member/MemberGroup (front facing part of website), I'm finding it a bit hard to figure out how to protect content in the front facing part of your website with a headless CMS such as Contentful that has no concept of Members nor any natively built in handling of that for the content on the front facing side.
In Umbraco you'd just create your Members, assign them to MemberGroups and then give those MemberGroups public access to certain content. Then this piece of content will no longer be publicly available, but only to registered Members with the specific MemberGroups.
Is there some resource - article or video - you can link me to, that describes how to restrict access to certain pages or pieces of content on the front facing part of your website with Contentful or similar headless CMS without native members built in?
Let's say I need to make a very simple React Single Page Application (SPA) that has:
This is trivial in many CMS and even headless CMS such as Umbraco Heartcore that has built in support for native members and membergroups/roles/permissions.
In Sitecore it's just about adding the right domain "extranet" to the user.
But how to achieve this simple task with contentful or other headless CMS who only handles roles/permissions for managing content, but exposes all content through non-protected content delivery api's without the possibility of restricting specific content?
One solution I could see is if there were some kind of system that subscribed to the content delivery api, and then this system would be used to map a user profile, role/group to the content. But such a system I would not find.
Update: Bonus if you can also tell me how I can make an intranet like site, like you can do with many other CMS that handles frontend access to content.
Upvotes: 3
Views: 1523
Reputation: 3999
The question here is how do you want to setup your solution design for this case, you obviously have multiple ways to approach this. Let me outline a few options I see often applied to business cases.
Please note that Option 1 & 2 have a contextual purpose in the walkthrough.
I should note now that the Enterprise Edition of Contentful has the option to create custom roles and permissions. You would i.e. be able to restrict or allow read rights for specific content spaces.
But hey, as we're all custodians of our sandbox billings, let's keep talking!
Let me start with the following though:
Headless CMS provider Contentful and similar headless CMS providers are exposing content data via their content delivery api's (CDAs). This means that your content can be fetched by a client with a valid access token.
Contentful has implemented the Oauth2.0 authorization protocol. You can follow the documentation to authorize your (machine) client and implement authentication for the CDA.
The first option would be a solution design that doesn't implement any user management or role based approach and just makes contact with the CDA by providing a valid access token that belongs to a specific environment in a space and fetching the content.
As you can imagine this wouldn't be a preferable option as we have to share the access token in the client. In case of a Single Page Application (SPA) any person can obtain this token and abuse your CDA.
Another option would be to use a form of isomorphic authorization. This means that the SPA determines the user role based on the provided user attributes. Then SPA renders it's web components based on the role.
In this case you will obviously need some form of user management service to keep track of your users and (role) attributes.
One of my favorite user directory management services for simple SPAs is AWS Cognito which uses the AWS Amplify framework to use the Cognito based authentication in your React app. Want to know how to set this up? Check out this cool tutorial.
Okay, now you have implemented user management in you SPA. Now you need to make sure that your users are getting the right content based on their role. This is where an isomorphic authorization package like CASL enters the ring.
CASL implements role based component access which ensures that our user will only be able to view the components that he/she is allowed to see. Within these components you can fetch specific content. See also the CASL React documentation.
Now you can use two approaches for providing the access token:
But in both cases, within a SPA, your user will be able to retrieve the access token if he/she is a bit tech savvy and will be able to abuse your API. That still doesn't feel very comfortable!
Still there? Let's take a look at the big guns for a more secure approach.
In this case you are going to use user request validation to make sure a specific user can only see the content that he/she is allowed to see and won't be able to abuse your CDA.
First you need to make sure to setup a user management service to authenticate your users in your web application. Again, AWS Cognito may be a good fit! But there are more services out there, like Auth0 for example.
Then you need to setup your own API Gateway with which your web application's client will be interacting instead of the CDA. On this API Gateway you can implement request validation to verify that the user is allowed to make a specific API request.
Here is a tutorial to implement this workflow for an AWS API Gateway + AWS Cognito setup. When a client logs in on your frontend web application it receives a token. This toke is provided in the request header for every API call the client makes to your API Gateway. The validation service checks for every request if the user can make this request.
When the request is validated, the API Gateway proxies the request to an event handler. This event handler fetches the user attributes from the request and checks if the user is allowed to request a specific content asset based on a mapping between it's user attributes (i.e. role) and a specific content environment that is configured for this attribute (i.e role).
The event handler then requests the specific content asset by interacting with the CDA and returning this to the client.
For implementing the event handler I would advise to use AWS Lambda, as it integrates seamlessly with AWS API Gateway.
That's it, now you have implemented a secure way to serve role based content.
You can optimize your implementation by combining Option 2 & Option 3, in my experience such an implementation does a really good job in role based application component serving.
May you opt in to integrate the Contentful Enterprise edition, you can even use embargoed assets to further restrict public access to the content by signing the asset URLs.
And if you want a cheaper way of using such cool features of signed asset URLs, high availability and other cool Content Delivery Network features; take a look at AWS CloudFront as your content management system instead of Contentful.
Bonus: Do you want to start with a tutorial to build a complete intranet with some of the techniques as described above? Check out this tutorial to make a start.
As per the discussion, you could also create an AWS only solution where you can leverage various powerful resource integrations.
In this case you can use AWS Amplify as your deployment provider, see this tutorial to see how to set up a React web application with Amplify and CloudFront.
As discussed earlier you can then leverage AWS Cognito for user management and validate requests to your API Gateway with a Lambda authorizer.
You can proxy your requests to a Lambda event handler and request private content either from S3 or from CloudFront. I recommend to use a signed URL setup, you can check the differences between S3 and CloudFront signed URLs here.
Upvotes: 4