DEV Community

Cover image for Building Anzen: What I Learned About Token Vault the Hard Way
Chella Kamina
Chella Kamina

Posted on

Building Anzen: What I Learned About Token Vault the Hard Way

When I started building Anzen for the Authorised to Act hackathon, I thought Token Vault would be the easy part. I was wrong.

The concept is simple and powerful: instead of your AI agent holding OAuth tokens for GitHub, Gmail, and Slack, Auth0 holds them in a secure vault. The agent requests a scoped token when it needs one, uses it, and the token is gone. No credentials stored in your app. No breach risk. No all-or-nothing access.

The implementation is where it gets interesting. The first thing I discovered is that nextjs-auth0 v4 is a completely different SDK from v3. The familiar handleAuth function is gone. The middleware file convention changed. Environment variable names changed. Even the callback URL path changed from /api/auth/callback to /auth/callback. None of this was obvious from the documentation.

The second discovery was about the token exchange flow itself. Calling getAccessToken() returns an Auth0 JWT — not the GitHub or Slack token you actually need. To get the provider token, you have to make a separate POST to the token endpoint using grant_type: urn:ietf:params:oauth:grant-type:token-exchange with specific subject and requested token type parameters. This is the Token Vault exchange, and getting those parameters exactly right took significant debugging.

The third discovery was Groq compatibility. Zod schemas include a $schema meta-field in their JSON output that Groq's API rejects, causing silent "Failed to call a function" errors. The fix was switching from Zod to jsonSchema() from the AI SDK.

Despite these challenges, the architecture we ended up with is exactly what AI agents should look like: zero credentials in the app, scoped access per action, and a user who stays in control at every step. That's the promise of Token Vault, and it's worth the difficulty of getting there.

Top comments (0)