Mapping Frontegg plans to third-party features for Entitlement management
You can map Frontegg plans to features in external applications connected to Frontegg to dynamically manage entitlements.
This allows you to synchronize access control across platforms, ensuring that changes in your external application are automatically reflected in Frontegg without requiring manual updates.
In this example, we will map features in Stripe to plans in Frontegg to create a clear and consistent way to manage entitlements based on external billing or subscription events.
Unlike a plug-and-play integration, this method requires a middleware service that listens for Stripe webhook events and updates entitlements in Frontegg via API calls.
This approach is particularly useful if you use Stripe Features rather than Stripe Plans to manage access control. If you do not already group features into Stripe Plans, this is the recommended method.
Step 1: Create a plan for each Stripe feature in your application
- Open the Frontegg portal, then navigate to [ENVIRONMENT] ➜ Entitlements ➜ Plans.
- Click Create plan.
- Enter the name of the plan into the Name field using a consistent naming convention that aligns with your Stripe feature identifiers (e.g.,
feature:feature_key
). This means each plan represents a single feature that an account user can access. Each feature in Stripe should correspond to a single-feature plan in Frontegg. - Click the Review and save.
- Review the plan and click Save
Step 2: Set up a middle server
To integrate Stripe with Frontegg, you need to set up a middle server that listens for Stripe webhook events and updates Frontegg entitlements accordingly. This server will:
Authenticate with Frontegg using the API to generate a vendor token.
Listen for Stripe webhook events related to entitlements.
Use the Frontegg API to assign or remove plans based on the Stripe event data.
Required APIs
Authenticate with env credentials API to authenticate and generate a vendor token by calling
POST https://api.frontegg.com/auth/vendor/
with the following payload:{ "clientId": "YOUR_CLIENT_ID", "secret": "YOUR_SECRET" }
You can find your client ID and secret in the [ENVIRONMENT] ➜ Keys & domains section.
Batch Create Entitlements API to assign a plan to a tenant by calling
POST https://api.frontegg.com/entitlements/resources/entitlements/v2/batch-actions
usingBearer YOUR_VENDOR_TOKEN
with the following payload:{ "createActions": [ { "planId": "PLAN_ID", "tenantId": "TENANT_ID" } ], "deleteActions": [] }
Batch Create Entitlements API to remove an obsolete plan from a tenant by calling
POST https://api.frontegg.com/entitlements/resources/entitlements/v2/batch-actions
usingBearer YOUR_VENDOR_TOKEN
with the following payload:{ "createActions": [], "deleteActions": [ { "planId": "PLAN_ID", "tenantId": "TENANT_ID" } ] }
Example configuration
require('dotenv').config(); const express = require('express'); const axios = require('axios'); const bodyParser = require('body-parser'); const cors = require('cors'); const app = express(); const PORT = process.env.PORT || 5000; app.use(cors()); app.use(bodyParser.json()); const FRONTEGG_API_BASE_URL = 'https://api.frontegg.com'; const CLIENT_ID = process.env.FRONTEGG_CLIENT_ID; const SECRET = process.env.FRONTEGG_SECRET; let vendorToken = ''; const authenticateWithFrontegg = async () => { try { const response = await axios.post(`${FRONTEGG_API_BASE_URL}/auth/vendor/`, { clientId: CLIENT_ID, secret: SECRET, }, { headers: { 'Content-Type': 'application/json' }, }); vendorToken = response.data.token; console.log('Vendor token generated successfully.'); } catch (error) { console.error('Error getting vendor token:', error.message); } }; authenticateWithFrontegg(); const updateFronteggEntitlements = async (tenantId, entitlements) => { try { const assignedPlans = entitlements.map(entitlement => ({ planId: entitlement, tenantId })); const payload = { createActions: assignedPlans, deleteActions: [] }; const headers = { Authorization: `Bearer ${vendorToken}`, 'Content-Type': 'application/json', }; await axios.post( `${FRONTEGG_API_BASE_URL}/entitlements/resources/entitlements/v2/batch-actions`, payload, { headers } ); console.log('Frontegg entitlements updated successfully'); } catch (error) { console.error('Error updating entitlements:', error.message); } }; app.post('/webhook/stripe', async (req, res) => { try { const event = req.body; if (event.type === 'entitlements.active_entitlement_summary.updated') { const { tenant_id, entitlements } = event.data; console.log(`Processing entitlements update for Tenant: ${tenant_id}`); await updateFronteggEntitlements(tenant_id, entitlements); } res.status(200).json({ received: true }); } catch (error) { console.error('Error processing webhook:', error.message); res.status(500).json({ message: 'Error processing webhook' }); } }); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });