dotnet-mcp-identityserver

sandeepuppalapati/dotnet-mcp-identityserver

3.2

If you are the rightful owner of dotnet-mcp-identityserver and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.

An ASP.NET Core implementation of the Model Context Protocol (MCP) server with Identity Server JWT authentication, featuring weather tools and Claude AI integration.

Tools
2
Resources
0
Prompts
0

.NET MCP Identity Server

A production-ready ASP.NET Core implementation of the Model Context Protocol (MCP) with enterprise-grade JWT authentication, demonstrating how to build secure, context-aware AI applications.

⚠️ Note: This is a demonstration/educational project showcasing MCP integration patterns. Review security settings and test thoroughly before production deployment.


📋 Table of Contents


🎯 The Problem

Modern AI applications need to:

  1. Authenticate Users: Integrate with enterprise identity systems (OAuth2, SAML, JWT)
  2. Provide Context: Give AI agents access to user-specific data, APIs, and resources
  3. Execute Tools: Allow AI to perform actions on behalf of authenticated users
  4. Maintain Security: Enforce proper authorization and API key management
  5. Scale Gracefully: Support multiple users with different permission levels

The Model Context Protocol (MCP) standardizes how AI applications access context and tools, but implementing it with enterprise authentication is complex.

Common Challenges

  • Authentication Gap: MCP servers often lack production-ready auth mechanisms
  • User Context: Tools need access to authenticated user identity and claims
  • API Key Management: Different users may have different API keys for external services
  • Testing Complexity: Difficult to test MCP flows without proper tooling

💡 The Solution

This project provides a reference implementation demonstrating:

  • Enterprise Authentication: JWT Bearer token validation via Identity Server
  • User-Scoped Tools: MCP tools that use authenticated user context
  • Hierarchical API Keys: User-specific, role-based, and default API key fallbacks
  • Built-in Test Client: Web UI for testing MCP flows without external tools
  • Production Patterns: Proper DI, logging, error handling, and configuration

Real-World Use Cases

  1. Enterprise AI Assistants: Deploy Claude/GPT with company SSO integration
  2. Multi-Tenant SaaS: Different customers get different API keys and data access
  3. Personalized Tools: Weather, calendar, CRM tools that respect user permissions
  4. Secure Agent Frameworks: Build LangChain/AutoGPT agents with proper auth

✨ Features

🔐 Authentication & Authorization

  • JWT Bearer Authentication with configurable Identity Server integration
  • Claims-Based Authorization extracting user identity, roles, and company context
  • Flexible Token Validation with development-friendly settings

🤖 AI Integration

  • Claude AI Integration with tool calling support (Anthropic SDK)
  • Multi-Turn Conversations with automatic tool execution
  • Demo Mode for testing without API keys

🌤️ Weather Tools (Example Integration)

  • Open-Meteo API integration (free, no auth required)
  • Location-Based Units: Automatic Celsius/Fahrenheit based on country
  • Geocoding: City name to coordinates resolution
  • User-Specific API Keys: Premium users can bring their own keys

📡 MCP Protocol Implementation

  • Complete MCP Server: Tools, resources, initialization endpoints
  • Tool Discovery: Dynamic tool listing with JSON schemas
  • Resource Access: User-scoped resources (profile, permissions)
  • Protocol Version: MCP 2024-11-05 specification

🌐 Web Test Client

  • Interactive UI: Test all MCP endpoints from browser
  • Token Management: JWT input and validation
  • Response Viewer: Formatted JSON with syntax highlighting
  • Tool Testing: Quick access to all implemented tools

🔧 Developer Experience

  • Swagger/OpenAPI: Auto-generated API documentation
  • Structured Logging: Debug-level logging for services
  • Configuration Flexibility: Environment-based settings
  • Demo Mode: Run without external dependencies

🏗️ Architecture

System Architecture

