Inspiration
This started with a project called DELM, Digital Employee Lifecycle Manager. The idea behind DELM was to treat AI agents the way companies treat employees: register them, track their lifecycle, assess their risk, and eventually retire them when they are no longer needed. It answered questions like which agents exist, who owns them, and should this one be decommissioned. Essentially the HR system for AI agents.
Building DELM taught us something unexpected. We could track that an agent existed. We could log its risk level and ownership. But we kept hitting a wall on a completely different question: what is this agent actually allowed to do right now, with which APIs, and at what scope?
Governance over the agent was one problem. Governance of what the agent actually does with external systems was a completely different one. DELM was never designed to answer it.
When the Auth0 Authorized to Act hackathon launched, we did not need to go looking for an idea. It was already sitting there from everything we learned building DELM. Auth0 Token Vault was exactly the missing piece we had been circling around: a proper credential layer where agents request scoped tokens at runtime rather than holding broad access forever.
What it does
ScopeGuard is a least-privilege AI agent authorization console built on Auth0 Token Vault. It enforces the principle that just because an agent has a token does not mean it is authorized to do everything with it.
Permission Dashboard
Every connected account, every scope each agent has been granted, when it was last used, and a one-click revoke button. A security team can answer "what can this agent do right now" in under ten seconds. The Reconnect GitHub button forces a fresh OAuth token with public_repo scope only, enforcing least privilege at the token level. Raw credentials never touch the frontend.
Agent Console with Real-Time Risk Classification
You type a natural language request. Claude Sonnet classifies the risk in real time:
- LOW (read-only): executes immediately
- MEDIUM (limited write): executes with audit log
- HIGH (destructive): step-up authorization modal appears before anything happens
The Agent Console shows Primary Agent and Sub-Agent side by side. Same command, two buttons, two different outcomes. That contrast is the Authorized to Act principle made visible.
Step-Up Authorization
High-risk actions like deleting a repository require explicit human approval before execution. The modal shows exactly what action is being requested, the risk classification, and gives the user Approve or Deny. Every step-up attempt is logged regardless of outcome.
Agent Delegation Console
When a parent agent spawns a sub-agent, ScopeGuard enforces downward scoping. The sub-agent can only inherit a subset of the parent permissions. Scope escalation is automatically blocked and logged. The full hierarchy is visible side by side: what the parent has, what the sub-agent gets, and what is blocked in red.
The sub-agent enforcement is not just visual. It is a real server-side action whitelist at /agent/sub. The sub-agent is limited to list_repos, list_issues, and get_user. Any write attempt returns a Scope Escalation Blocked response before the GitHub API is ever called.
Audit Log
Every action logged with timestamp, action type, API called, scope used, risk classification, and outcome. Token exchanges from Auth0 Token Vault are logged. Scope escalation attempts are logged. Step-up authorizations are logged. When something goes wrong you can reconstruct exactly what happened and when.
How we built it
The whole thing centers on Auth0 Token Vault as the credential layer. When a user connects their GitHub account, Auth0 handles the OAuth flow and stores the token securely. The agent never sees the raw credential. When it needs to act, it requests a scoped token at runtime through the Auth0 Management API.
| Layer | Technology |
|---|---|
| Backend | Node.js and Express with Auth0 OpenID Connect middleware |
| Auth and Credentials | Auth0 Token Vault for GitHub OAuth token management |
| AI Agent | Claude Sonnet for risk classification and agent logic |
| Sub-Agent Enforcement | Server-side whitelist: list_repos, list_issues, get_user only |
| Audit Storage | sql.js SQLite in-memory database seeded on startup |
| Frontend | Vanilla HTML, CSS, and JS with dark-themed responsive UI |
| Deployment | Render free tier with BASE_URL as environment variable |
The Most Important Architectural Decision
The sub-agent uses the same Auth0 Token Vault token as the primary agent. Least-privilege enforcement happens at the action whitelist level on the server, not at the token level. This is what Authorized to Act actually means in practice: authorization is contextual and enforced at the point of action, not just when credentials are issued.
Challenges we ran into
Honestly, almost everything broke at least once.
1. Token Vault GitHub Connection Failing With No Useful Error
The GitHub connection kept returning "connection not active for authentication." The root cause was a single dropdown in the Auth0 console: the connection Purpose must be set to "Authentication and Connected Accounts for Token Vault" not just "Connected Accounts for Token Vault." One dropdown, easy to miss, costs you half a day.
2. M2M Client Needed Its Own Separate Management API Grant
The M2M client needed its own Auth0 Management API grant with read:user_idp_tokens scope, completely separate from the Regular Web App grant. The error "Client not authorized to access resource server api/v2" gave no indication that a second grant on a second client was what was missing. We had to map the full Auth0 client architecture from scratch before it clicked.
3. Hardcoded localhost Breaking Render Deployment
The baseURL in server.js was hardcoded to localhost, which meant Auth0 callbacks always redirected back to localhost even after deploying to Render. Fixed by switching to process.env.BASE_URL with a localhost fallback. Render also runs on port 10000 internally, not 4000.
4. Duplicate Environment Variable Silently Blocking Deployment
A duplicate AUTH0_AUDIENCE entry in the Render environment variables was quietly preventing the deployment from working. No clear error message. Removing the duplicate fixed it immediately. The lesson: when a deployment fails with no obvious reason, audit your environment variables first.
5. PAT Fallback Hiding the Real Problem
A GITHUB_TOKEN personal access token fallback in the agent route was silently masking Token Vault retrieval failures. When Token Vault failed, the agent fell back to the PAT and everything looked fine. The real issue was completely invisible until we read the audit log carefully. We pulled the fallback out entirely, things broke visibly, and then we could actually fix the underlying problem.
6. Private Repos Showing Despite public_repo Scope
Even after configuring the Auth0 GitHub connection for public_repo scope, private repos kept appearing. The cached token from a previous connection still had full repo scope. Fixed two ways: added a /disconnect-github route that unlinks and relinks the GitHub identity via Auth0 Management API to force a fresh token, and changed the GitHub API endpoint to use ?visibility=public&affiliation=owner as a second enforcement layer.
7. Render Ephemeral Filesystem Wiping Audit Log
sql.js writes to a file on disk, but Render free tier has an ephemeral filesystem that resets on every restart. Solved by switching to a pure in-memory database seeded with realistic audit entries on every startup, so the audit log always tells the full story regardless of restarts.
8. Sub-Agent Was Visual Only, Not Functional
The original Agent Delegation tab was a static diagram. Upgraded to a real functional sub-agent at /agent/sub with a server-side scope whitelist. Added a side-by-side Agent Console UI with two buttons so the same command can be run on both agents, making the least-privilege enforcement visible and demonstrable in real time.
Accomplishments that we're proud of
The side-by-side agent comparison is the moment. Same command, same token vault, two agents, two completely different outcomes. Primary Agent creates the issue. Sub-Agent gets Scope Escalation Blocked. That 30-second demo makes the Authorized to Act concept tangible in a way that no diagram or explanation can.
We built a real sub-agent, not a visual one. It would have been easy to leave the delegation tab as a static diagram. Instead we built actual server-side enforcement at /agent/sub with a scope whitelist. The blocking is real, the logging is real, and the audit trail proves it.
Auth0 Token Vault is the only credential layer. Raw GitHub tokens never appear in the frontend, never touch the agent code, and never sit in environment variables at runtime. The agent requests what it needs, uses it, and the token lifecycle is managed entirely by Auth0. That is production-grade credential hygiene.
The audit log tells the full story. Every token exchange, every risk classification, every blocked scope escalation, every step-up authorization attempt is logged with timestamp, scope used, and outcome. A compliance officer can open this and answer any question about what the agent did and when.
What we learned
Auth0 Token Vault genuinely reduces the complexity of building secure agentic applications. The OAuth plumbing, token refresh, and credential storage that would normally take weeks came down to a handful of SDK calls. That freed us to focus on the governance layer, which is where the interesting problems live.
The hardest part of agentic AI security is not the technology. It is the governance model: deciding what an agent should be allowed to do, who gets to decide that, and what happens when it tries something outside those bounds. That is a product design problem as much as an engineering one.
The sub-agent delegation piece surprised us most. Every multi-agent system being shipped right now is making permission inheritance decisions implicitly, with no framework and no visibility. ScopeGuard makes it explicit.
One product gap worth flagging for Auth0: Token Vault works really well when a live user session exists. For background agents that need to act autonomously without a user present, the flow gets complicated in ways the current SDK does not handle cleanly. That is a real use case the next version of Token Vault should address.
What's next for ScopeGuard
Connect to more APIs. GitHub was the proof of concept. The same pattern applies to Salesforce, Jira, Confluence, and any OAuth-protected API. The architecture is generic. The next step is making it provider-agnostic.
Dynamic scope assignment. Right now scope profiles are set manually. The next version would have Claude Sonnet recommend the minimum scope needed for a given task before the agent runs, and auto-assign it.
Integration with DELM. ScopeGuard controls what agents can do. DELM tracks which agents exist and their risk levels. Connecting them closes the full loop: DELM registers an agent at MEDIUM risk, ScopeGuard automatically assigns the corresponding permission envelope, and both systems share the audit trail.
Background agent support. Token Vault works cleanly with live user sessions. Building a flow for autonomous background agents that need to act without a user present is the next technical frontier, and a gap worth solving with Auth0.
Policy as code. Export permission envelopes as YAML or JSON so they can live in source control, be reviewed in pull requests, and be versioned alongside the agent code that depends on them.
Bonus Blog Post
I have been building AI governance tools for the better part of a year. DELM tracked which agents existed. TrustGuard screened what went into them. But there was always a gap in the middle: what is the agent actually authorized to do right now, with which API, at what scope?
Token Vault closed that gap for me.
Before this hackathon I had never built a proper credential layer for agents. I had used API keys, PAT tokens, environment variables -- all the shortcuts that feel fine until something goes wrong. Token Vault changed how I think about agent credentials entirely. The token never touches the agent. The agent requests what it needs, uses it, and the lifecycle is managed by Auth0. That is not just cleaner. It is fundamentally safer.
The hardest thing I built was not the risk classifier or the audit log. It was the sub-agent. Not the code, the concept. Getting the enforcement to happen at the action whitelist level rather than the token level forced me to really understand what Authorized to Act means. The token is necessary. It is not sufficient. Authorization is contextual. It has to be enforced at the point of action.
The moment that made it real was running the same command on both agents side by side. Primary Agent created the issue. Sub-Agent got blocked. Same Token Vault, same command, different authorization. That is the whole idea in thirty seconds.
One honest gap I found: Token Vault works beautifully when a user session is present. Background agents acting autonomously without a user in the loop hit friction the current SDK was not designed for. That is the next problem worth solving. I hope the Auth0 team picks it up.
Building ScopeGuard taught me that agent security is an architecture problem, not a policy problem. You cannot prompt your way to least privilege. You have to build it in.
Built With
- anthropic-api
- auth0
- auth0-token-vault
- claude-sonnet
- css
- express-openid-connect
- express.js
- github-api
- github-oauth
- html
- javascript
- node-fetch
- node.js
- render
- sql.js
- sqlite
Log in or sign up for Devpost to join the conversation.