This guide explains how to configure a SGNL Protected System with a Custom Transform that formats SGNL Access API responses to conform to the OpenID AuthZEN Authorization API 1.0 standard. This enables your Protected System to act as an AuthZEN-compliant Policy Enforcement Point (PEP), receiving decisions from SGNL as the Policy Decision Point (PDP) in a standardized format.
The AuthZEN Authorization API 1.0 standard defines a common interface between Policy Enforcement Points (PEPs) and Policy Decision Points (PDPs). SGNL is a co-author of this standard. SGNL’s Access API returns decisions in its own native format; using a Custom Transform, you can reformat those responses to match the AuthZEN schema, enabling interoperability with any AuthZEN-compliant system.
A single AuthZEN Access Evaluation response looks like this:
{
"decision": true
}
With optional context:
{
"decision": false,
"context": {
"reason_admin": {
"403": "Request failed policy C076E82F"
},
"reason_user": {
"403": "Insufficient privileges. Contact your administrator"
}
}
}
The decision field is a boolean (true = allow, false = deny) — note that SGNL’s native API returns string values ("Allow" / "Deny"), so the transform must handle this conversion.
The Request mapping tells SGNL how to extract the principal, action, and asset from the incoming AuthZEN request payload. Navigate to the Request tab of your Protected System configuration.
An AuthZEN Access Evaluation request has the following structure:
{
"subject": {
"type": "user",
"id": "[email protected]"
},
"evaluations": [
{
"action": {
"name": "can_read"
},
"resource": {
"type": "account",
"id": "123"
}
}
]
}
Configure the mapped fields as follows:
| Mapped Field | Access API Parameter | |
|---|---|---|
{$.subject.id} | → | id |
| (leave blank) | → | ipAddress (Optional) |
{$.evaluations.*.action.name} | → | query.action |
{$.evaluations.*.resource.id} | → | query.assetId |
Note:
subject.typeandresource.typeare not mapped here — asset types are managed through your Protected System’s asset type configuration rather than the request mapper.
Navigate to the Transforms tab of your Protected System configuration.
This transform maps a SGNL Access API response to the AuthZEN Access Evaluation response format (Section 6.2 of the spec).
{{- /* AuthZEN Access Evaluations Transform - Boxcarred Decisions */ -}}
{
"evaluations": [
{{- range $i, $item := .decisions}}{{if $i}},{{end}}
{
"decision": {{if eq $item.decision "Allow"}}true{{else}}false{{end}}{{if $item.assetId}},
"context": {
"resource_id": "{{$item.assetId}}"
}{{end}}
}
{{- end}}
]
}
Note: To receive a transformed response, append
?transform=trueto your Access API request URL. For example:https://your-tenant.sgnlapis.cloud/access/v2/evaluations?transform=true
For richer AuthZEN compliance, include decision context such as reason codes and policy metadata. This aligns with the AuthZEN Decision Context specification (Section 5.5.1):
{{- /* AuthZEN Access Evaluations Transform with Context */ -}}
{
"evaluations": [
{{- range $i, $item := .decisions}}{{if $i}},{{end}}
{
"decision": {{if eq $item.decision "Allow"}}true{{else}}false{{end}},
"context": {
{{- if $item.assetId}}
"resource_id": "{{$item.assetId}}",
{{- end}}
"reason_admin": {
"{{if eq $item.decision "Allow"}}200{{else}}403{{end}}": "{{if eq $item.decision "Allow"}}Access granted{{else}}Access denied by SGNL policy{{end}}"
},
"reason_user": {
"{{if eq $item.decision "Allow"}}200{{else}}403{{end}}": "{{if eq $item.decision "Allow"}}Access permitted{{else}}Insufficient privileges. Contact your administrator{{end}}"
},
"evaluation_time_ms": {{$.evaluationDuration}}
}
}
{{- end}}
]
}
Once the request mapping and transform are configured, your Protected System (acting as the PEP) should send AuthZEN-formatted requests to SGNL with ?transform=true. SGNL will extract the principal, action, and asset using the request mapping, evaluate the access decision, and return a response in the AuthZEN format via the transform.
POST /access/v2/evaluations?transform=true HTTP/1.1
Host: clientName.sgnlapis.cloud
Content-Type: application/json
Authorization: Bearer <your-sgnl-token>
{
"subject": {
"type": "user",
"id": "[email protected]"
},
"action": {
"name": "can_read"
},
"resource": {
"type": "account",
"id": "123"
}
}
{
"evaluations": [
{
"decision": true,
"context": {
"resource_id": "123",
"reason_admin": {
"200": "Access granted"
},
"reason_user": {
"200": "Access permitted"
},
"evaluation_time_ms": 12
}
}
]
}
The following table maps AuthZEN concepts to their SGNL equivalents:
| AuthZEN Concept | AuthZEN Field | SGNL Equivalent | Request Mapping |
|---|---|---|---|
| Subject | subject.id | principalId | {$.subject.id} → id |
| Resource | resource.id | assetId | {$.evaluations.*.resource.id} → query.assetId |
| Action | action.name | action | {$.evaluations.*.action.name} → query.action |
| Decision (allow) | "decision": true | "decision": "Allow" | Handled by transform |
| Decision (deny) | "decision": false | "decision": "Deny" | Handled by transform |
| Decision Context | context object | Custom via transform | Handled by transform |
assetId may be absent.decision; always use {{if eq $item.decision "Allow"}}true{{else}}false{{end}} rather than passing the SGNL string value directly.?transform=true — Validate your transform output before connecting your Protected System by calling the API directly and inspecting the response.