┌─────────────────────────────────────────────────────────────────┐
│                         Client Layer                            │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐         │
│  │   Browser    │  │  MCP Client  │  │  Mobile App  │         │
│  │  Test UI     │  │   (Claude    │  │              │         │
│  │              │  │   Desktop)   │  │              │         │
│  └──────────────┘  └──────────────┘  └──────────────┘         │
└────────────┬────────────────┬────────────────┬─────────────────┘
             │                │                │
             │  JWT Bearer    │  JWT Bearer    │  JWT Bearer
             │  Token         │  Token         │  Token
             │                │                │
┌────────────▼────────────────▼────────────────▼─────────────────┐
│              ASP.NET Core MCP Auth Server                       │
│                                                                  │
│  ┌────────────────────────────────────────────────────────┐   │
│  │               JWT Middleware (Program.cs)              │   │
│  │  • Validate token signature                            │   │
│  │  • Extract claims (sub, email, role, etc.)            │   │
│  │  • Set HttpContext.User                                │   │
│  └────────────────────────────────────────────────────────┘   │
│                              │                                  │
│  ┌───────────────────────────▼──────────────────────────────┐ │
│  │              Controller Layer                            │ │
│  │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │ │
│  │  │     MCP      │  │     Chat     │  │     User     │  │ │
│  │  │  Controller  │  │  Controller  │  │  Controller  │  │ │
│  │  │              │  │              │  │              │  │ │
│  │  │ • Initialize │  │ • Claude     │  │ • Profile    │  │ │
│  │  │ • Tools      │  │   Chat       │  │ • Claims     │  │ │
│  │  │ • Resources  │  │ • Tools      │  │ • API Keys   │  │ │
│  │  └──────────────┘  └──────────────┘  └──────────────┘  │ │
│  └─────────┬──────────────────┬──────────────────┬─────────┘ │
│            │                  │                  │            │
│  ┌─────────▼──────────────────▼──────────────────▼─────────┐ │
│  │                  Service Layer                           │ │
│  │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │ │
│  │  │   Weather    │  │    Claude    │  │  UserAPIKey  │  │ │
│  │  │   Service    │  │   Service    │  │   Service    │  │ │
│  │  │              │  │              │  │              │  │ │
│  │  │ • Geocoding  │  │ • Messages   │  │ • Key        │  │ │
│  │  │ • Weather    │  │ • Tool Use   │  │   Resolution │  │ │
│  │  │   Data       │  │ • Response   │  │ • Hierarchical│ │ │
│  │  │ • Unit Conv. │  │   Parsing    │  │   Fallback   │  │ │
│  │  └──────────────┘  └──────────────┘  └──────────────┘  │ │
│  └─────────┬──────────────────┬──────────────────┬─────────┘ │
└────────────┼──────────────────┼──────────────────┼───────────┘
             │                  │                  │
┌────────────▼─────┐  ┌─────────▼─────┐  ┌────────▼──────────┐
│   Open-Meteo     │  │   Anthropic   │  │  Identity Server  │
│   Weather API    │  │   Claude API  │  │                   │
│                  │  │               │  │  • Token Issuer   │
│  • Geocoding     │  │  • Messages   │  │  • User Store     │
│  • Forecast      │  │  • Tool Call  │  │  • OIDC/OAuth2    │
└──────────────────┘  └───────────────┘  └───────────────────┘

Data Flow: Weather Tool with User Context

1. User → MCP Client: "What's the weather in London?"

2. MCP Client → Server: POST /api/mcp/tools/call
   Headers: Authorization: Bearer <JWT>
   Body: { "name": "get_weather", "arguments": { "city": "London" } }

3. Server: JWT Middleware validates token, extracts claims
   • sub: "user123"
   • email: "john@company.com"
   • role: "premium"

4. McpController → WeatherService.GetWeatherAsync("London", "user123")

5. WeatherService: Resolve API key
   • Check Weather:UserApiKeys:user123 → not found
   • Check role "premium" → Weather:RoleApiKeys:Premium → found!
   • Use premium API key

6. WeatherService → Open-Meteo:
   a. Geocode "London" → {lat: 51.5074, lon: -0.1278, country: "UK"}
   b. Get weather → {temp: 18°C, humidity: 72%, clouds: 40%}

7. WeatherService: Determine unit (UK → Celsius)

