Zuplo is an API gateway platform that can use external policy engines to make traffic routing decisions. This article describes how you can integrate Zuplo with the Rock Solid Knowledge Enforcer ABAC engine.
Zuplo is a lightweight, fully managed API management platform designed for developers. It provides a programmable and cost-effective alternative to traditional API management solutions.
Zuplo allows you to add policies, which are modular, executable components that intercept and modify API requests and responses as they flow through the API gateway. They act as "middleware" for your API traffic.
In this blog post, we will demonstrate how you can use Enforcer to create a Policy Decision Point (PDP) and add an AuthZen policy to Zuplo that will call this PDP to authorize requests.
This blog post assumes you have some basic knowledge of the Enforcer product and AuthZen. If not, you can watch Andrew Clymer’s AuthZen video to get an introduction. This blog post also assumes a basic working knowledge of Zuplo.
Creating a Zuplo Todo List API with API Key Authentication
For this blog post, we will require a basic API in Zuplo using their Todo List API example. To follow along, create a new API in Zuplo and select the “Todo List” option.

We will also need to add API Key authorization. I will not go into too much detail with this, but Zuplo has great documentation as part of their Getting Started tutorial that walks you through adding API Key Authentication.
I followed their tutorial and added the API Key Authentication Policy to all of the endpoints of my Todo List API.

I also created two separate consumers in their API Key Service.

I can confirm that the API Key Authentication Policy is working by testing one of the API endpoints. When calling the endpoint without an authorization header, Zuplo returns an HTTP 401 (Unauthorized) response.

Using the API key from one of the previously created consumers, the API endpoint returns the list of todos.

Create a Basic PDP
With the Zuplo API created, let’s focus on the PDP. I created an empty ASP.NET Core project and installed the Rsk.Enforcer.AuthZen NuGet package.
Then, I updated my startup code by adding the following code:
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddLogging(b => { b.AddConsole(); })
.AddEnforcer("AcmeCorp.global",
o =>
{
o.Licensee = "DEMO";
o.LicenseKey = "...";
})
.AddPolicyEnforcementPoint(o => o.Bias = PepBias.Deny)
.AddEmbeddedPolicyStore("ZuploEnforcerPdp.Policies");
var app = builder.Build();
app.UseEnforcerAuthZen();
app.Run();
- The call to
AddEnforcer
adds the various Enforcer services. You can obtain an Enforcer demo license from here AddPolicyEnforcementPoint
adds and configures the Policy Enforcement Point (PEP) for Enforcer.AddEmbeddedPolicyStore
Informs Enforcer that the policies must be loaded as embedded resources and will be prefixed withZuploEnforcerPdp".Policies
(in other words, these embedded resources can be found in the application’s/Policies
directory. (We’ll look at the policy below).- Finally, we register the AuthZen middleware by calling the
app.UseEnforcerAuthZen();
As mentioned above, I am using the embedded resource policy store. I added an ALFA policy document to the /Policies
directory and set its build action as an embedded resource.

For now, the ALFA policy document looks as follows.
namespace AcmeCorp {
import Oasis.Functions.*
import Oasis.Attributes.*
policyset global {
apply firstApplicable
policy routeAccess
}
condition AccessRoute ResourceType == "route"
policy routeAccess {
target clause AccessRoute
apply denyUnlessPermit
}
}
I can ensure that everything is hooked up correctly by running the project and sending an empty payload to the evaluation endpoint:
POST http://localhost:5217/access/v1/evaluation
Content-Type: application/json
{
}
The PDP responds as follows:
HTTP/1.1 200 OK
{
"decision" : false
}
The response indicates that the authorization request is not allowed, which is expected since a) our policy document does not contain any rules yet, and b) the request payload did not contain information on which the PDP can base a decision.
Once again, I suggest you watch Andrew Clymer’s AuthZen video to understand how the requests should be made.
Exposing our PDP to the Internet
Before we switch back to Zuplo, I’d like to expose the PDP to the internet. Currently, we can make requests to the evaluation endpoint on localhost; however, once we add the AuthZen Policy to Zuplo, this policy must be able to make requests to the PDP running on my local machine.
I am using ngrok and running the following command:
ngrok http --host-header=rewrite localhost:5217
This will start the ngrok tunnel and indicate the public URL I can use to access my PDP.

