HermitAI: Fine-Grained AI Access Control, API-First Auth & Permissions with Permit.io, Astro & Gemini
This is a submission for the Permit.io Authorization Challenge: AI Access Control, API-First Authorization Reimagined and Permissions Redefined What I Built For the Permit.io Authorization Challenge, I built HermitAI, an Astro-based web application showcasing a multi-faceted approach to fine-grained authorization using Permit.io. The application features two core components secured by Permit.io: Dynamic Blog RBAC: A blog list where user permissions (specifically, the ability to see 'Edit' and 'Delete' buttons) are dynamically controlled based on the selected user's role (admin, editor, basic) and the corresponding permissions defined in Permit.io for update and delete actions on the Blog resource. The UI updates in real-time upon user selection. AI Q&A with Topic-Based Authorization: An innovative Q&A feature powered by Google Gemini 2.5 Pro. Users can select a predefined topic ('Document Tag' like 'bitcoin', 'hermit', 'brain') and ask questions. Permit.io enforces that only users with the appropriate role (admin, qna) granted the ask action on the specific Blog resource (keyed by the selected document tag) within the blog-tenant can receive an answer from the AI. The AI is also prompted only to answer questions relevant to the selected topic. This project tackles the complexity of managing granular access control in modern web apps, demonstrating how Permit.io can secure both traditional CRUD-like operations and cutting-edge AI features within a unified, centrally managed policy, enforced via dedicated Astro API routes. Demo Live App: HermitAI: Intelligent Q&A with Fine-Grained AI Access Control | backup Video Demo: Project Repo Repo: Astro + Permit.io + Gemini: RBAC & AI Q&A Example kafechew / astro Astro + Permit.io + Gemini: RBAC & AI Q&A Example This project demonstrates integrating Role-Based Access Control (RBAC) using Permit.io and AI-powered Q&A using Google Gemini into an Astro web application. It showcases: Dynamic RBAC: A blog where user permissions (Edit/Delete posts) change dynamically based on the selected user and their assigned role via Permit.io. AI-Powered Q&A with Authorization: A Q&A interface where users ask Google Gemini questions about specific topics ("documents" identified by tags), with access controlled by Permit.io policies. Core Features Permit.io RBAC: Manages user permissions for blog actions (update, delete) and Q&A access (ask). Google Gemini Integration: Provides AI-driven answers based on user questions and document tags. Dynamic UI: Frontend components (React BlogList, Astro QnaForm) reactively update based on the selected user's permissions fetched from Permit.io via Astro API routes. Astro API Routes: Backend endpoints (/api/getPermissions.json, /api/askQna.json) handle authorization checks… View on GitHub The repository includes a detailed README.md with full setup instructions (including Permit.io configuration steps), code explanations, and deployment guidance. My Journey & Technical Implementation Building HermitAI involved several iterative steps, focusing on integrating Permit.io for robust, fine-grained authorization: Foundation: Started with a basic Astro project structure (astro.config.mjs, package.json) and displayed static blog data (src/utils/blogs.js). Fine-Grained Blog RBAC: Permit.io Setup: Defined a Blog resource with read, update, delete actions; created roles (admin, editor, basic); assigned permissions (e.g., editor gets read/update/delete); created users (admin, editor, newuser) and assigned roles within a blog-tenant. API Route: Created /api/getPermissions.json.js using the Permit.io Node SDK (permitio). This route takes a userId and checks permissions for update and delete actions against the Blog resource type: permit.check(userId, action, { type: 'Blog', tenant: 'blog-tenant' }). Frontend Integration: Built a React component (src/components/BlogList.jsx) rendered as an Astro island. This component fetches permissions from the API route for the currently selected user and conditionally renders 'Edit'/'Delete' buttons. Adding AI: Integrated the Google AI SDK (@google/generative-ai), created a service (src/services/geminiService.js) to handle Gemini API calls, and built the frontend form (src/components/QnaForm.astro). Securing AI with Fine-Grained Control: Permit.io Setup: Added an ask action to the Blog resource. Created a qna role with ask permission. Assigned this role to relevant users (e.g., admin, canaskonly). API Route: Created /api/askQna.json.js. This route receives userId, question, and documentTag. Authorization Check: Crucially, it performs a fine-grained Permit.io check: permit.check(userId, 'ask', { type: 'Blog', key: documentTag, tenant: 'blog-tenant' }). This verifies if the user can ask about the specific documentTag (acting as the resourc
This is a submission for the Permit.io Authorization Challenge: AI Access Control, API-First Authorization Reimagined and Permissions Redefined
What I Built
For the Permit.io Authorization Challenge, I built HermitAI, an Astro-based web application showcasing a multi-faceted approach to fine-grained authorization using Permit.io. The application features two core components secured by Permit.io:
- Dynamic Blog RBAC: A blog list where user permissions (specifically, the ability to see 'Edit' and 'Delete' buttons) are dynamically controlled based on the selected user's role (
admin
,editor
,basic
) and the corresponding permissions defined in Permit.io forupdate
anddelete
actions on theBlog
resource. The UI updates in real-time upon user selection. - AI Q&A with Topic-Based Authorization: An innovative Q&A feature powered by Google Gemini 2.5 Pro. Users can select a predefined topic ('Document Tag' like 'bitcoin', 'hermit', 'brain') and ask questions. Permit.io enforces that only users with the appropriate role (
admin
,qna
) granted theask
action on the specificBlog
resource (keyed by the selected document tag) within theblog-tenant
can receive an answer from the AI. The AI is also prompted only to answer questions relevant to the selected topic.
This project tackles the complexity of managing granular access control in modern web apps, demonstrating how Permit.io can secure both traditional CRUD-like operations and cutting-edge AI features within a unified, centrally managed policy, enforced via dedicated Astro API routes.
Demo
Live App: HermitAI: Intelligent Q&A with Fine-Grained AI Access Control | backup
Video Demo:
Project Repo
Repo: Astro + Permit.io + Gemini: RBAC & AI Q&A Example
Astro + Permit.io + Gemini: RBAC & AI Q&A Example
This project demonstrates integrating Role-Based Access Control (RBAC) using Permit.io and AI-powered Q&A using Google Gemini into an Astro web application.
It showcases:
- Dynamic RBAC: A blog where user permissions (Edit/Delete posts) change dynamically based on the selected user and their assigned role via Permit.io.
- AI-Powered Q&A with Authorization: A Q&A interface where users ask Google Gemini questions about specific topics ("documents" identified by tags), with access controlled by Permit.io policies.
Core Features
-
Permit.io RBAC: Manages user permissions for blog actions (update, delete) and Q&A access (
ask
). - Google Gemini Integration: Provides AI-driven answers based on user questions and document tags.
-
Dynamic UI: Frontend components (React
BlogList
, AstroQnaForm
) reactively update based on the selected user's permissions fetched from Permit.io via Astro API routes. -
Astro API Routes: Backend endpoints (
/api/getPermissions.json
,/api/askQna.json
) handle authorization checks…
The repository includes a detailed README.md
with full setup instructions (including Permit.io configuration steps), code explanations, and deployment guidance.
My Journey & Technical Implementation
Building HermitAI involved several iterative steps, focusing on integrating Permit.io for robust, fine-grained authorization:
- Foundation: Started with a basic Astro project structure (
astro.config.mjs
,package.json
) and displayed static blog data (src/utils/blogs.js
). - Fine-Grained Blog RBAC:
- Permit.io Setup: Defined a
Blog
resource withread
,update
,delete
actions; created roles (admin
,editor
,basic
); assigned permissions (e.g.,editor
getsread
/update
/delete
); created users (admin
,editor
,newuser
) and assigned roles within ablog-tenant
. - API Route: Created
/api/getPermissions.json.js
using the Permit.io Node SDK (permitio
). This route takes auserId
and checks permissions forupdate
anddelete
actions against theBlog
resource type:permit.check(userId, action, { type: 'Blog', tenant: 'blog-tenant' })
. - Frontend Integration: Built a React component (
src/components/BlogList.jsx
) rendered as an Astro island. This component fetches permissions from the API route for the currently selected user and conditionally renders 'Edit'/'Delete' buttons.
- Permit.io Setup: Defined a
- Adding AI: Integrated the Google AI SDK (
@google/generative-ai
), created a service (src/services/geminiService.js
) to handle Gemini API calls, and built the frontend form (src/components/QnaForm.astro
). - Securing AI with Fine-Grained Control:
- Permit.io Setup: Added an
ask
action to theBlog
resource. Created aqna
role withask
permission. Assigned this role to relevant users (e.g.,admin
,canaskonly
). - API Route: Created
/api/askQna.json.js
. This route receivesuserId
,question
, anddocumentTag
. - Authorization Check: Crucially, it performs a fine-grained Permit.io check:
permit.check(userId, 'ask', { type: 'Blog', key: documentTag, tenant: 'blog-tenant' })
. This verifies if the user canask
about the specificdocumentTag
(acting as the resource key). - Conditional AI Call: Only if the
permit.check
passes does the route call thegeminiService.js
. Otherwise, it returns a 403 Forbidden.
- Permit.io Setup: Added an
- Dynamic User Switching & Permissions:
- Implemented a user selector dropdown (
src/components/UserSelector.astro
). - Used a combination of custom DOM events (
userChanged
) dispatched fromUserSelector.astro
and listened for inindex.astro
, which then updates awindow
property. - The
BlogList.jsx
component listens for changes to thiswindow
property (via an effect hook) to detect user switches, re-fetch permissions from/api/getPermissions.json.js
for the new user, and update its state to show/hide buttons accordingly.
- Implemented a user selector dropdown (
- UI/UX Refinements: Moved the User ID selector, constrained Document Tags to a dropdown, and set a less-privileged default user (
newuser
). - Deployment & Debugging: Deployed to Vercel using
@astrojs/vercel/serverless
. Resolved Vercel-specific build errors related to TypeScript types (event handling, props) and explicittsconfigPath
configuration inastro.config.mjs
. - Prompt Engineering: Refined the prompt sent to Gemini via
geminiService.js
(called from/api/askQna.json.js
) to include thedocumentTag
and instruct the AI to verify question relevance, returning "Not related." for off-topic queries.
Challenges:
- Implementing robust state management and event-driven communication between Astro components and React islands (
UserSelector.astro
->index.astro
->BlogList.jsx
). - Debugging Vercel-specific build issues distinct from the local development environment.
Learnings:
- The effectiveness of Permit.io in decoupling complex, fine-grained authorization logic (both RBAC and resource-specific checks) from application code.
- Techniques for inter-component communication in Astro (custom events, window object).
- The necessity of explicit build configurations (
tsconfigPath
) for certain deployment environments. - The basics of prompt engineering to constrain AI behavior based on authorization context (the allowed topic).
Managing Policies with the Permit CLI
Permit CLI offers a powerful alternative for managing authorization configuration programmatically, especially beneficial for larger projects or GitOps workflows.
The Permit CLI enables:
- Automation: Scripting the creation/update of Resources, Actions, Roles, Tenants, and User Assignments.
- Policy as Code: Defining policies in YAML/JSON, version-controlled in Git.
- CI/CD Integration: Syncing policy changes within deployment pipelines.
- Bulk Operations: Managing large sets of users or policies efficiently.
Why Consider the CLI?
- Reproducibility: Ensures consistent setup across environments (dev, staging, prod).
- Collaboration: Standard Git workflows for policy changes.
- Auditability: Policy evolution tracked in version control.
Getting Started Example:
- Install:
npm install -g @permitio/cli
- Login:
permit login --key
- Explore:
permit list roles
,permit list resources
,permit policy get
- Apply Changes:
permit policy apply --file policy.yaml
Using Permit.io for Authorization: A Deep Dive
Permit.io was central to implementing fine-grained access control in HermitAI:
-
Modeling (Permit.io Dashboard):
- Resources: Defined
Blog
(representing both blog posts and Q&A topics). - Actions: Created specific actions:
read
,update
,delete
(for blog posts) andask
(for Q&A topics). - Roles: Created
admin
,editor
,basic
,qna
to represent different permission levels. - Tenants: Used a single
blog-tenant
to scope the policies. - Policy Definition: Visually mapped Roles to Actions on Resources within the Tenant (e.g.,
editor
canupdate
anddelete
Blog
resources;qna
canask
Blog
resources). -
- Resources: Defined
-
User Management (Permit.io Directory): Created users (
admin
,editor
,canaskonly
,newuser
) and assigned them the appropriate roles within theblog-tenant
. -
SDK Integration (
permitio
npm package): Imported and initialized the Permit SDK in the Astro API routes (/api/getPermissions.json.js
,/api/askQna.json.js
) using the API key from.env
.-
Runtime Enforcement (API Routes):
- Blog Permissions (
getPermissions.json.js
): Usespermit.check(userId, action, { type: 'Blog', tenant: 'blog-tenant' })
forupdate
anddelete
actions. This checks if the user's role grants the general permission for these actions on theBlog
resource type within the tenant. - Q&A Permissions (
askQna.json.js
): Uses a more specific check:permit.check(userId, 'ask', { type: 'Blog', key: documentTag, tenant: 'blog-tenant' })
. This verifies if the user has theask
permission specifically for theBlog
resource instance identified by thedocumentTag
key within the tenant. This is the core of the fine-grained AI access control.
- Blog Permissions (
Configuration: Securely stored the
PERMIT_TOKEN
in a.env
file, loaded by Astro.
This detailed setup allowed for context-aware, fine-grained authorization logic enforced at the API layer, keeping the core application code clean.
API-First Authorization in Practice
HermitAI embodies API-first authorization:
- Centralized Enforcement Points: All authorization decisions happen within the backend Astro API routes (
/api/*.js
). The frontend (BlogList.jsx
,QnaForm.astro
) does not contain authorization logic itself; it merely displays UI elements based on permissions granted by the backend. - APIs as Gatekeepers: The API routes act as strict gatekeepers.
/api/getPermissions.json.js
determines if action buttons should be enabled, and/api/askQna.json.js
determines if the Gemini API should even be called, based on thepermit.check()
result. - Externalized Policy: The "rules" (who can do what) live entirely within the Permit.io platform, not hardcoded in the Astro/React code.
- Decoupling & Agility: Changing who can edit blogs or ask about 'bitcoin' requires only a configuration change in the Permit.io dashboard (or via the CLI/API), not a code modification and redeployment of the Astro application. The changes take effect on the next
permit.check()
call.
Fine-Grained Authorization for AI Applications with Permit.io
Securing AI interactions was a key goal, achieved through Permit.io's fine-grained capabilities:
- The Challenge: Simply allowing/denying access to the AI feature isn't enough. We needed to control which topics a user could query.
- Permit.io Solution:
- Action: Defined a specific
ask
action. - Resource Modeling: Used the existing
Blog
resource type but leveraged thekey
attribute to represent individual topics (e.g.,key: 'bitcoin'
,key: 'hermit'
). - Policy: Granted the
ask
action on specificBlog
resource keys (or potentially using attributes/resource sets) to roles likeqna
andadmin
within theblog-tenant
. - Enforcement (
/api/askQna.json.js
): The checkpermit.check(userId, 'ask', { type: 'Blog', key: documentTag, tenant: 'blog-tenant' })
precisely enforces this policy at runtime. If a user only has permission toask
about 'bitcoin', an attempt to ask about 'hermit' will be denied by Permit.io before any expensive AI call is made.
- Action: Defined a specific
- Outcome: True fine-grained control over AI usage, ensuring users only access information domains they are authorized for.
- ABAC Potential: This resource-specific check (
key: documentTag
) naturally extends to Attribute-Based Access Control (ABAC). By adding attributes to users (e.g.,department: 'finance'
) and resources (e.g.,sensitivity: 'high'
) in Permit.io, and passing them in thecontext
ofpermit.check()
, even more sophisticated rules could be implemented without altering the API route's core logic (e.g., "Allow users in 'finance' to 'ask' about 'Blog' resources where 'sensitivity' is 'high'").
Permit.io provides the necessary granularity (resource-instance-level checks) and flexibility (RBAC evolving towards ABAC) required to secure modern AI interactions effectively.
Benefits of Externalized Authorization (Permit.io vs. Hardcoding)
Using Permit.io for externalized authorization provided clear advantages over embedding logic directly in the code:
Feature | Traditional (Hardcoded) | Externalized (Permit.io - HermitAI) |
---|---|---|
Logic Location | Scattered in controllers, services, UI components | Centralized in Permit.io Platform |
Code Complexity | High - if/else checks, role logic clutter code |
Low - Simple permit.check() calls in API routes |
Policy Updates | Requires code changes, testing, redeployment | No code change needed; update via UI/CLI/API, effective immediately |
Visibility | Poor - Hard to see the full policy picture | High - Central dashboard/policy files show clear overview |
Consistency | Risk of inconsistent checks across the application | High - Single source of truth ensures consistent enforcement |
Scalability | Becomes increasingly difficult to manage | Scales well with complexity; policies managed independently |
Agility | Slow - Changes tied to development cycles | Fast - Security/Product teams can update policies rapidly |
Example Change | Grant 'basic' users 'edit' access: Modify code, deploy | Grant 'basic' users 'edit' access: Update policy in Permit.io dashboard |
Externalizing authorization with Permit.io made HermitAI cleaner, more secure, and significantly more agile. Modifying permissions is a configuration task, not a development bottleneck.
Conclusion
HermitAI successfully demonstrates the power and flexibility of Permit.io for implementing fine-grained, API-first authorization in a modern Astro application. By externalizing authorization logic, it achieves granular control over both traditional blog management (dynamic RBAC) and innovative AI Q&A features (topic-specific access), all managed centrally. This project serves as a practical blueprint for building secure, maintainable, and adaptable applications by treating authorization as a distinct, manageable layer, showcasing how Permit.io effectively addresses the complexities of modern access control needs, including those unique to AI integration.