8. WeatherService → McpController: WeatherData object

9. McpController: Format MCP response
   {
     "content": [{
       "type": "text",
       "text": "Hi john! 🌤️ Weather in London, UK\nTemp: 18°C..."
     }]
   }

10. Server → MCP Client: HTTP 200 with formatted weather

Key Architectural Patterns

1. Authentication Middleware Chain
Request → HTTPS Redirect → Static Files → Authentication → Authorization → Controllers
2. Service Layer Abstraction

All business logic lives in services implementing interfaces:

  • IWeatherService: Weather data retrieval
  • IClaudeService: AI interactions
  • IUserApiKeyService: Key management
3. Dependency Injection
// Program.cs
builder.Services.AddHttpClient<IWeatherService, WeatherService>();
builder.Services.AddHttpClient<IClaudeService, ClaudeService>();
builder.Services.AddSingleton<IUserApiKeyService, UserApiKeyService>();
4. Configuration-Driven API Keys
{
  "Weather": {
    "ApiKey": "default-key",                    // Fallback
    "UserApiKeys": { "user123": "user-key" },   // User-specific
    "RoleApiKeys": { "Premium": "premium-key" } // Role-based
  }
}

🚀 Quick Start

Prerequisites

  • .NET 8.0 SDK or later
  • Identity Server instance for JWT issuing (optional for demo mode)
  • Claude API Key from Anthropic (optional, demo mode available)

Installation

  1. Clone the repository

    git clone https://github.com/yourusername/dotnet-mcp-identityserver.git
    cd dotnet-mcp-identityserver
    
  2. Configure settings

    # Create development settings (gitignored)
    cp appsettings.json appsettings.Development.json
    
    # Edit with your values
    nano appsettings.Development.json
    
  3. Minimal configuration for demo

    {
      "IdentityServer": {
        "Authority": "https://your-identity-server.com",
        "Audience": "your-client-id",
        "RequireHttpsMetadata": false  // For local development
      },
      "Weather": {
        "ApiKey": "open-meteo"  // Free, no auth required
      },
      "Claude": {
        "ApiKey": "demo",  // Use demo mode
        "Model": "claude-3-5-sonnet-20241022"
      }
    }
    
  4. Build and run

    dotnet build
    dotnet run
    
  5. Access the application

First Test (Without Identity Server)

If you don't have an Identity Server, use the test endpoint:

# Get a test token (development only!)
curl https://localhost:7000/api/test/token

# Use the token
curl -H "Authorization: Bearer <token-from-above>" \
     https://localhost:7000/api/mcp/tools

⚙️ Configuration

Identity Server Setup

Configure your Identity Server in appsettings.json:

{
  "IdentityServer": {
    "Authority": "https://your-identity-server.com",
    "Audience": "mcp-server-api",
    "RequireHttpsMetadata": true
  }
}

Required Identity Server Configuration:

  • Client ID (Audience): mcp-server-api
  • Allowed Scopes: openid, profile, email
  • Token Type: JWT (not reference tokens)

Recommended Claims to Include:

  • sub: User identifier (required)
  • email: User email
  • name or firstName/lastName: Display name
  • role: User role (for role-based API keys)
  • companyId: Tenant/organization context (optional)

Weather API Configuration

Basic (Free Open-Meteo)
{
  "Weather": {
    "ApiKey": "open-meteo"
  }
}
Advanced (User-Specific Keys)
{
  "Weather": {
    "ApiKey": "open-meteo",           // Default fallback
    "PremiumApiKey": "premium-key",   // For premium users
    "UserApiKeys": {
      "user123": "user-specific-key", // Specific user
      "admin": "admin-key"
    },
    "PremiumUsers": {
      "user123": true                 // Mark as premium
    },
    "RoleApiKeys": {
      "Admin": "admin-tier-key",      // Role-based
      "Premium": "premium-tier-key"
    }
  }
}

Resolution Order:

  1. UserApiKeys:{userId} - User-specific key
  2. RoleApiKeys:{userRole} - Role-based key
  3. PremiumApiKey (if PremiumUsers:{userId} is true)
  4. ApiKey - Default fallback

