Intelligent Role-Based Data Access Control Blueprint
- AccessIQ — Authorization-Aware Chatbot
- Table of Contents
- Project Overview
- How It Works
- Architecture
- Get Started
- Project Structure
- Usage Guide
- Authorization Model
- Auth0 Configuration
- Environment Variables
- Inference Metrics
- Model Capabilities
- LLM Provider Configuration
- Technology Stack
- Troubleshooting
- License
- Disclaimer
AccessIQ is an authorization-aware chatbot for protected data access. Users authenticate with Auth0, submit prompts through a React frontend, and receive responses only after the backend validates identity, interprets intent with LangGraph, and checks authorization through MCP before touching protected data in PostgreSQL.
This makes AccessIQ suitable for:
- Authorization demos where natural language must remain inside strict access boundaries
- Secure application prototypes that need a clean separation between authentication, orchestration, policy, and data access
- Security-focused chatbot workflows where the LLM can assist with intent and response wording but never grant access
- Role- and attribute-based access control experiments using Auth0 claims plus resource metadata
Core system rule:
Auth0 -> Backend -> LangGraph -> MCP -> PostgreSQL -> Response
- A user logs in through Auth0 Universal Login.
- Auth0 returns a JWT containing trusted identity and namespaced access claims.
- The React frontend sends the prompt and bearer token to the FastAPI backend.
- The backend validates the JWT and normalizes user context.
- LangGraph interprets the request and maps it to an action and resource type.
- The backend performs a metadata-first authorization check through the MCP service.
- If access is allowed, the backend queries PostgreSQL for authorized data only.
- The backend returns a grounded response to the frontend.
- If access is denied, the system returns a safe denial response without exposing protected content.
AccessIQ uses a four-service local deployment: React frontend, FastAPI backend, dedicated MCP policy service, and PostgreSQL. Auth0 is the identity provider, while LangGraph coordinates request understanding and response generation inside the backend flow.
graph TB
subgraph "User Interface (port 3000)"
A[React Frontend]
A1[Login]
A2[Chat UI]
A3[Reports UI]
end
subgraph "Identity"
B[Auth0]
end
subgraph "Backend API (port 8000)"
C[FastAPI Backend]
C1[JWT Validation]
C2[LangGraph Orchestration]
C3[Metadata-First Access Flow]
end
subgraph "Authorization (port 9000)"
D[MCP Service]
D1[RBAC + ABAC Policy]
end
subgraph "Data (port 5432)"
E[PostgreSQL]
E1[reports table]
end
A1 --> B
B --> A
A2 --> C
A3 --> C
C --> C1
C1 --> C2
C2 --> C3
C3 --> D
D --> C3
C3 --> E
E --> C
C --> A
style A fill:#e1f5ff,color:#000
style B fill:#fdf2e9,color:#000
style C fill:#fff4e1,color:#000
style D fill:#e8f5e9,color:#000
style E fill:#f3e5f5,color:#000
Frontend (React + Vite)
- Auth0 login/logout flow
- Chat interface for protected requests
- Token forwarding to the backend
- Safe rendering of authorized data and denied states
Backend (FastAPI)
- Validates Auth0 JWTs
- Normalizes trusted user context
- Invokes LangGraph for intent understanding
- Calls MCP before protected actions
- Queries PostgreSQL only after authorization succeeds
MCP Service
- Central authorization decision point
- Combines RBAC and ABAC
- Returns
allowordeny, plus filters for list access when needed
PostgreSQL
- Stores protected
reports - Stores authorization-relevant resource metadata such as
owner_id,department, andsensitivity
Auth0
- Handles user authentication
- Stores access-related attributes in
app_metadata - Injects namespaced claims into tokens via a Post Login Action
| Service | Container | Host Port | Description |
|---|---|---|---|
frontend |
frontend |
3000 |
React client for login, chat, and reports UI |
backend |
backend |
8000 |
FastAPI API, LangGraph orchestration, Auth0 validation, DB access |
mcp |
mcp |
9000 |
Authorization service enforcing RBAC + ABAC |
postgres |
postgres |
5432 |
PostgreSQL database storing protected reports |
- User logs in through Auth0.
- Frontend calls the backend with the Auth0 bearer token.
- Backend validates the token and extracts
role,department, andclearance_level. - LangGraph interprets the request.
- Backend calls MCP with user, action, and resource metadata.
- MCP returns
allowordeny. - Backend reads only authorized records from PostgreSQL.
- Frontend renders the final response or a safe denial message.
Before you begin, make sure you have:
- Docker Desktop with Docker Compose
- An Auth0 tenant
- An OpenAI API key
**psql** installed locally if you want to run the seed script directly from your machine
docker --version
docker compose version
docker ps
psql --versiongit clone <your-repo-url>
cd <repo-folder>Create both environment files:
cp .env.example .env
cp backend/.env.example backend/.envPopulate the files with your Auth0, database, MCP, and OpenAI values.
Root .env:
VITE_AUTH0_DOMAIN=your-tenant.us.auth0.com
VITE_AUTH0_CLIENT_ID=YOUR_AUTH0_SPA_CLIENT_ID
VITE_AUTH0_AUDIENCE=https://api.yourapp.local
VITE_AUTH0_REDIRECT_URI=http://localhost:3000
VITE_API_BASE_URL=http://localhost:8000
POSTGRES_DB=appdb
POSTGRES_USER=app
POSTGRES_PASSWORD=app
POSTGRES_PORT=5432
MCP_PORT=9000
MCP_LOG_LEVEL=INFO
MCP_POLICY_MODE=developmentbackend/.env:
AUTH0_DOMAIN=your-tenant.us.auth0.com
AUTH0_AUDIENCE=https://api.yourapp.local
AUTH0_ISSUER=https://your-tenant.us.auth0.com/
AUTH0_CLIENT_ID=YOUR_AUTH0_M2M_CLIENT_ID
AUTH0_CLIENT_SECRET=YOUR_AUTH0_M2M_CLIENT_SECRET
AUTH0_CLAIM_NAMESPACE=https://your-app.example
DATABASE_URL=postgresql+psycopg://app:app@postgres:5432/appdb
MCP_BASE_URL=http://mcp:9000
MCP_TIMEOUT_SECONDS=5
OPENAI_API_KEY=YOUR_OPENAI_API_KEY
OPENAI_MODEL=gpt-4o-mini
LANGGRAPH_DEBUG=false
APP_ENV=development
APP_PORT=8000
LOG_LEVEL=INFOCreate these Auth0 resources:
- A Single Page Application for the React frontend
- An API for the FastAPI backend audience
- A Machine to Machine Application for Auth0 Management API access
Auth0 SPA settings:
- Allowed Callback URLs:
http://localhost:3000 - Allowed Logout URLs:
http://localhost:3000 - Allowed Web Origins:
http://localhost:3000 - Allowed Origins (CORS):
http://localhost:3000
For each test user, set access attributes in app_metadata:
{
"app_metadata": {
"role": "employee",
"department": "sales",
"clearance_level": 1
}
}Supported roles in this project:
adminmanageremployee
Use [auth0/actions/post-login.js](/Users/csuftitan/Desktop/c2l/MCPRBAC/auth0/actions/post-login.js) as your Auth0 Post Login Action.
The action should:
- read
event.user.app_metadata - require
role,department, andclearance_level - inject namespaced claims into the ID token and access token
- deny login if required metadata is missing
Add an Auth0 Action secret:
AUTH0_CLAIM_NAMESPACE
Set it to the same value used in [backend/.env.example](/Users/csuftitan/Desktop/c2l/MCPRBAC/backend/.env.example), for example:
https://your-app.example
Then bind the action to the Post Login flow in Auth0.
docker compose -f compose.yml up --buildDetached mode:
docker compose -f compose.yml up -d --buildIn a second terminal:
./scripts/seed_sample_data.shThis will:
- create the
reportstable from[scripts/init.sql](/Users/csuftitan/Desktop/c2l/MCPRBAC/scripts/init.sql) - seed sample reports from
[scripts/seed_sample_data.sql](/Users/csuftitan/Desktop/c2l/MCPRBAC/scripts/seed_sample_data.sql)
Once the stack is running:
- Frontend: http://localhost:3000
- Backend API: http://localhost:8000
- Backend Health: http://localhost:8000/health
- MCP Health: http://localhost:9000/health
curl http://localhost:8000/health
curl http://localhost:9000/health
docker compose -f compose.yml psUseful logs:
docker compose -f compose.yml logs -f
docker compose -f compose.yml logs -f backend
docker compose -f compose.yml logs -f frontend
docker compose -f compose.yml logs -f mcpdocker compose -f compose.yml downRun the services directly on your machine if you do not want to use Docker for frontend and backend iteration.
Backend
cd backend
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000Frontend
cd frontend
npm install
npm run dev -- --host 0.0.0.0 --port 3000MCP
cd mcp
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload --host 0.0.0.0 --port 9000PostgreSQL can still run through Docker if preferred.
AccessIQ/
├── auth0/
│ └── actions/
│ └── post-login.js
├── backend/
│ ├── app/
│ │ ├── api/routes/
│ │ ├── db/
│ │ ├── schemas/
│ │ └── services/
│ ├── tests/
│ ├── Dockerfile
│ ├── requirements.txt
│ └── .env.example
├── docs/
│ ├── architecture.md
│ ├── auth0.md
│ ├── backend.md
│ ├── api.md
│ ├── mcp.md
│ ├── database.md
│ ├── frontend.md
│ ├── deployment.md
│ ├── env.md
│ ├── langgraph.md
│ └── security.md
├── frontend/
│ ├── src/
│ │ ├── components/
│ │ ├── lib/
│ │ ├── pages/
│ │ └── App.jsx
│ ├── Dockerfile
│ ├── package.json
│ └── vite.config.js
├── mcp/
│ ├── app/
│ ├── tests/
│ ├── Dockerfile
│ └── requirements.txt
├── scripts/
│ ├── init.sql
│ ├── seed_sample_data.sql
│ └── seed_sample_data.sh
├── compose.yml
├── .env.example
└── README.md
Authenticate
- Open http://localhost:3000.
- Click the login button.
- Complete Auth0 Universal Login.
- Return to the app with a valid session.
Use the chatbot
- Enter a request such as
Show me the HR team report. - The frontend sends the prompt and bearer token to
POST /chat. - The backend validates the token, checks MCP, and returns the result.
- If authorized, you see grounded content.
- If denied, you see a safe access-denied response.
Browse reports
GET /reportsGET /reports/{id}DELETE /reports/{id}for authorized users only
Admin flow
POST /admin/userscreates Auth0 users through the backend using the Auth0 Management API
Useful protected endpoints:
GET /mePOST /chatGET /reportsGET /reports/{report_id}DELETE /reports/{report_id}POST /admin/users
AccessIQ uses RBAC + ABAC through the MCP service.
RBAC
adminhas broad accessmanageris department-scopedemployeeis owner-scoped
ABAC
Authorization decisions also depend on:
departmentclearance_levelowner_idsensitivity
Important system rules:
- Auth0 is the only identity source
- MCP is the only authorization authority
- Backend enforces all protected access
- LangGraph interprets requests but does not grant access
- The LLM never decides permissions
- Deny by default when inputs or policy are unclear
AccessIQ expects custom namespaced claims from Auth0. A typical token should include:
{
"sub": "auth0|123",
"https://your-app.example/role": "manager",
"https://your-app.example/department": "sales",
"https://your-app.example/clearance_level": 2
}The backend normalizes these into:
{
"id": "auth0|123",
"role": "manager",
"department": "sales",
"clearance_level": 2
}Minimum practical Auth0 Management API scopes for the backend admin user-creation flow:
create:usersread:usersupdate:users
This file is shared by:
frontendpostgresmcpbackendfor shared values
| Variable | Description |
|---|---|
VITE_AUTH0_DOMAIN |
Auth0 tenant domain used by the frontend |
VITE_AUTH0_CLIENT_ID |
Auth0 SPA client ID |
VITE_AUTH0_AUDIENCE |
Backend API audience |
VITE_AUTH0_REDIRECT_URI |
Local frontend callback URL |
VITE_API_BASE_URL |
Browser-reachable backend URL |
POSTGRES_DB |
PostgreSQL database name |
POSTGRES_USER |
PostgreSQL username |
POSTGRES_PASSWORD |
PostgreSQL password |
POSTGRES_PORT |
PostgreSQL host port |
MCP_PORT |
MCP service port |
MCP_LOG_LEVEL |
MCP log level |
MCP_POLICY_MODE |
MCP runtime mode |
This file is backend-only and contains trusted service configuration.
| Variable | Description |
|---|---|
AUTH0_DOMAIN |
Auth0 tenant domain |
AUTH0_AUDIENCE |
Expected API audience |
AUTH0_ISSUER |
Expected Auth0 issuer URL |
AUTH0_CLIENT_ID |
Backend-related Auth0 client ID |
AUTH0_CLIENT_SECRET |
Backend-related Auth0 client secret |
AUTH0_CLAIM_NAMESPACE |
Namespace for custom claims |
DATABASE_URL |
PostgreSQL connection string |
MCP_BASE_URL |
MCP base URL |
MCP_TIMEOUT_SECONDS |
MCP request timeout |
OPENAI_API_KEY |
LLM provider API key |
OPENAI_MODEL |
LLM model name |
LANGGRAPH_DEBUG |
LangGraph debug flag |
APP_ENV |
App environment |
APP_PORT |
Backend listen port |
LOG_LEVEL |
Backend log level |
The table below summarizes the current OpenAI cloud inference benchmark for AccessIQ using the chatbot request path averaged over 3 runs.
| Provider | Model | Deployment | Context Window | Avg Input Tokens | Avg Output Tokens | Avg Tokens / Request | P50 Latency (ms) | P95 Latency (ms) | Throughput (req/s) | Hardware |
|---|---|---|---|---|---|---|---|---|---|---|
| OpenAI (Cloud) | gpt-4o-mini |
API (Cloud) | 128K | 1,417 | 162 | 1,579 | 3,180 | 5,950 | 0.241 | N/A |
Notes:
- Input Tokens are the tokens sent into the final OpenAI response-generation step, including the response-writer instructions, the user's prompt, and the authorized data prepared by the backend after LangGraph orchestration, MCP authorization, and database retrieval.
- Output Tokens are the tokens generated by OpenAI in the final natural-language response returned to the user.
- MCP is part of the backend enforcement flow in AccessIQ, not an OpenAI tool call in the current implementation, so MCP authorization and database retrieval are part of the overall request path but are not themselves counted as model tokens.
- Token counts and latency may vary slightly per run due to model non-determinism and differences in authorized data returned to the response generator.
gpt-4o-miniis configured throughOPENAI_API_KEYandOPENAI_MODELinbackend/.env.
OpenAI's cost-efficient multimodal model, used here through the OpenAI API for AccessIQ response generation.
| Attribute | Details |
|---|---|
| Parameters | Not publicly disclosed |
| Architecture | Multimodal Transformer (text + image input, text output) |
| Context Window | 128,000 tokens input / 16,384 tokens max output |
| Reasoning Mode | Standard inference |
| Tool / Function Calling | Supported |
| Structured Output | JSON mode and schema-constrained output supported |
| Multilingual | Broad multilingual support |
| Pricing | $0.15 / 1M input tokens, $0.60 / 1M output tokens |
| Fine-Tuning | Supported via OpenAI API |
| License | Proprietary (OpenAI Terms of Use) |
| Deployment | Cloud-only |
| Knowledge Cutoff | October 2023 |
| Capability | GPT-4o-mini |
|---|---|
| Natural-language response generation | Yes |
| Function / tool calling | Yes |
| JSON structured output | Yes |
| Cloud deployment | Yes |
| On-prem / air-gapped deployment | No |
| Multimodal support | Yes |
| Native context window | 128K |
Set the following values in backend/.env:
OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-4o-mini
OPENAI_TIMEOUT_SECONDS=10Recommended model:
gpt-4o-mini
OPENAI_API_KEY enables the final natural-language response writer, while OPENAI_MODEL selects the OpenAI model used for that final response generation step.
- FastAPI for API routing and request handling
- Uvicorn as the ASGI server
- LangGraph for chatbot orchestration
- SQLAlchemy / psycopg for PostgreSQL access
- Auth0 JWT validation for trusted user identity
- React 18
- Vite
- Auth0 SPA integration
- Docker Compose for local multi-service orchestration
- PostgreSQL 16 for protected resource storage
- Dedicated MCP service for centralized authorization
- Auth0 for authentication and claim injection
Login succeeds but backend rejects the token
Check:
AUTH0_AUDIENCEmatchesVITE_AUTH0_AUDIENCEAUTH0_ISSUERis exactlyhttps://<tenant-domain>/AUTH0_CLAIM_NAMESPACEmatches the Auth0 Action secret- the Post Login Action is attached to the Post Login flow
Login fails immediately after Auth0 authentication
The Post Login Action fails closed when required app_metadata is missing. Confirm the user has:
roledepartmentclearance_level
Frontend cannot reach backend
Check:
VITE_API_BASE_URL=http://localhost:8000- backend container is running
- frontend is running on
http://localhost:3000
Backend cannot create Auth0 users
Check:
- Auth0 Management API scopes
Database seeding fails
Check:
- PostgreSQL container is running
- your
.envPostgres values are correct psqlis available locally if you use./scripts/seed_sample_data.sh
Protected access looks wrong
Check:
- the user claims in Auth0
- the report metadata in PostgreSQL
- the MCP service health and logs
- whether the request should be department-scoped, owner-scoped, or denied
This project is licensed under our LICENSE file for details.
AccessIQ is provided as-is for demonstration and educational purposes. While we strive for accuracy:
- Authorization behavior, generated responses, and orchestration flows should be reviewed by a qualified engineer before use in production systems
- Do not rely solely on AI-generated responses without testing, validation, and policy review
- Do not submit confidential or proprietary data to third-party API providers without reviewing their data handling policies
- The quality of generated responses depends on the underlying model, prompts, authorization inputs, and available data
For full disclaimer details, see DISCLAIMER.md.
