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

May 4, 2025 - 18:41
 0
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:

  1. 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.
  2. 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

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:

  1. Dynamic RBAC: A blog where user permissions (Edit/Delete posts) change dynamically based on the selected user and their assigned role via Permit.io.
  2. 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…

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:

  1. Foundation: Started with a basic Astro project structure (astro.config.mjs, package.json) and displayed static blog data (src/utils/blogs.js).
  2. 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.
  3. 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).
  4. 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 resource key).
    • Conditional AI Call: Only if the permit.check passes does the route call the geminiService.js. Otherwise, it returns a 403 Forbidden.
  5. Dynamic User Switching & Permissions:
    • Implemented a user selector dropdown (src/components/UserSelector.astro).
    • Used a combination of custom DOM events (userChanged) dispatched from UserSelector.astro and listened for in index.astro, which then updates a window property.
    • The BlogList.jsx component listens for changes to this window 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.
  6. UI/UX Refinements: Moved the User ID selector, constrained Document Tags to a dropdown, and set a less-privileged default user (newuser).
  7. Deployment & Debugging: Deployed to Vercel using @astrojs/vercel/serverless. Resolved Vercel-specific build errors related to TypeScript types (event handling, props) and explicit tsconfigPath configuration in astro.config.mjs.
  8. Prompt Engineering: Refined the prompt sent to Gemini via geminiService.js (called from /api/askQna.json.js) to include the documentTag 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:

  1. Install: npm install -g @permitio/cli
  2. Login: permit login --key
  3. Explore: permit list roles, permit list resources, permit policy get
  4. 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:

  1. 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) and ask (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 can update and delete Blog resources; qna can ask Blog resources).
    • Policy Definition
  2. User Management (Permit.io Directory): Created users (admin, editor, canaskonly, newuser) and assigned them the appropriate roles within the blog-tenant.

    • User Management
  3. 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.

  4. Runtime Enforcement (API Routes):

    • Blog Permissions (getPermissions.json.js): Uses permit.check(userId, action, { type: 'Blog', tenant: 'blog-tenant' }) for update and delete actions. This checks if the user's role grants the general permission for these actions on the Blog 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 the ask permission specifically for the Blog resource instance identified by the documentTag key within the tenant. This is the core of the fine-grained AI access control.
  5. 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 the permit.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 the key attribute to represent individual topics (e.g., key: 'bitcoin', key: 'hermit').
    • Policy: Granted the ask action on specific Blog resource keys (or potentially using attributes/resource sets) to roles like qna and admin within the blog-tenant.
    • Enforcement (/api/askQna.json.js): The check permit.check(userId, 'ask', { type: 'Blog', key: documentTag, tenant: 'blog-tenant' }) precisely enforces this policy at runtime. If a user only has permission to ask about 'bitcoin', an attempt to ask about 'hermit' will be denied by Permit.io before any expensive AI call is made.
  • 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 the context of permit.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.