Claude AI Configuration

{
  "Claude": {
    "ApiKey": "sk-ant-api03-...",  // Your Anthropic API key
    "Model": "claude-3-5-sonnet-20241022"
  }
}

Supported Models:

  • claude-3-5-sonnet-20241022 (recommended)
  • claude-3-opus-20240229
  • claude-3-sonnet-20240229
  • claude-3-haiku-20240307

Demo Mode: Set ApiKey: "demo" to use canned responses without API calls.

Logging Configuration

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "McpAuthServer.Services.ClaudeService": "Debug",
      "McpAuthServer.Services.WeatherService": "Debug"
    }
  }
}

🔧 Usage & Examples

Web Test Client

The built-in web client at https://localhost:7000 provides:

  1. JWT Token Input: Paste your token from Identity Server
  2. User Info: View extracted claims from token
  3. Tool Testing: Quick buttons for all MCP tools
  4. Resource Browser: View user-scoped resources
  5. Response Viewer: Formatted JSON output

API Endpoints

MCP Protocol Endpoints
EndpointMethodAuthDescription
/api/mcp/initializePOSTMCP protocol handshake
/api/mcp/toolsGETList available tools
/api/mcp/tools/callPOSTExecute a tool
/api/mcp/resourcesGETList user resources
/api/mcp/resources/readPOSTRead resource data
User Management Endpoints
EndpointMethodAuthDescription
/api/user/detailsGETUser profile and claims
/api/user/claimsGETAll JWT claims
/api/user/apikeysGETConfigured API keys
Chat Endpoints
EndpointMethodAuthDescription
/api/chat/completionsPOSTSimple Claude chat
/api/chat/toolsPOSTClaude with tool access

Example: Weather Tool

Request:

curl -X POST https://localhost:7000/api/mcp/tools/call \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "get_weather",
    "arguments": {
      "city": "London"
    }
  }'

Response:

{
  "content": [
    {
      "type": "text",
      "text": "Hi John! 🌤️ Here's the weather in London, United Kingdom\nTemperature: 18°C\nCondition: Partly cloudy\nHumidity: 72%\nPressure: 1013 hPa\nWind Speed: 5.5 m/s\nCloud Cover: 40%"
    }
  ]
}

Example: Claude with Tools

Request:

curl -X POST https://localhost:7000/api/mcp/tools/call \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "claude_with_tools",
    "arguments": {
      "message": "What is the weather like in Paris?"
    }
  }'

What Happens:

  1. Request sent to Claude with tool definitions
  2. Claude responds with tool_use for get_weather(city: "Paris")
  3. Server executes weather API call
  4. Results sent back to Claude
  5. Claude generates natural language response
  6. Final response returned to client

Response:

{
  "content": [
    {
      "type": "text",
      "text": "🧠 Claude with tools:\nThe weather in Paris is currently 22°C with clear skies. It's a beautiful day with 45% humidity and light winds at 3.2 m/s. Perfect weather for a walk along the Seine!"
    }
  ]
}

Example: User Resources

Request:

curl https://localhost:7000/api/mcp/resources \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Response:

{
  "resources": [
    {
      "uri": "user://profile",
      "name": "User Profile",
      "description": "Current user profile information"
    },
    {
      "uri": "user://permissions",
      "name": "User Permissions",
      "description": "Current user permissions and roles"
    }
  ]
}

Read Resource:

curl -X POST https://localhost:7000/api/mcp/resources/read \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "uri": "user://profile" }'

🧠 Technical Decisions

Why ASP.NET Core?

Decision: Use ASP.NET Core instead of Python FastAPI or Node.js Express

Rationale:

  • Enterprise Ready: Built-in JWT authentication, DI, configuration
  • Performance: Faster than Python/Node for I/O-bound workloads
  • Type Safety: C# strong typing prevents runtime errors
  • Tooling: Excellent IDE support (Visual Studio, Rider, VS Code)
  • .NET 8 Features: Native AOT, improved async, minimal APIs

Trade-offs: Larger initial footprint, fewer MCP examples in C#

Why Identity Server for Auth?

