Bonjour!

Who Let the Cats Out?

Solving the Authorization Mystery with ReBAC

🐱
HalfStack Vienna • September 2025

Bonjour! 👋

**Edouard Maleix** Engineering @ GetLarge Cat enthusiast & authorization detective 🕵️

AGENDA

→ OWASP API Security Risks (with cats!)

― Live Exploitation Demos

― The Authorization Horror Story

― Enter ReBAC: Relationships > Roles

― PurrfectSitter Demo with OpenFGA

Conference & Cat Edition 🐱

OWASP API Security Top 10

API1:2023 - Broken Object Level Authorization

**"Taking Someone Else's Stuff"** 🎒

**Conference example:** ✓ You're a speaker (correct role) ✗ But you can't take MY backpack! Just because you can access `/api/backpacks/` doesn't mean you can access `/api/backpacks/mine`

// Vulnerable: Only checks if user is a sitter
GET /api/cats/romeo/health-updates
Authorization: Bearer 
// Anne is a sitter, but is she Romeo's sitter? 🤔

API5:2023 - Broken Function Level Authorization

**"Wrong Time Slot, Wrong Stage"** ⏰

**Conference example:** ✓ You're a speaker (correct role) ✗ But it's NOT your time slot (wrong context) *"Hey, I'm also a speaker!" waves from audience* Audience: "Wait, two speakers?"

// Anne tries to post updates at 3 AM when her sitting ended at 6 PM
POST /api/sittings/123/updates
Authorization: Bearer 
// Is Anne an ACTIVE sitter right now?

Breaking PurrfectSitter 🔴

Live Exploitation Demos

🔴 LIVE: The Curious Case of the Wrong Cat Owner

**Setup:** Bob owns Romeo, Anne is a sitter

// Vulnerable endpoint
app.get('/api/cats/:catId', authenticateUser, async (req, res) => {
  const { catId } = req.params;
  // ❌ Only checks if user is authenticated, not if they own this cat
  const cat = await db.cats.findById(catId);
  res.json(cat);
});

🎯 **Audience participation:** "What cat IDs should we try?"

Enter ReBAC 🌟

There Has To Be A Better Way!

What If We Could Express This Naturally?

Bob **owns** Romeo Anne **sits** Romeo Jenny **administers** system

user:bob → owner → cat:romeo
user:anne → sitter → cat:romeo
user:jenny → admin → system

Welcome to **ReBAC** *Relation-Based Access Control*

From 127 Lines to This

**The Model:**

type cat
  relations
    define owner: [user]
    define admin: admin from system
    define active_sitter: [cat_sitting#sitter with is_active]
    define can_update: owner or admin or active_sitter

**The Check:**

const canUpdate = await fga.check({
  user: `user:${userId}`,
  relation: 'can_update',
  object: `cat:${catId}`,
  context: { current_time: new Date().toISOString() }
});

*"Raise your hand if you'd rather debug 6 lines than 127 lines at 3 AM 🙋"*

Who Let the Cats Out?: Solving the Authorization Mystery with ReBAC

By edouard_maleix

Who Let the Cats Out?: Solving the Authorization Mystery with ReBAC

HalfStack Vienna - September 2025

  • 10