Skip to main content

Service-to-Service Authorization

Gate internal API calls between your backend services with the same policy engine that authorizes your users.

AuthzX treats every caller as a subject — users, services, AI agents. A service is just a subject with type: "service". No special infrastructure required.

How it works

  1. Create a subject for each service (e.g., service:billing, service:webhook)
  2. Write policies that grant those subjects access to specific resources and actions
  3. Each service authenticates to AuthzX via client credentials and calls /v1/authorize before accessing another service's resources

Step 1 — Create service subjects

Via Terraform

resource "authzx_subject" "billing_service" {
application_id = authzx_application.platform.id
name = "service:billing"
type = "service"
attributes = {
environment = "production"
team = "payments"
}
}

resource "authzx_subject" "webhook_service" {
application_id = authzx_application.platform.id
name = "service:webhook"
type = "service"
}

Via the Console

Go to your application → SubjectsCreate Subject. Set the type to service.

Step 2 — Write policies

Grant the billing service read access to invoices:

resource "authzx_policy" "billing_reads_invoices" {
application_id = authzx_application.platform.id
name = "billing_reads_invoices"
effect = "ALLOW"

subjects = [{ subject_id = authzx_subject.billing_service.id }]

resources = [{
resource_id = authzx_resource.invoices.id
actions = ["read", "list"]
}]
}

Grant the webhook service publish access to events:

resource "authzx_policy" "webhook_publishes_events" {
application_id = authzx_application.platform.id
name = "webhook_publishes_events"
effect = "ALLOW"

subjects = [{ subject_id = authzx_subject.webhook_service.id }]

resources = [{
resource_id = authzx_resource.events.id
actions = ["publish"]
}]
}

You can also use roles — create a service_reader role and assign it to multiple service subjects.

Step 3 — Check before every internal call

Each service authenticates to AuthzX using OAuth2 client credentials and checks authorization before accessing another service's resources.

Node.js (using @authzx/sdk)

import { AuthzXClient } from "@authzx/sdk";

const authzx = new AuthzXClient({
clientId: process.env.AUTHZX_CLIENT_ID,
clientSecret: process.env.AUTHZX_CLIENT_SECRET,
agentUrl: "http://localhost:8181",
});

async function fetchInvoice(invoiceId: string) {
const { allowed } = await authzx.authorize({
subject: { id: "service:billing", type: "service" },
resource: { type: "invoice", name: invoiceId },
action: { name: "read" },
});

if (!allowed) throw new Error("service:billing is not authorized to read this invoice");

return await invoiceService.get(invoiceId);
}

Go (using authzx-go)

client := authzx.NewClient(
authzx.WithClientCredentials(os.Getenv("AUTHZX_CLIENT_ID"), os.Getenv("AUTHZX_CLIENT_SECRET")),
authzx.WithAgentURL("http://localhost:8181"),
)

result, err := client.Authorize(ctx, &authzx.AuthorizeRequest{
Subject: authzx.Subject{ID: "service:billing", Type: "service"},
Resource: authzx.Resource{Type: "invoice", Name: invoiceID},
Action: authzx.Action{Name: "read"},
})

if !result.Allowed {
return fmt.Errorf("service:billing not authorized to read invoice %s", invoiceID)
}

curl

curl -X POST http://localhost:8181/v1/authorize \
-H "Authorization: Bearer $AUTHZX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"subject": { "id": "service:billing", "type": "service" },
"resource": { "type": "invoice", "name": "inv-2024-001" },
"action": { "name": "read" }
}'

Using ABAC conditions

You can add attribute-based conditions to service policies. For example, only allow the billing service to read invoices in production:

resource "authzx_policy" "billing_prod_only" {
application_id = authzx_application.platform.id
name = "billing_prod_only"
effect = "ALLOW"

subjects = [{ subject_id = authzx_subject.billing_service.id }]

resources = [{
resource_id = authzx_resource.invoices.id
actions = ["read"]
}]

conditions = [{
field = "subject.attributes.environment"
operator = "equals"
value = "production"
}]
}

Audit trail

Every service-to-service authorization decision is logged in the Decision Log with the full context — subject, resource, action, matched policy, and latency. Filter by type: service to see only service calls.