-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathcreate_auth_test.sh
More file actions
executable file
·320 lines (272 loc) · 9.79 KB
/
create_auth_test.sh
File metadata and controls
executable file
·320 lines (272 loc) · 9.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
#!/bin/bash
# Quick setup script for FastApps OAuth testing
# This creates a test project with auth-enabled widgets
set -e
echo "========================================="
echo "FastApps OAuth Testing - Quick Setup"
echo "========================================="
echo ""
# Check if we're in the right directory
if [ ! -f "setup.py" ]; then
echo "❌ Error: Please run this script from the FastApps directory"
exit 1
fi
# Prompt for Auth0 credentials
echo "📝 Enter your Auth0 configuration:"
echo "(You can get these from https://auth0.com dashboard)"
echo ""
read -p "Auth0 Domain (e.g., your-tenant.us.auth0.com): " AUTH0_DOMAIN
read -p "API Identifier/Audience (e.g., https://fastapps-test.example.com): " AUTH0_AUDIENCE
if [ -z "$AUTH0_DOMAIN" ] || [ -z "$AUTH0_AUDIENCE" ]; then
echo "❌ Error: Both Auth0 domain and API identifier are required"
exit 1
fi
# Create test directory
TEST_DIR="../fastapps-auth-test"
echo ""
echo "📁 Creating test project in: $TEST_DIR"
mkdir -p "$TEST_DIR"
cd "$TEST_DIR"
# Create virtual environment
echo "🐍 Creating Python virtual environment..."
python3 -m venv venv
source venv/bin/activate
# Install FastApps
echo "📦 Installing FastApps..."
# Detect if uv is available
if command -v uv &> /dev/null; then
echo "Using uv for package installation..."
uv pip install -q -e ../FastApps
uv pip install -q httpx PyJWT cryptography
else
echo "Using pip for package installation..."
pip install -q --upgrade pip
pip install -q -e ../FastApps
pip install -q httpx PyJWT cryptography
fi
# Initialize project
echo "🏗️ Initializing FastApps project..."
fastapps init auth-test-widgets
cd auth-test-widgets
# Install JS dependencies
echo "📦 Installing JavaScript dependencies..."
npm install --silent
# Create test widgets
echo "🎨 Creating test widgets..."
# 1. Protected widget
fastapps create protected-widget
cat > server/tools/protected_widget_tool.py << 'EOF'
from fastapps import BaseWidget, ConfigDict, auth_required, UserContext
from pydantic import BaseModel
from typing import Dict, Any
class ProtectedWidgetInput(BaseModel):
model_config = ConfigDict(populate_by_name=True)
message: str = "Hello"
@auth_required(scopes=["user", "read:data"])
class ProtectedWidgetTool(BaseWidget):
identifier = "protected-widget"
title = "Protected Widget"
description = "Requires authentication with user and read:data scopes"
input_schema = ProtectedWidgetInput
invoking = "Loading protected widget..."
invoked = "Protected widget loaded!"
widget_csp = {
"connect_domains": [],
"resource_domains": []
}
async def execute(self, input_data: ProtectedWidgetInput, context=None, user: UserContext = None) -> Dict[str, Any]:
return {
"type": "protected",
"message": f"Hello, {user.claims.get('name', 'User')}!",
"user_id": user.subject,
"email": user.claims.get('email', 'N/A'),
"scopes": user.scopes,
"permissions": {
"read": user.has_scope("read:data"),
"write": user.has_scope("write:data"),
"admin": user.has_scope("admin"),
},
"input": input_data.message,
}
EOF
# 2. Public widget
fastapps create public-widget
cat > server/tools/public_widget_tool.py << 'EOF'
from fastapps import BaseWidget, ConfigDict, no_auth
from pydantic import BaseModel
from typing import Dict, Any
class PublicWidgetInput(BaseModel):
model_config = ConfigDict(populate_by_name=True)
@no_auth
class PublicWidgetTool(BaseWidget):
identifier = "public-widget"
title = "Public Widget"
description = "No authentication required - public access"
input_schema = PublicWidgetInput
invoking = "Loading public widget..."
invoked = "Public widget loaded!"
widget_csp = {
"connect_domains": [],
"resource_domains": []
}
async def execute(self, input_data: PublicWidgetInput, context=None, user=None) -> Dict[str, Any]:
return {
"type": "public",
"message": "This is public content - no auth required",
"authenticated": user.is_authenticated if user else False,
"access_level": "public",
}
EOF
# 3. Flexible widget
fastapps create flexible-widget
cat > server/tools/flexible_widget_tool.py << 'EOF'
from fastapps import BaseWidget, ConfigDict, optional_auth, UserContext
from pydantic import BaseModel
from typing import Dict, Any
class FlexibleWidgetInput(BaseModel):
model_config = ConfigDict(populate_by_name=True)
@optional_auth(scopes=["user"])
class FlexibleWidgetTool(BaseWidget):
identifier = "flexible-widget"
title = "Flexible Widget"
description = "Works for both authenticated and anonymous users"
input_schema = FlexibleWidgetInput
invoking = "Loading flexible widget..."
invoked = "Flexible widget loaded!"
widget_csp = {
"connect_domains": [],
"resource_domains": []
}
async def execute(self, input_data: FlexibleWidgetInput, context=None, user: UserContext = None) -> Dict[str, Any]:
if user and user.is_authenticated:
return {
"type": "flexible",
"tier": "premium",
"message": f"Welcome back, {user.claims.get('name', 'User')}!",
"user_id": user.subject,
"features": ["basic", "advanced", "export", "share"],
"personalized": True,
}
return {
"type": "flexible",
"tier": "free",
"message": "Welcome! Sign in to unlock premium features.",
"features": ["basic"],
"personalized": False,
}
EOF
# Update server/main.py with Auth0 config
cat > server/main.py << EOF
from pathlib import Path
import sys
import importlib
import inspect
sys.path.insert(0, str(Path(__file__).parent.parent))
from fastapps import WidgetBuilder, WidgetMCPServer, BaseWidget
import uvicorn
PROJECT_ROOT = Path(__file__).parent.parent
TOOLS_DIR = Path(__file__).parent / "tools"
def auto_load_tools(build_results):
tools = []
for tool_file in TOOLS_DIR.glob("*_tool.py"):
module_name = tool_file.stem
try:
module = importlib.import_module(f"server.tools.{module_name}")
for name, obj in inspect.getmembers(module, inspect.isclass):
if issubclass(obj, BaseWidget) and obj is not BaseWidget:
tool_identifier = obj.identifier
if tool_identifier in build_results:
tool_instance = obj(build_results[tool_identifier])
tools.append(tool_instance)
print(f"✓ {name}")
else:
print(f"⚠ No build: {tool_identifier}")
except Exception as e:
print(f"✗ Error: {tool_file.name}: {e}")
return tools
# Build widgets
builder = WidgetBuilder(PROJECT_ROOT)
build_results = builder.build_all()
tools = auto_load_tools(build_results)
# Auth0 Configuration
AUTH0_DOMAIN = "${AUTH0_DOMAIN}"
AUTH0_AUDIENCE = "${AUTH0_AUDIENCE}"
PUBLIC_URL = "https://REPLACE-ME.trycloudflare.com" # Will be auto-generated by Cloudflare Tunnel
# Create server with OAuth
server = WidgetMCPServer(
name="fastapps-auth-test",
widgets=tools,
auth_issuer_url=f"https://{AUTH0_DOMAIN}",
auth_resource_server_url=f"{PUBLIC_URL}/mcp",
auth_audience=AUTH0_AUDIENCE,
auth_required_scopes=["user"],
)
app = server.get_app()
if __name__ == "__main__":
print()
print("=" * 60)
print("FastApps OAuth Test Server")
print("=" * 60)
print(f"Auth Provider: {AUTH0_DOMAIN}")
print(f"Widgets: {len(tools)}")
print(f"Port: 8001")
print()
print("⚠️ IMPORTANT: Use 'fastapps dev' to start with Cloudflare Tunnel")
print(" OR manually start cloudflared:")
print(" cloudflared tunnel --url http://localhost:8001")
print()
print("📝 After getting Cloudflare URL, update PUBLIC_URL in server/main.py")
print(" (e.g., https://random-name.trycloudflare.com)")
print()
print("✨ Test Widgets:")
for tool in tools:
auth_type = getattr(tool, '_auth_required', None)
symbol = "🔒" if auth_type is True else "🌐" if auth_type is False else "🔓"
print(f" {symbol} {tool.title}")
print()
print("=" * 60)
print()
uvicorn.run(app, host="0.0.0.0", port=8001)
EOF
# Build widgets
echo "🔨 Building widgets..."
npm run build > /dev/null 2>&1
echo ""
echo "========================================="
echo "✅ Setup Complete!"
echo "========================================="
echo ""
echo "📂 Project created at: $(pwd)"
echo ""
echo "🚀 Next Steps:"
echo ""
echo "Option A - Using fastapps dev (Recommended):"
echo " cd $(pwd)"
echo " source venv/bin/activate"
echo " fastapps dev"
echo " # Cloudflare Tunnel URL will be displayed automatically"
echo ""
echo "Option B - Manual setup:"
echo "1. Start the server (Terminal 1):"
echo " cd $(pwd)"
echo " source venv/bin/activate"
echo " python server/main.py"
echo ""
echo "2. Start Cloudflare Tunnel (Terminal 2):"
echo " cloudflared tunnel --url http://localhost:8001"
echo ""
echo "3. Copy Cloudflare URL and update server/main.py:"
echo " PUBLIC_URL = \"https://random-name.trycloudflare.com\""
echo ""
echo "4. Restart server and test in ChatGPT:"
echo " Settings → Connectors → Add Connector"
echo " URL: https://random-name.trycloudflare.com/mcp"
echo ""
echo "📖 Full guide: ../FastApps/OAUTH_TESTING_GUIDE.md"
echo ""
echo "✨ Test widgets created:"
echo " 🔒 protected-widget - Requires auth (user + read:data scopes)"
echo " 🌐 public-widget - No auth required"
echo " 🔓 flexible-widget - Optional auth (premium vs free)"
echo ""
echo "========================================="