Use of ngrok
In a production environment, your PDP will be hosted on public infrastructure, so this is just needed during development when I want to allow Zuplo to make requests to the PDP running on my development machine.
Add AuthZen Policy in Zuplo
With the PDP in place, let’s add the AuthZen Policy in Zuplo. Navigate to your list of policies, and create a new inbound policy.

Add a new “AuthZen Authorization” policy. You can leave the default configuration, but be sure to update the authorizerHostname
with the URL of the PDP - in our case, the public URL generated by ngrok.

Once the policy is created, we can apply it to all the operations in the Todo List API.

Next, we can switch to the Route Designer and ensure that the AuthZen policy executes after the API Key policy but before any other policies, such as input validation policies.

Finally, we can test the “Get all todos” endpoint. We’ll get an HTTP status 403 (Forbidden) response, which is expected, as our policy document does not yet contain any rules.

Add a read-access Rule
Let’s add a rule to the policy document allowing read access for all users.
However, before we do that, let’s revisit the AuthZen Policy configuration in Zuplo. You will notice that the default policy that was created bound the request method to the action attribute.

If we look at the previously denied request sent from Zuplo to our PDP in the ngrok inspector, we will notice the presence of the action attribute, which contains the HTTP method.

With this in mind, let’s update the ALFA policy document to add a hasReadAccess
rule for all users. This rule will allow all requests where the Action (i.e. the HTTP verb) is “GET”.
namespace AcmeCorp {
import Oasis.Functions.*
import Oasis.Attributes.*
policyset global {
apply firstApplicable
policy routeAccess
}
condition AccessRoute ResourceType == "route"
policy routeAccess {
target clause AccessRoute
apply denyUnlessPermit
rule hasReadAccess {
target clause Action == "GET"
permit
}
}
}
When we restart the PDP and execute the request to list all todos again, the authorization succeeds, and Zuplo returns the list of todos.

Add a Write Access Rule for Managers
Next, we want to enable managers to have write access. In other words, managers must be able to execute any of the endpoints that use the POST, PATCH, or DELETE HTTP verbs.
For this, we can update the policy document as follows:
namespace AcmeCorp {
import Oasis.Functions.*
import Oasis.Attributes.*
policyset global {
apply firstApplicable
policy routeAccess
}
condition IsManager Subject.Role == "manager"
condition AccessRoute ResourceType == "route"
policy routeAccess {
target clause AccessRoute
apply denyUnlessPermit
rule hasReadAccess {
target clause Action == "GET"
permit
}
rule hasWriteAccess {
target clause Action == "POST" || Action == "PATCH" || Action == "DELETE"
permit
condition IsManager
}
}
}
- We added an
IsManager
condition to check whether a user is a manager based on their role. - We added a
hasWriteAccess
rule that permits managers to perform “POST”, “PATCH”, or “DELETE” actions.
Next, we need a way to determine whether a user has the “manager” role. There are two approaches we can take to do this:
- We can set the roles in Zuplo using additional user metadata in the API Key Service and attach it to the request made to the PDP.
- We can let our PDP resolve the user roles based on the user ID.
I will demonstrate both these approaches.
Attach User Roles via Zuplo
The first thing I am going to do is to add the manager role for one of the users I created earlier. The second user will not have the manager role.

