Authz: access decisions for your applications
Authz lets a realm answer “may this subject perform this action on this resource?” for the applications you build. You describe your application’s types and actions in a Cedar schema and write policies against it. Each application that enforces access, called a policy enforcement point (PEP), asks FerrisKey over the AuthZEN 1.0 evaluation API. FerrisKey is the policy decision point (PDP): it evaluates the realm’s rules and answers true or false.
Not the same as role permissions
FerrisKey already checks permissions, the bits a role carries, before it lets someone call its admin API. Authz is a different system. It decides about the objects of your own applications (a document, an invoice, an MCP tool) with rules your realm writes and owns.
The two meet in two places. A policy can test the roles, groups and organizations FerrisKey holds for a user, and a few permissions control who edits the rules and who may ask for decisions.
How a decision is made
- The PEP, authenticated as a service account, posts a subject, an action, a resource and an optional context.
- For a
FerrisKey::UserorFerrisKey::Clientsubject, FerrisKey reads the subject’s roles, groups, organizations and attributes from the database. Any other subject is described by the request itself. - FerrisKey checks the request against the realm’s schema, then Cedar evaluates the realm’s enabled policies. A request is allowed only when a
permitpolicy matches and noforbidpolicy does. - The response carries
"decision": trueor"decision": false.
Anything the rules do not permit is a deny, so a realm with no policy permits nothing.
The gates
Three conditions have to hold before a PEP gets a decision.
| Gate | Setting | When it is closed |
|---|---|---|
| The instance serves the module | AUTHZ_ENABLED=true (default false) | Every route of the module answers 404, as if it did not exist |
| The realm has the module on | PUT /realms/{realm_name}/authz/state with {"enabled": true} | Evaluation answers 404 with the reason authorization_not_enabled. The management routes keep working, so you can prepare the rules first |
| The caller may ask | A service account holding evaluate_authorization | 403, or 401 when there is no valid token |
AUTHZ_ENABLED mounts the whole module, not only the evaluation endpoint: the management routes under /realms/{realm_name}/authz, the evaluation routes under /realms/{realm_name}/authzen, and the metadata document under /.well-known/authzen-configuration. The variable is described with the others in Configuration.
Permissions
Four permissions decide who can do what. All of them are granted through roles.
| Permission | What it allows |
|---|---|
evaluate_authorization | Call the two evaluation endpoints. Only a service account is accepted |
view_authorization | Read the schema, policies, templates and state, validate and simulate policies, and see which policies decided (context.reason_admin) in evaluation responses |
manage_authorization | Everything view_authorization allows, plus writing the schema, policies, templates and state |
manage_realm | The same as manage_authorization. For evaluation it also satisfies the permission check, but only for a service account |
A human administrator cannot evaluate
The evaluation endpoints refuse any caller that is not a service account, whatever permissions it holds. An administrator with manage_realm who calls them with their own token gets a 403. Give the PEP its own confidential client, and use an administrator for the management routes.
Quick start
The example protects a document:read action with a role. It uses a realm named acme. Steps 2 to 4 need the token of an administrator holding manage_authorization or manage_realm. Step 6 uses the token of the PEP built in step 5. Every call carries Authorization: Bearer <token>.
Turn the module on for the instance
Set AUTHZ_ENABLED=true and restart the API. Until you do, none of the routes below exist.
Describe your application
Upload a Cedar schema. Without a namespace it is stored under App.
PUT /realms/acme/authz/schema{
"source": "namespace App {\n entity Document = {\n classification: String,\n };\n\n action \"document:read\" appliesTo {\n principal: [FerrisKey::User],\n resource: [Document],\n };\n}"
}The same schema, unescaped:
namespace App {
entity Document = {
classification: String,
};
action "document:read" appliesTo {
principal: [FerrisKey::User],
resource: [Document],
};
} Write a policy
The policy permits the holders of one role to read documents. Roles are named by their UUID, so replace the one below with the id of a role of your realm (GET /realms/acme/roles lists them) and give that role to the user you will test with.
POST /realms/acme/authz/policies{
"name": "editors-can-read",
"body": {
"type": "static",
"source": "permit (\n principal in FerrisKey::Role::\"01a0d3dd-a750-7817-aa2b-7e8b98f68e66\",\n action == App::Action::\"document:read\",\n resource is App::Document\n);"
}
}The answer is a 201 with the stored policy. A policy that does not validate against the schema is refused with a 422.
Turn the realm on
PUT /realms/acme/authz/state{ "enabled": true }{ "enabled": true, "version": 2 }version counts the writes to the schema, the policies and the templates. Turning the realm on does not change it.
Create the PEP
Create a confidential client with service_account_enabled (see service accounts). Then create a role that carries the permission and give it to the client’s service account user.
POST /realms/acme/roles
GET /realms/acme/users?service_account=true
POST /realms/acme/users/{user_id}/roles/{role_id}{ "name": "pdp-caller", "permissions": ["evaluate_authorization"] }The client_id field of a user in the list is the UUID of its client. Finally, request a token for the client with the client credentials grant.
Ask for a decision
With the PEP’s token, ask whether a user may read a document. The user is identified by their UUID.
POST /realms/acme/authzen/access/v1/evaluation{
"subject": { "type": "FerrisKey::User", "id": "0199a4f2-3c1e-7b8a-9f00-1a2b3c4d5e6f" },
"action": { "name": "document:read" },
"resource": {
"type": "App::Document",
"id": "42",
"properties": { "classification": "public" }
}
}{ "decision": true }If the user does not hold the role, the same call returns { "decision": false } with a 200. A refusal is not an error. If the PEP also holds view_authorization, the response adds the policies that decided:
{
"decision": true,
"context": { "reason_admin": { "policies": ["editors-can-read"] } }
} The user’s roles are read from the database on every request, so changing them takes effect on the next decision. Only the compiled rules are cached, and no replica serves rules that are more than a second out of date.
The console has no screen for Authz. Every step above goes through the API.
In this module
Schema, policies and templates
Write the rules: the Cedar schema, static and linked policies, validation, simulation and the limits.
Evaluation
The AuthZEN endpoints: subjects, batches, failure behaviour and the agent and MCP types.
AuthZEN
The standard behind the evaluation API, and the problem it solves.
Roles and permissions
The permissions that control who edits rules and who asks for decisions.