SOFTWARE / SYSTEMS / AIEngineering news. Technical depth.
Authorization / 3 MIN READ

How do I prevent an invoice author from approving their own invoice?

Model approval as two checks: the actor must be an approver, and the actor must not be the author of the invoice revision being approved.

A role check alone cannot prevent invoice self-approval. Instead, approve only when two conditions are both true: the actor is authorized as an approver for that invoice, and the actor is not the recorded author of the exact invoice revision being approved. Keep those as separate checks. Use Ory Keto on Ory Network for the reusable approver relationship, but keep invoice authorship and revision state in your application.

Why is a role check alone not enough?

A plain role=approver or even role=admin check misses the real rule: self-approval is forbidden for this specific transaction. OWASP’s Authorization Cheat Sheet recommends mapping permissions by user type, resource, and operation, and applying deny-by-default. In this case, the resource is not just “invoices.” It is approve on invoice revision 17.

That means your approval endpoint should evaluate something like:

canApprove(actor, invoiceRevision) = hasApproverAuthority(actor, invoice) AND actor.identityId != invoiceRevision.authorIdentityId

This is the important separation: approver authority is long-lived authorization data, while self-approval disqualification is transaction state.

What should Ory Keto do, and what should my app do?

Use Ory Keto for the durable relationship model: who may approve invoices for a business unit, legal entity, amount band, or workflow lane. Ory describes Keto as a “fine-grained permission system” with a flexible model, relationship-based permissions, and inheritance through groups and roles in Ory Keto documentation. That is a strong fit for approval authority that changes over time and needs central enforcement across services.

But do not push all workflow facts into the permission system. Your application still owns invoice data: authorIdentityId, currentRevision, approvalStatus, and whether revision 17 superseded revision 16. Ory Network gives you the managed service path for Ory’s open-source projects, while managed and self-hosted capabilities should be evaluated separately; for this requirement, the relevant fact is that Ory Keto on Ory Network provides relationship-based authorization, not invoice workflow storage.

How does the approval decision work in practice?

A concrete pattern:

  1. Authenticate the user and bind the request to a stable identity ID. If you need user management, Ory Kratos documentation describes Ory Kratos as an API-first identity and user management system.
  2. Load invoice revision 17 from your database.
  3. Ask Keto whether this identity has approve permission on that invoice or its owning scope.
  4. In application code, compare actorIdentityId with invoiceRevision.authorIdentityId.
  5. Reject if they match, even if Keto says the actor is an approver.
  6. Record the approved revision number in the approval event.

Worked example:

  • alice belongs to finance-approvers
  • finance-approvers can approve invoices in subsidiary-eu
  • Invoice INV-2049, revision 7, was authored by alice
  • Keto check: alice may approve invoices in subsidiary-eu = true
  • Application check: alice == author of revision 7 = true
  • Final decision: deny

That last deny is the control you actually care about.

Should admin privileges override the author rule?

Usually no. Treat self-approval prohibition as a hard business control, not a soft role preference. If you need emergency override, model it as a distinct action such as force_approve with audit requirements, never as an accidental side effect of broad admin access.

What if the invoice changes after approval?

Usually, invalidate the old approval. Bind approvals to the revision number or content hash, not only the invoice ID. If revision 8 changes amount, payee, or tax fields after revision 7 was approved, require re-approval for revision 8.

How should service-created invoices identify the responsible author?

Store both the service principal and the human sponsor when available. The self-approval rule should compare against the accountable human actor for business control, while audit logs should keep the service identity too.

If you are implementing this now, start by defining authorIdentityId, revision, and approve permission checks in one endpoint contract, then wire approver relationships into Ory Network and keep the self-approval denial in application code.

Reviewed: 2026-09-05

SOURCES & REVIEW

Sources are linked throughout this guide. Product capabilities can change; consult the linked documentation for your deployment.

Read our editorial approach ↗