Decision: Use external Identity Server (OAuth2/OIDC) instead of built-in auth

Rationale:

  • Enterprise Standard: Most companies already have Identity Server/Azure AD/Okta
  • Centralized Auth: Single source of truth for users
  • Production Ready: Battle-tested token validation
  • Flexibility: Works with any OIDC-compliant provider

Trade-offs: Requires external service, more complex local setup

Why Open-Meteo for Weather?

Decision: Use Open-Meteo API instead of OpenWeatherMap/WeatherAPI

Rationale:

  • No API Key Required: Free tier doesn't need registration
  • Good Coverage: Global weather data
  • No Rate Limits: Generous free usage
  • Simple API: Easy to demonstrate concepts

Trade-offs: Less detailed than paid services, no historical data

Why User-Specific API Keys?

Decision: Implement hierarchical API key resolution (user → role → default)

Rationale:

  • Multi-Tenancy: Different customers can use their own keys
  • Cost Attribution: Track API usage per user/department
  • Rate Limits: Avoid hitting global rate limits
  • Premium Tiers: Easy to offer paid features

Implementation: WeatherService.GetApiKeyForUser() checks config hierarchy

Why Demo Mode?

Decision: Support "demo" API key for testing without external services

Rationale:

  • Developer Experience: Run project immediately after clone
  • CI/CD Testing: No secrets needed for tests
  • Demonstrations: Show architecture without API setup

Implementation: Services detect ApiKey: "demo" and return canned data

JWT Validation Settings

Decision: Relaxed validation in Program.cs (ValidateIssuerSigningKey=false)

Rationale:

  • Development Ease: Some Identity Server setups have certificate issues
  • Flexibility: Works with various token issuers
  • Clear Warning: Code comments explain this is for development

⚠️ Production: Set ValidateIssuerSigningKey: true and RequireSignedTokens: true

Service Layer Pattern

Decision: Separate controllers from business logic via service interfaces

Rationale:

  • Testability: Easy to mock services in tests
  • Reusability: Services used by multiple controllers
  • Separation of Concerns: HTTP logic vs business logic
  • Dependency Injection: Lifetime management (Singleton/Scoped)

Example:

public interface IWeatherService {
    Task<WeatherData?> GetWeatherAsync(string city, string? userId);
}

Temperature Unit Localization

Decision: Automatically use Fahrenheit for US, Celsius elsewhere

Rationale:

  • User Experience: Show familiar units automatically
  • No User Input: Infer from location, not explicit setting
  • Simple Logic: Country-based lookup (GetTemperatureUnit())

Implementation: Open-Meteo geocoding returns country, lookup table maps to unit

Structured Logging

Decision: Use ILogger with structured parameters, debug level for services

Rationale:

  • Debugging: Trace API calls and responses
  • Production: Info level for high-level operations
  • Searchable: Structured logs work with Seq/ELK/Azure Monitor

Example:

_logger.LogInformation("Weather request for {City}, user {UserId}", city, userId);

🧪 Development

Project Structure

dotnet-mcp-identityserver/
├── Controllers/                  # API Controllers
│   ├── McpController.cs         # MCP protocol (tools, resources)
│   ├── ChatController.cs        # Claude chat endpoints
│   ├── UserController.cs        # User info, claims, API keys
│   ├── UserApiKeyController.cs  # API key management
│   ├── WeatherTestController.cs # Weather testing
│   ├── AuthController.cs        # Auth helpers
│   └── TestController.cs        # Test token generation
├── Services/                     # Business Logic
│   ├── WeatherService.cs        # Open-Meteo integration
│   ├── ClaudeService.cs         # Anthropic Claude SDK
│   └── UserApiKeyService.cs     # API key resolution
├── wwwroot/                      # Static Files
│   ├── index.html               # Test client UI
│   └── mcp-client.js            # Client-side JavaScript
├── Program.cs                    # App startup & middleware
├── McpAuthServer.csproj         # Project file
├── appsettings.json             # Configuration (template)
├── appsettings.Development.json # Local settings (gitignored)
├── README.md                     # This file
├── CLAUDE.md                     # AI assistant guidance
└── CONTRIBUTING.md              # Contribution guidelines