When configuring the AuthZen Policy earlier, we used the default settings to set the user
attribute using the special $authzen-prop
function. Unfortunately, as indicated in the AuthZen Authorization Policy documentation, this function only works for setting the id
and name
properties.
To attach the roles as custom properties to the user
attribute, we must do so in code. For this, we can create a custom code inbound policy and use the setAuthorizationContext
method to set the user attribute.
The code for this policy attaches the roles from the metadata to the properties of the user attribute.
import {AuthZenInboundPolicy, ZuploContext, ZuploRequest} from "@zuplo/runtime";
type MyPolicyOptionsType = {
myOption: any;
};
export default async function policy(
request: ZuploRequest,
context: ZuploContext,
options: MyPolicyOptionsType,
policyName: string
) {
AuthZenInboundPolicy.setAuthorizationPayload(context, {
subject: {
type: "user",
id: request.user.sub,
properties: {
role: request.user.data.role
}
}
});
return request;
}
Next, we need to wire up this policy on all our routes and ensure that it executes before the AuthZen Authorization Policy.

With this configured, we can confirm that the new policy works correctly by making a request with the API key of the user who does not have the manager role.
As expected, we get an HTTP Status 403 (Forbidden).

If we make the same request with the API key of the user with the manager role, the request succeeds.

Resolve User Roles Inside the PDP
The other approach is to assign the correct user roles to each user inside our PDP. For this, we can create a Custom AttributeValueProvider, allowing us to return additional attributes to the PDP.
First, let’s create a record containing the return result, which will be the roles for the subject
.
public class SubjectRecord
{
[PolicyAttributeValue(PolicyAttributeCategories.Subject, "role")]
public string[] Roles { get; init; } = [];
}
Then, we can add the implementation for the record attribute provider. For this sample, the provider will return a hard-coded set of roles for each user we previously created in the API Key Service in Zuplo. In a production application, this data will most likely come from a database or another data source.
public class SubjectRecordProvider : RecordAttributeValueProvider<SubjectRecord>
{
private static readonly PolicyAttribute SubjectIdentifier =
new PolicyAttribute("id", PolicyValueType.String, PolicyAttributeCategories.Subject);
private static readonly SubjectRecord Anonymous = new SubjectRecord();
private static readonly Dictionary<string, SubjectRecord> SubjectRecordMap = new Dictionary<string, SubjectRecord>
{
["jerrie-pelser"] = new SubjectRecord { Roles = ["manager"] },
["peter-parker"] = new SubjectRecord { Roles = [] }
};
protected override async Task<SubjectRecord> GetRecordValue(IAttributeResolver attributeResolver, CancellationToken ct)
{
var id = (await attributeResolver.Resolve<string>(SubjectIdentifier, ct))
.Single();
if (!SubjectRecordMap.TryGetValue(id, out var subject)) return Anonymous;
return subject;
}
}
The final bit is to tell Enforcer about the custom Policy Attribute Provider by adding a call to AddPolicyAttributeProvider<T>
.
builder.Services
.AddLogging(b => { b.AddConsole(); })
.AddEnforcer("AcmeCorp.Global",
o =>
{
o.Licensee = "DEMO";
o.LicenseKey = "...";
})
.AddPolicyEnforcementPoint(o => o.Bias = PepBias.Deny)
.AddPolicyAttributeProvider<SubjectRecordProvider>()
.AddEmbeddedPolicyStore("ZuploEnforcerPdp.Policies");
With this in place, we can make a request using the API key of the user who does not have the manager role. As expected, we get an HTTP Status 403 (Forbidden).

Making the same request again, but this time with the API key of the user who has the manager role, the request succeeds.

Conclusion
In this blog post, I demonstrated how to create a PDP with Enforcer and how to enable Zuplo API Gateway to consult the Enforcer PDP using the AuthZen protocol to determine if the API call should be allowed. This separation of concerns enables developers to use their preferred API gateway and Policy Decision Point.
Enforcers' ALFA policy language is the most readable policy language available today, allowing not only developers but also other stakeholders to understand access control.
The source code for the PDP can be found on GitHub
Try Enforcer for Free
Get a 30-day free, fully functional license of Enforcer and use policies to enforce your API gateway rules today.