OpenFGA is a high-performance and flexible authorization system that implements
the Zanzibar model. It allows you to define complex authorization rules based on
relationships between users, objects, and actions. This policy will authorize
requests using OpenFGA. If the request is not authorized, a 403 response will be
returned.
Beta
This policy is in beta. You can use it today, but it may change in non-backward compatible ways before the final release.
The options for this policy are specified below. All properties are optional unless specifically marked as required.
apiUrl(required)<string> - The URL of the OpenFGA service.
storeId(required)<string> - The ID of the store.
authorizationModelId(required)<string> - The ID of the authorization model.
allowUnauthorizedRequests<boolean> - Indicates whether the request should continue if authorization fails. Default is false which means unauthorized users will automatically receive a 403 response. Defaults to false.
credentials(required)<undefined> - No description available.
To use this policy, you must programmatically set the relationship checks to be
performed against your OpenFGA store. This is done using the static
setContextChecks method.
The most common way to set the authorization checks are:
Creating custom inbound policies for each authorization scenario
Creating a custom inbound policy that reads data from the OpenAPI operation
and sets the authorization checks dynamically
Create a file like modules/openfga-checks.ts to define your custom
authorization policies:
import { ZuploRequest, ZuploContext, RuntimeError, HttpProblems, OpenFGAAuthZInboundPolicy,} from "@zuplo/runtime";export async function canReadFolder( request: ZuploRequest, context: ZuploContext,) { if (!request.params?.folderId) { throw new RuntimeError("Folder ID not found in request"); } context.log.info("Setting OpenFGA context checks"); if (!request.user?.sub) { return HttpProblems.forbidden(request, context, { detail: "User not found", }); } // Set the authorization check to verify if the user has viewer access to the folder OpenFGAAuthZInboundPolicy.setContextChecks(context, { user: `user:${request.user.sub}`, relation: "viewer", object: `folder:${request.params.folderId}`, }); return request;}export async function canEditDocument( request: ZuploRequest, context: ZuploContext,) { if (!request.params?.documentId) { throw new RuntimeError("Document ID not found in request"); } if (!request.user?.sub) { return HttpProblems.forbidden(request, context, { detail: "User not found", }); } // Set the authorization check to verify if the user has editor access to the document OpenFGAAuthZInboundPolicy.setContextChecks(context, { user: `user:${request.user.sub}`, relation: "editor", object: `document:${request.params.documentId}`, }); return request;}
You can make your authorization checks more dynamic by reading data from your
OpenAPI specification or other sources. This allows you to define authorization
rules that adapt based on the route, method, or other request properties.
For example, you could access custom data defined in your route:
export async function dynamicAuthCheck( request: ZuploRequest, context: ZuploContext,) { // Access custom data from the route configuration const data = context.route.raw<{ "x-authz": { resourceType: string; permission: string; resourceIdParam: string; }; }>(); const authzData = data["x-authz"]; if (!authzData?.resourceType || !authzData?.permission) { throw new RuntimeError( "Missing resource type or permission in route config", ); } if (!request.user?.sub) { return HttpProblems.forbidden(request, context); } // Extract resource ID from request parameters const resourceId = request.params?.[authzData.resourceIdParam]; if (!resourceId) { throw new RuntimeError( `Resource ID parameter '${authzData.resourceIdParam}' not found`, ); } // Set dynamic authorization check OpenFGAAuthZInboundPolicy.setContextChecks(context, { user: `user:${request.user.sub}`, relation: authzData.permission, object: `${authzData.resourceType}:${resourceId}`, }); return request;}
typescript
Then in your OpenAPI document, you would set the custom data on the x-authz
property: