Permissions without
the if-statements.

Roles, scopes, conditions, attributes — typed end-to-end and evaluated at the edge in under 2 ms. One can() call replaces the spaghetti.

policies/projects.deploy.az
L 12 · C 28authaz/policy● 0 errors
1234567891011121314
// resources, actions, conditions — typed end-to-end
policy "projects.deploy" {
  resource  = Project
  action    = "deploy"

  allow if user.role in ["owner", "admin", "developer"]
  allow if resource.team in user.teams
  allow if user.attrs.level >= 4

  deny  if resource.archived == true
  deny  if request.geo in org.blocked_geos
  deny  if mfa.age > "5m" && resource.tier == "prod"
}
— LIVE DECISION1.2ms
userval@acme.com · adminactionprojects.deployresourceproj_01HZ · platform
role:admin in [owner,admin,developer]0.4ms
team:platform ∈ user.teams0.3ms
·resource.archived == false0.2ms
·mfa.age = 47s · ok0.3ms
→ ALLOWlogged · evt_8KQ2
— SCOPE INSPECTORsession
tenantorg_acmealways carried by session
teamplatform · billingderived from group claims
roleadmininherits :developer + :billing-admin
attrsfte · level=5attribute-based · ABAC
The model

Roles get you 80% there. Authaz handles the rest.

Pure RBAC breaks the moment your customer asks for 'admins, but only for their team.' Authaz lets you express that — and a hundred more — without rebuilding your authz.

— 01 · INPUTS

Roles, scopes, attributes.

Start with roles. Add scopes (team, region, resource). Add attributes (level, FTE, employment type). Compose them; we evaluate them.

roletenantteamregionlevelemploymentmfa.age
— 02 · ENGINE

One can.enforce() call.

Edge-evaluated, sub-2 ms. Combines RBAC + ABAC + resource scopes against your typed policies. Returns allow / deny with the matching rule.

p50 0.7msp99 1.4ms47 regionsversioneddiffable
— 03 · OUTPUTS

Decision + audit trail.

Typed allow / deny in your codebase. Every decision streams to your SIEM with the inputs, the matching policy, and the verdict — replay-able.

allowdenypartialaudit-evtsiemreplay
The matrix

Roles you can see at a glance.

Every project starts here: a role × action grid that tells you exactly who can do what. Override per-tenant from the same UI.

tenantorg_acme/policyv17/live
+ add role+ add actionoverrides · 3simulatediff →
role ↓
action →
read
100%
write
57%
deploy
43%
invite
71%
billing
43%
audit
86%
tokens
43%
delete
43%
admin
29%
scope
owner
org
admin
owner − billing
mfa
org
developer
admin − admin
team
·
if
team
team
·
team:platformteam:billing
reviewer
developer (read)
if
·
·
·
·
·
·
team:platform
billing-admin
+ billing.*
·
·
·
·
·
billing.*
support
reviewer + impersonate
·
if
·
·
·
·
tenant in cohort:enterprise
guest
— (read-only)
if
·
·
·
·
·
·
·
·
expires 7d
legend allow· denyif conditionteam teammfa mfa override
7 roles · 9 actions · 3 overrides · last edit 4m ago by@maya
Pieces

Everything an authz model touches.

— 01

A scope is more than a tenant id.

Tenant, team, role, attributes, resource. Authaz threads all five through every decision so policies can branch on whatever the buyer cares about — geo, employment type, MFA freshness, anything.

  • session-bound · forge-proof
  • ABAC + RBAC, composable
  • add a scope without a migration
tenant
org_acme
always carried by session
team
team:platform · team:billing
derived from group claims
role
admin
inherits :developer + :billing-admin
attrs
employee_type=fte · level≥4
attribute-based · ABAC
res
proj_01HZ · status=active
resource-aware filter
— 02

Policies that read like English.

Define rules in our policy language — or in TypeScript next to your app. Both compile to the same engine. Both are diff-reviewable.

  • versioned · diffable · rollback-able
  • TypeScript or DSL, your call
  • simulator runs every branch on real data
// resources, actions, conditions — typed end-to-end policy "projects.write" { allow if user.role in ["owner", "admin", "developer"] allow if resource.team in user.teams deny if resource.archived == true deny if request.geo in org.blocked_geos }
— 03

Every decision, traced and auditable.

Each can() emits an audit event with the inputs, the matching policy, and the verdict. Stream to Datadog, S3, or your SIEM.

  • allow / deny / partial
  • sub-2ms p99
  • replay-able from the audit log
userval@acme.com · adminactionprojects.deployresproj_01HZ · team:platform
✓ role:admin → projects.* · allow0.4ms
✓ team:platform ∈ user.teams · allow0.3ms
✗ resource.archived == false · ok0.2ms
→ ALLOW · projects.deploy on proj_01HZ
Code

One call. Same shape, every endpoint.

app/api/projects/[id]/deploy.tstyped
await authaz.can.enforce({ user: session.user, action: "projects.deploy", resource: project, }); // continues only if allow — otherwise throws PermissionDenied await deployer.queue(project);
Spec

The fine print, up front.

Model
RBAC + ABAC + resource scopes · composable
Decision latency
p50 0.7ms · p99 1.4ms · global edge
Custom roles
unlimited per org · inheritance · override per scope
Resources
typed in your codebase · auto-generated SDK · safe-rename
Conditions
role · team · attribute · time · geo · risk · custom function
Audit
every check · streamed · exportable to S3, Datadog, your SIEM
Tooling
simulator · explain · diff · rollback
Pricing model
unlimited checks · billed per active user
Pairs with

One platform. Every primitive.

Every Authaz product shares the same primitives — sessions, policies, audit, tenants. Pick what you need today; add the rest when you do.

Explore all products →
Get started

Stop branching on user.role.

Roles, scopes, attributes — one call, sub-2 ms decisions, audited every time.