Running Tests

# Run all tests
dotnet test

# Run with detailed output
dotnet test --logger "console;verbosity=detailed"

# Run with code coverage
dotnet test --collect:"XPlat Code Coverage"

# Generate coverage report (requires reportgenerator)
reportgenerator -reports:**/coverage.cobertura.xml -targetdir:coverage

Building for Production

# Optimized release build
dotnet publish -c Release -o ./publish

# Self-contained deployment (includes .NET runtime)
dotnet publish -c Release -r linux-x64 --self-contained

# Single-file executable
dotnet publish -c Release -r linux-x64 -p:PublishSingleFile=true

Docker Deployment

Dockerfile:

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["McpAuthServer.csproj", "."]
RUN dotnet restore
COPY . .
RUN dotnet build -c Release -o /app/build

FROM build AS publish
RUN dotnet publish -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "McpAuthServer.dll"]

Build and run:

# Build image
docker build -t mcp-auth-server:latest .

# Run container
docker run -d -p 8080:80 -p 8081:443 \
  -e IdentityServer__Authority=https://your-ids.com \
  -e IdentityServer__Audience=mcp-api \
  -e Claude__ApiKey=$CLAUDE_API_KEY \
  --name mcp-server \
  mcp-auth-server:latest

# View logs
docker logs -f mcp-server

Environment Variables

Override configuration via environment variables (Docker/Kubernetes):

# Format: Section__Property
export IdentityServer__Authority="https://ids.company.com"
export IdentityServer__Audience="mcp-api"
export IdentityServer__RequireHttpsMetadata="true"
export Claude__ApiKey="sk-ant-..."
export Weather__ApiKey="open-meteo"

Adding a New Tool

  1. Define tool in McpController.GetTools()

    new {
        name = "get_stock_price",
        description = "Get current stock price",
        inputSchema = new {
            type = "object",
            properties = new {
                symbol = new { type = "string", description = "Stock ticker" }
            },
            required = new[] { "symbol" }
        }
    }
    
  2. Add case to McpController.CallTool()

    "get_stock_price" => await HandleStockPriceTool(request.Arguments)
    
  3. Implement handler

    private async Task<IActionResult> HandleStockPriceTool(object? arguments) {
        var symbol = ExtractStringFromArguments(arguments, "symbol");
        var price = await _stockService.GetPriceAsync(symbol);
        return Ok(new {
            content = new[] {
                new { type = "text", text = $"${symbol}: ${price}" }
            }
        });
    }
    
  4. Create service (optional)

    public interface IStockService {
        Task<decimal> GetPriceAsync(string symbol);
    }
    
  5. Register service in Program.cs

    builder.Services.AddHttpClient<IStockService, StockService>();
    

Debugging Tips

Enable detailed logging:

{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "Microsoft.AspNetCore": "Debug"
    }
  }
}

View JWT claims:

curl https://localhost:7000/api/user/claims \
  -H "Authorization: Bearer YOUR_TOKEN"

Test without auth: Use [AllowAnonymous] attribute on controller during development:

[AllowAnonymous]  // Remove before production!
public class TestController : ControllerBase { }

View Swagger docs: Navigate to https://localhost:7000/swagger


🤝 Contributing

We welcome contributions! See for guidelines.

Quick Contribution Guide

  1. Fork the repository
  2. Create a branch: git checkout -b feature/amazing-tool
  3. Make changes with tests and documentation
  4. Commit: git commit -m "feat: add stock price tool"
  5. Push: git push origin feature/amazing-tool
  6. Open PR with description of changes

Code Style

  • Follow standard C# conventions (PascalCase for public members)
  • Add XML documentation to public APIs
  • Use dependency injection for all services
  • Write unit tests for business logic
  • Keep controllers thin (business logic in services)

📄 License

This project is licensed under the MIT License - see the file for details.


🙏 Acknowledgments


📚 Additional Resources


📞 Support


Star this repository if you find it helpful!

🔗 Share with others building AI applications with enterprise auth!