python docs, AI bot rough start

This commit is contained in:
Madison 2025-07-01 16:01:44 -05:00
parent 445369fe42
commit de707ad953
11 changed files with 1055 additions and 452 deletions

View File

@ -18,6 +18,9 @@
"clear-docs": "node scripts/clear-docs.js"
},
"dependencies": {
"@ai-sdk/google": "^1.2.21",
"@ai-sdk/openai": "^1.3.22",
"@ai-sdk/react": "^1.2.12",
"@radix-ui/react-collapsible": "^1.1.11",
"@radix-ui/react-popover": "^1.1.14",
"@radix-ui/react-presence": "^1.1.4",
@ -26,6 +29,7 @@
"@radix-ui/react-tabs": "^1.1.12",
"@stackframe/stack": "workspace:^",
"@xyflow/react": "^12.6.4",
"ai": "^4.3.16",
"class-variance-authority": "^0.7.1",
"fumadocs-core": "15.3.3",
"fumadocs-mdx": "11.6.4",
@ -45,7 +49,8 @@
"remark-gfm": "^4.0.1",
"remark-mdx": "^3.1.0",
"shiki": "^3.4.2",
"tailwind-merge": "^3.3.0"
"tailwind-merge": "^3.3.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.7",

View File

@ -1,3 +1,4 @@
import AIChat from '@/components/chat/ai-chat';
import DocsSelector from '@/components/homepage/iconHover';
export default function HomePage() {
@ -34,6 +35,11 @@ export default function HomePage() {
<div className="mb-6">
<DocsSelector />
</div>
{/* AI Chat Assistant */}
<div className="mb-6">
<AIChat />
</div>
</div>
</section>
</main>

View File

@ -0,0 +1,119 @@
import { createGoogleGenerativeAI } from '@ai-sdk/google';
import { streamText } from 'ai';
// Allow streaming responses up to 30 seconds
export const maxDuration = 30;
// Configure Google Gemini with custom API key variable
const google = createGoogleGenerativeAI({
apiKey: process.env.GOOGLE_AI_API_KEY,
});
export function errorHandler(error: unknown) {
if (error == null) {
return 'unknown error';
}
if (typeof error === 'string') {
return error;
}
if (error instanceof Error) {
return error.message;
}
return JSON.stringify(error);
}
async function getStackAuthDocs() {
try {
// Get the base URL from the request or use localhost for development
const baseUrl = process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}`
: 'http://localhost:8104';
console.log('Fetching docs from:', `${baseUrl}/llms.txt`);
const response = await fetch(`${baseUrl}/llms.txt`);
console.log('Docs fetch response status:', response.status);
if (!response.ok) {
console.error('Failed to fetch Stack Auth docs:', response.status, response.statusText);
return null;
}
const docsContent = await response.text();
console.log('Docs content length:', docsContent?.length || 0);
console.log('Docs content preview:', docsContent?.substring(0, 200) + '...');
return docsContent;
} catch (error) {
console.error('Error fetching Stack Auth docs:', error);
return null;
}
}
export async function POST(req: Request) {
try {
const { messages } = await req.json();
console.log('Received messages:', messages);
console.log('Google AI API Key configured:', !!process.env.GOOGLE_AI_API_KEY);
// Fetch Stack Auth documentation
const stackAuthDocs = await getStackAuthDocs();
// Create system message with documentation context
const systemMessage = {
role: 'system' as const,
content: `You are a technical AI assistant specializing in Stack Auth, a complete authentication solution. You are helping developers who want detailed, technical guidance.
IMPORTANT INSTRUCTIONS:
- You can ONLY answer questions about Stack Auth and authentication topics
- If someone asks about anything else, politely redirect them to ask about Stack Auth
- Your audience is DEVELOPERS who need in-depth technical information
- Provide comprehensive, detailed answers with code examples when available
- Include specific implementation details, configuration options, and best practices
- Reference exact function names, parameters, and code snippets from the documentation
- Don't oversimplify - developers want the full technical depth
- When explaining concepts, include relevant code examples and implementation details
- If there are multiple approaches, explain the different options and their trade-offs
${stackAuthDocs ? `
Here is the complete Stack Auth documentation with detailed examples and technical information:
${stackAuthDocs}
Use this documentation to provide comprehensive, technical answers. Include code examples, configuration details, and implementation specifics. Developers are looking for actionable, detailed guidance, not basic overviews.
` : 'Stack Auth documentation could not be loaded. Please answer based on general Stack Auth knowledge, but provide detailed technical information for developers.'}
Remember: Your responses should match the technical depth and detail level of the Stack Auth documentation. Provide code examples, configuration snippets, and comprehensive implementation guidance.`
};
// Prepend system message to the conversation
const messagesWithContext = [systemMessage, ...messages];
const result = streamText({
model: google('gemini-1.5-flash'),
messages: messagesWithContext,
});
return result.toDataStreamResponse({
getErrorMessage: errorHandler,
});
} catch (error) {
console.error('Error in chat API:', error);
return new Response(
JSON.stringify({
error: 'Internal server error',
details: error instanceof Error ? error.message : 'Unknown error'
}),
{
status: 500,
headers: {
'Content-Type': 'application/json',
},
}
);
}
}

View File

@ -0,0 +1,120 @@
'use client';
import { useChat } from '@ai-sdk/react';
import { Bot, Send, User } from 'lucide-react';
import { useState } from 'react';
export default function AIChat() {
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat();
const [isOpen, setIsOpen] = useState(false);
// Debug logging
console.log('Messages:', messages);
return (
<div className="w-full max-w-4xl mx-auto">
{/* Toggle Button */}
<div className="flex justify-center mb-8">
<button
onClick={() => setIsOpen(!isOpen)}
className="flex items-center gap-2 px-6 py-3 bg-primary text-primary-foreground rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 hover:-translate-y-1 font-semibold"
>
<Bot size={20} />
{isOpen ? 'Hide AI Assistant' : 'Ask AI Assistant'}
</button>
</div>
{/* Chat Interface */}
{isOpen && (
<div className="bg-card border border-border rounded-xl shadow-lg p-6 mb-8">
<div className="flex items-center gap-2 mb-4 pb-4 border-b border-border">
<Bot size={24} className="text-primary" />
<h3 className="text-lg font-semibold">Stack Auth AI Assistant</h3>
<span className="text-sm text-muted-foreground ml-auto">
Ask questions about Stack Auth documentation
</span>
</div>
{/* Messages Container */}
<div className="h-96 overflow-y-auto mb-4 space-y-4 scroll-smooth">
{messages.length === 0 && (
<div className="text-center text-muted-foreground py-8">
<Bot size={48} className="mx-auto mb-4 opacity-50" />
<p className="text-lg font-medium mb-2">Welcome to Stack Auth!</p>
<p className="text-sm">
Ask me anything about authentication, documentation, or how to get started.
</p>
</div>
)}
{messages.map(message => (
<div
key={message.id}
className={`flex gap-3 ${
message.role === 'user' ? 'justify-end' : 'justify-start'
}`}
>
{message.role === 'assistant' && (
<div className="w-8 h-8 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0 mt-1">
<Bot size={16} className="text-primary" />
</div>
)}
<div
className={`max-w-[80%] rounded-xl px-4 py-3 ${
message.role === 'user'
? 'bg-primary text-primary-foreground ml-auto'
: 'bg-muted'
}`}
>
<div className="whitespace-pre-wrap text-sm leading-relaxed">
{message.content}
</div>
</div>
{message.role === 'user' && (
<div className="w-8 h-8 rounded-full bg-muted flex items-center justify-center flex-shrink-0 mt-1">
<User size={16} />
</div>
)}
</div>
))}
{isLoading && (
<div className="flex gap-3 justify-start">
<div className="w-8 h-8 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0 mt-1">
<Bot size={16} className="text-primary" />
</div>
<div className="bg-muted rounded-xl px-4 py-3">
<div className="flex items-center gap-1">
<div className="w-2 h-2 bg-muted-foreground/50 rounded-full animate-bounce" />
<div className="w-2 h-2 bg-muted-foreground/50 rounded-full animate-bounce" style={{ animationDelay: '0.1s' }} />
<div className="w-2 h-2 bg-muted-foreground/50 rounded-full animate-bounce" style={{ animationDelay: '0.2s' }} />
</div>
</div>
</div>
)}
</div>
{/* Input Form */}
<form onSubmit={handleSubmit} className="flex gap-2">
<input
className="flex-1 px-4 py-3 bg-background border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent placeholder:text-muted-foreground"
value={input}
placeholder="Ask about Stack Auth documentation..."
onChange={handleInputChange}
disabled={isLoading}
/>
<button
type="submit"
disabled={isLoading || !input.trim()}
className="px-4 py-3 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex items-center justify-center"
>
<Send size={16} />
</button>
</form>
</div>
)}
</div>
);
}

View File

@ -227,259 +227,74 @@ const DocsIcon3D: React.FC<DocsIcon3DProps> = ({
}
};
// Add custom CSS for the floating animation
const floatingDotsStyle = `
@keyframes float-up {
0% {
transform: translateY(0px) scale(1);
opacity: 0.8;
filter: blur(0px);
}
50% {
opacity: 0.6;
filter: blur(0.5px);
}
100% {
transform: translateY(-200px) scale(0.3);
opacity: 0;
filter: blur(1px);
}
}
.animate-float-up {
animation: float-up 3s ease-out infinite;
}
`;
return (
<>
<style dangerouslySetInnerHTML={{ __html: floatingDotsStyle }} />
<div className="flex justify-center items-center p-4">
<div
className={`
grid gap-4 max-w-4xl w-full justify-center
${platformSections.length === 1 ? 'grid-cols-1 max-w-xs' : ''}
${platformSections.length === 2 ? 'grid-cols-1 md:grid-cols-2 max-w-lg' : ''}
${platformSections.length === 3 ? 'grid-cols-1 md:grid-cols-3 max-w-2xl' : ''}
${platformSections.length === 4 ? 'grid-cols-2 md:grid-cols-4' : ''}
`}
>
{platformSections.map((section) => (
<div className="flex justify-center items-center p-4">
<div
className={`
grid gap-6 max-w-4xl w-full justify-center
${platformSections.length === 1 ? 'grid-cols-1 max-w-xs' : ''}
${platformSections.length === 2 ? 'grid-cols-1 md:grid-cols-2 max-w-lg' : ''}
${platformSections.length === 3 ? 'grid-cols-1 md:grid-cols-3 max-w-2xl' : ''}
${platformSections.length === 4 ? 'grid-cols-2 md:grid-cols-4' : ''}
`}
>
{platformSections.map((section) => (
<div
key={section.id}
className="cursor-pointer group transform transition-all duration-200 hover:scale-105"
onMouseEnter={() => setHoveredSection(section.id)}
onMouseLeave={() => setHoveredSection(null)}
onClick={() => handleSectionClick(section)}
>
<div
key={section.id}
className={`
relative cursor-pointer group
transform transition-all duration-500 ease-out
hover:scale-105 hover:-translate-y-2 hover:rotate-1
active:scale-95 active:rotate-0
`}
onMouseEnter={() => setHoveredSection(section.id)}
onMouseLeave={() => setHoveredSection(null)}
onClick={() => handleSectionClick(section)}
>
<div
className={`
relative bg-gradient-to-br from-background via-background to-muted/20
border-2 border-border rounded-2xl p-4 w-full h-44
bg-card border-[0.5px] border-border rounded-xl p-6 w-full h-40
flex flex-col items-center justify-center
shadow-xl hover:shadow-2xl
overflow-hidden backdrop-blur-sm
transition-all duration-500 ease-out
before:absolute before:inset-0 before:bg-gradient-to-br before:opacity-0
hover:before:opacity-100 before:transition-opacity before:duration-500
${hoveredSection === section.id ? "border-opacity-100 shadow-2xl" : "border-opacity-50"}
shadow-sm hover:shadow-lg
transition-all duration-200
`}
style={{
transformStyle: "preserve-3d",
perspective: "1000px",
borderColor: hoveredSection === section.id ? section.color : undefined,
boxShadow:
hoveredSection === section.id
? `0 25px 50px -12px ${section.color}40, 0 0 0 1px ${section.color}20`
: undefined,
}}
>
{/* Animated background gradient */}
style={{
borderColor: hoveredSection === section.id ? section.color : undefined,
}}
>
{/* Icon Container */}
<div className="mb-4">
<div
className={`
absolute inset-0 rounded-2xl transition-opacity duration-500
${hoveredSection === section.id ? "opacity-100" : "opacity-0"}
`}
w-12 h-12 rounded-lg flex items-center justify-center
transition-all duration-200
`}
style={{
background: `
radial-gradient(circle at 30% 20%, ${section.color}15 0%, transparent 50%),
linear-gradient(135deg, ${section.color}08, ${section.color}03, transparent)
`,
}}
/>
{/* Continuous upward floating dots effect */}
<div className="absolute inset-0 overflow-hidden rounded-2xl">
{hoveredSection === section.id && (
<div className="absolute inset-0">
{[...Array(12)].map((_, i) => (
<div
key={`${section.id}-${i}`}
className="absolute w-1.5 h-1.5 rounded-full animate-float-up"
style={{
backgroundColor: section.color,
left: `${10 + Math.random() * 80}%`,
bottom: "-6px",
animationDelay: `${i * 0.3}s`,
animationDuration: "3s",
animationIterationCount: "infinite",
animationTimingFunction: "ease-out",
}}
/>
))}
</div>
)}
</div>
{/* 3D Icon Container */}
<div
className={`
relative mb-4 transition-all duration-700 ease-out
${
hoveredSection === section.id
? "transform -rotate-y-12 rotate-x-6 translate-z-8"
: "transform rotate-0"
}
`}
style={{
transformStyle: "preserve-3d",
backgroundColor: hoveredSection === section.id ? section.color : `${section.color}20`,
color: hoveredSection === section.id ? 'white' : 'hsl(var(--foreground))',
}}
>
{/* Enhanced shadow layers */}
<div
className={`
absolute inset-0 rounded-xl transition-all duration-500
${hoveredSection === section.id ? "opacity-50 blur-sm" : "opacity-20"}
`}
style={{
backgroundColor: section.color,
transform: "translateZ(-6px) translateX(3px) translateY(3px)",
}}
/>
{/* Main icon with enhanced styling */}
<div
className={`
relative z-10 w-16 h-16 rounded-xl flex items-center justify-center
border-2 transition-all duration-500 ease-out
${
hoveredSection === section.id
? "text-white border-transparent scale-110 rotate-3"
: "text-foreground border-border bg-background scale-100"
}
`}
style={{
backgroundColor: hoveredSection === section.id ? section.color : undefined,
boxShadow:
hoveredSection === section.id
? `0 12px 40px ${section.color}60, inset 0 1px 0 rgba(255,255,255,0.2)`
: `0 6px 25px ${section.color}30`,
}}
>
<div
className={`
transition-all duration-500 ease-out
${hoveredSection === section.id ? "scale-125 rotate-6" : "scale-100"}
`}
>
{React.cloneElement(section.icon as React.ReactElement, {
size: 24,
strokeWidth: hoveredSection === section.id ? 2.5 : 2,
})}
</div>
</div>
</div>
{/* Enhanced Title */}
<h3
className={`
text-lg font-bold mb-2 text-center transition-all duration-500
${hoveredSection === section.id ? "scale-105 font-extrabold" : "scale-100"}
`}
style={{
color: hoveredSection === section.id ? section.color : undefined,
textShadow: hoveredSection === section.id ? `0 0 20px ${section.color}40` : undefined,
}}
>
{section.title}
</h3>
{/* Enhanced Description */}
<p
className={`
text-xs text-center leading-relaxed px-2 transition-all duration-500
${
hoveredSection === section.id
? "text-muted-foreground opacity-100 scale-105"
: "text-muted-foreground opacity-70"
}
`}
>
{section.description}
</p>
{/* Enhanced hover indicator */}
<div
className={`
absolute bottom-0 left-0 right-0 h-1.5 rounded-b-2xl
transition-all duration-500 ease-out
${hoveredSection === section.id ? "scale-x-100" : "scale-x-0"}
`}
style={{
transformOrigin: "left",
backgroundColor: section.color,
boxShadow: hoveredSection === section.id ? `0 0 20px ${section.color}60` : undefined,
}}
/>
{/* Enhanced corner accents with glow */}
<div
className={`
absolute top-3 right-3 w-2 h-2 rounded-full
transition-all duration-500 ease-out
${hoveredSection === section.id ? "scale-150 opacity-100" : "scale-75 opacity-30"}
`}
style={{
backgroundColor: section.color,
boxShadow: hoveredSection === section.id ? `0 0 15px ${section.color}80` : undefined,
}}
/>
<div
className={`
absolute bottom-3 left-3 w-2 h-2 rounded-full
transition-all duration-500 ease-out
${hoveredSection === section.id ? "scale-150 opacity-100" : "scale-75 opacity-30"}
`}
style={{
backgroundColor: section.color,
boxShadow: hoveredSection === section.id ? `0 0 15px ${section.color}80` : undefined,
}}
/>
{/* New: Diagonal accent line */}
<div
className={`
absolute top-0 right-0 w-16 h-16 overflow-hidden rounded-tr-2xl
transition-all duration-500
${hoveredSection === section.id ? "opacity-100" : "opacity-0"}
`}
>
<div
className="absolute top-0 right-0 w-full h-0.5 origin-top-right rotate-45 translate-x-2 translate-y-4"
style={{ backgroundColor: section.color }}
/>
{React.cloneElement(section.icon as React.ReactElement, {
size: 20,
strokeWidth: 2,
})}
</div>
</div>
{/* Title */}
<h3
className="text-sm font-semibold mb-2 text-center transition-all duration-200"
style={{
color: hoveredSection === section.id ? section.color : 'hsl(var(--card-foreground))',
}}
>
{section.title}
</h3>
{/* Description */}
<p className="text-xs text-center text-muted-foreground leading-relaxed px-2 opacity-80">
{section.description}
</p>
</div>
))}
</div>
</div>
))}
</div>
</>
</div>
);
};

View File

@ -1,6 +1,6 @@
'use client';
import { Code, FileText, Link as LinkIcon, Play, Puzzle, Settings, Shield, UserCheck } from 'lucide-react';
import { Code, FileText, Link as LinkIcon, Play, Puzzle, Settings, Shield, User, UserCheck } from 'lucide-react';
import Link from 'next/link';
import { type ReactNode } from 'react';
import { cn } from '../../lib/cn';
@ -17,6 +17,7 @@ const iconMap: Record<string, React.ComponentType<{ className?: string }>> = {
'user-check': UserCheck,
'link': LinkIcon,
'shield-check': Shield,
'user': User,
};
export type CardProps = {

View File

@ -1,165 +0,0 @@
---
title: API Setup & Configuration
---
This guide covers the essential setup for using Stack Auth's REST API in your Python application. Stack Auth provides a REST API for managing users, sessions, and authentication flows.
## Prerequisites
Before you begin, make sure you have:
- A Stack Auth [project](https://app.stack-auth.com/projects) created
- Python 3.7+ installed
- `requests` library (`pip install requests`)
## Environment Configuration
First, set up your API credentials. Get these from your Stack Auth dashboard:
```bash
# .env file
STACK_PROJECT_ID=your_project_id
STACK_PUBLISHABLE_KEY=your_publishable_key
STACK_SECRET_KEY=your_secret_key
STACK_API_URL=https://api.stack-auth.com
```
## Basic API Client Setup
Create a basic API client to handle Stack Auth requests:
```python
import os
import requests
from typing import Dict, Any, Optional
class StackAuthClient:
def __init__(self):
self.project_id = os.getenv('STACK_PROJECT_ID')
self.secret_key = os.getenv('STACK_SECRET_KEY')
self.publishable_key = os.getenv('STACK_PUBLISHABLE_KEY')
self.api_url = os.getenv('STACK_API_URL', 'https://api.stack-auth.com')
if not all([self.project_id, self.secret_key]):
raise ValueError("Missing required Stack Auth credentials")
def _make_request(
self,
method: str,
endpoint: str,
data: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, str]] = None
) -> requests.Response:
"""Make authenticated request to Stack Auth API"""
url = f"{self.api_url}/api/v1{endpoint}"
# Add authentication headers
auth_headers = {
'X-Stack-Project-Id': self.project_id,
'X-Stack-Secret-Key': self.secret_key,
'Content-Type': 'application/json'
}
if headers:
auth_headers.update(headers)
response = requests.request(
method=method,
url=url,
json=data,
headers=auth_headers
)
# Handle common error cases
if response.status_code == 401:
raise Exception("Authentication failed - check your API credentials")
elif response.status_code == 403:
raise Exception("Forbidden - insufficient permissions")
elif not response.ok:
raise Exception(f"API request failed: {response.status_code} - {response.text}")
return response
def get(self, endpoint: str, **kwargs) -> requests.Response:
return self._make_request('GET', endpoint, **kwargs)
def post(self, endpoint: str, data: Dict[str, Any] = None, **kwargs) -> requests.Response:
return self._make_request('POST', endpoint, data, **kwargs)
def put(self, endpoint: str, data: Dict[str, Any] = None, **kwargs) -> requests.Response:
return self._make_request('PUT', endpoint, data, **kwargs)
def delete(self, endpoint: str, **kwargs) -> requests.Response:
return self._make_request('DELETE', endpoint, **kwargs)
# Initialize the client
stack_client = StackAuthClient()
```
## Testing Your Setup
Test your API connection with a simple request:
```python
def test_connection():
try:
# Test with a simple API call
response = stack_client.get('/users')
print("✅ Connection successful!")
print(f"Found {len(response.json().get('users', []))} users")
return True
except Exception as e:
print(f"❌ Connection failed: {e}")
return False
# Run the test
if __name__ == "__main__":
test_connection()
```
## Error Handling
Implement proper error handling for production use:
```python
import logging
from typing import Optional
logger = logging.getLogger(__name__)
class StackAuthError(Exception):
"""Base exception for Stack Auth errors"""
pass
class StackAuthClient:
# ... previous code ...
def safe_request(
self,
method: str,
endpoint: str,
data: Optional[Dict[str, Any]] = None
) -> Optional[Dict[str, Any]]:
"""Make a safe request with comprehensive error handling"""
try:
response = self._make_request(method, endpoint, data)
return response.json()
except requests.exceptions.ConnectionError:
logger.error("Failed to connect to Stack Auth API")
raise StackAuthError("Network connection failed")
except requests.exceptions.Timeout:
logger.error("Request to Stack Auth API timed out")
raise StackAuthError("Request timed out")
except Exception as e:
logger.error(f"Stack Auth API error: {e}")
raise StackAuthError(f"API request failed: {e}")
```
## Next Steps
With your API client set up, you can now:
- [Authenticate users](./user-sessions) with your application
- [Handle OAuth flows](./oauth-flows) for social login
- [Validate server-side sessions](./server-validation)
For detailed API reference, see the [REST API documentation](/api/overview).

View File

@ -1,56 +1,252 @@
---
title: "Authentication Flows"
description: "Learn how to implement authentication flows in your Python application using Stack Auth's REST API"
title: Authentication
---
This section covers the core authentication patterns and flows you'll need to implement secure authentication in your Python application using Stack Auth's REST API.
## Core Authentication
This guide covers how to implement authentication in your Python application using Stack Auth. We'll walk through all the available authentication methods and show you how to handle user sessions.
<CardGroup cols={2}>
<Card
title="API Setup & Configuration"
href="./authentication/api-setup"
icon="settings"
>
Set up your Python client and configure API credentials for Stack Auth integration.
</Card>
<Card
title="User Sessions"
href="./user-sessions"
icon="user-check"
>
Handle user session management, validation, and lifecycle in Python applications.
</Card>
<Card
title="OAuth Flows"
href="./oauth-flows"
icon="link"
>
Implement OAuth authentication flows for social login providers via the REST API.
</Card>
<Card
title="Server Validation"
href="./server-validation"
icon="shield-check"
>
Validate user tokens and authenticate requests on your Python backend.
</Card>
</CardGroup>
## Overview
## Authentication Patterns
Stack Auth supports multiple authentication methods that you can enable or disable based on your needs:
Each authentication flow addresses specific use cases:
- **[Email/Password](./password.mdx)** - Traditional email and password authentication
- **[Magic Links (OTP)](./magic-links.mdx)** - Passwordless authentication via email
- **[OAuth Providers](./oauth.mdx)** - Sign in with Google, GitHub, Facebook, and more
- **[Passkeys](./passkeys.mdx)** - Modern biometric authentication
- **[Anonymous Users](./anonymous.mdx)** - Guest users that can be upgraded later
- **API Setup**: Foundation for all authentication operations
- **User Sessions**: Managing authenticated user state and session lifecycle
- **OAuth Flows**: Social login integration (Google, GitHub, etc.)
- **Server Validation**: Securing your API endpoints and validating requests
## Basic Authentication Flow
## Getting Started
All authentication methods in Stack Auth follow a similar pattern:
Start with [API Setup & Configuration](./api-setup) to establish your Python client, then proceed to the specific authentication flow that matches your application's needs.
1. **Initiate authentication** - Start the sign-in/sign-up process
2. **User verification** - User completes authentication (password, email link, OAuth, etc.)
3. **Receive tokens** - Get access and refresh tokens
4. **Manage session** - Use tokens to authenticate API requests
For framework-specific integration examples, see the [Framework Integration](../integration) section.
Here's the basic structure for handling authentication:
```python
from typing import Optional
import requests
class AuthManager:
def __init__(self, stack_auth_client):
self.client = stack_auth_client
self.current_user_token: Optional[str] = None
def authenticate_request(self, access_token: str) -> dict:
"""Verify and get user info from access token"""
return self.client._make_request(
'GET',
'/api/v1/users/me',
access_type="client",
access_token=access_token
)
def refresh_token(self, refresh_token: str) -> dict:
"""Refresh an expired access token"""
return self.client._make_request(
'POST',
'/api/v1/auth/sessions/current/refresh',
access_type="client",
json={'refresh_token': refresh_token}
)
def sign_out(self, access_token: str) -> bool:
"""Sign out the current user"""
try:
self.client._make_request(
'DELETE',
'/api/v1/auth/sessions/current',
access_type="client",
access_token=access_token
)
return True
except Exception:
return False
# Initialize auth manager
auth_manager = AuthManager(stack_auth)
```
## Token Management
Stack Auth uses two types of tokens:
- **Access Token** - Short-lived token for API requests (typically 1 hour)
- **Refresh Token** - Long-lived token to get new access tokens (typically 30 days)
### Storing Tokens Securely
For web applications, store tokens securely:
```python
from flask import session
import jwt
from datetime import datetime
def store_tokens_securely(access_token: str, refresh_token: str):
"""Store tokens in secure HTTP-only cookies or session"""
# For session storage
session['access_token'] = access_token
session['refresh_token'] = refresh_token
# For cookie storage (recommended for web apps)
# Set secure, HTTP-only cookies
response.set_cookie(
'access_token',
access_token,
httponly=True,
secure=True,
samesite='Strict'
)
def get_current_user_from_token(access_token: str) -> Optional[dict]:
"""Extract user info from access token"""
try:
# Verify token and get user
user_info = auth_manager.authenticate_request(access_token)
return user_info
except Exception:
return None
```
## Middleware for Authentication
Here's how to create middleware that automatically handles authentication:
```python
from functools import wraps
from flask import request, jsonify, g
def require_auth(f):
"""Decorator that requires authentication"""
@wraps(f)
def decorated_function(*args, **kwargs):
# Get token from header or cookie
access_token = request.headers.get('Authorization')
if access_token and access_token.startswith('Bearer '):
access_token = access_token[7:]
elif 'access_token' in request.cookies:
access_token = request.cookies['access_token']
else:
return jsonify({'error': 'Authentication required'}), 401
# Verify token and get user
try:
user_info = auth_manager.authenticate_request(access_token)
g.current_user = user_info
g.access_token = access_token
return f(*args, **kwargs)
except Exception as e:
return jsonify({'error': 'Invalid token'}), 401
return decorated_function
def optional_auth(f):
"""Decorator for optional authentication"""
@wraps(f)
def decorated_function(*args, **kwargs):
access_token = request.headers.get('Authorization')
if access_token and access_token.startswith('Bearer '):
access_token = access_token[7:]
try:
user_info = auth_manager.authenticate_request(access_token)
g.current_user = user_info
g.access_token = access_token
except Exception:
g.current_user = None
else:
g.current_user = None
return f(*args, **kwargs)
return decorated_function
```
## Usage Examples
### Protected Route
```python
from flask import Flask, g, jsonify
app = Flask(__name__)
@app.route('/profile')
@require_auth
def get_profile():
"""Get current user's profile - requires authentication"""
return jsonify({
'user': g.current_user,
'message': 'This is your profile'
})
@app.route('/public')
@optional_auth
def public_endpoint():
"""Public endpoint with optional user context"""
if g.current_user:
return jsonify({
'message': f'Hello {g.current_user.get("display_name", "User")}!'
})
else:
return jsonify({'message': 'Hello anonymous user!'})
```
### Manual Token Refresh
```python
def refresh_user_session(refresh_token: str) -> Optional[dict]:
"""Refresh user session and return new tokens"""
try:
token_response = auth_manager.refresh_token(refresh_token)
return {
'access_token': token_response['access_token'],
'refresh_token': token_response['refresh_token']
}
except Exception as e:
print(f"Token refresh failed: {e}")
return None
```
## Error Handling
Common authentication errors and how to handle them:
```python
class AuthError(Exception):
def __init__(self, message: str, status_code: int = 401):
self.message = message
self.status_code = status_code
super().__init__(self.message)
def handle_auth_errors(func):
"""Decorator to handle common authentication errors"""
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except requests.exceptions.HTTPError as e:
if e.response.status_code == 401:
raise AuthError("Invalid or expired token", 401)
elif e.response.status_code == 403:
raise AuthError("Insufficient permissions", 403)
else:
raise AuthError("Authentication error", e.response.status_code)
except Exception as e:
raise AuthError(f"Unexpected error: {str(e)}", 500)
return wrapper
```
## Next Steps
Choose the authentication method that best fits your application:
1. **[Email/Password Authentication](./password.mdx)** - Most common, familiar to users
2. **[Magic Link Authentication](./magic-links.mdx)** - Modern passwordless approach
3. **[OAuth Authentication](./oauth.mdx)** - Easy sign-in with existing accounts
4. **[Passkey Authentication](./passkeys.mdx)** - Most secure, future-proof option
5. **[Anonymous Authentication](./anonymous.mdx)** - Great for guest users
You can also enable multiple methods simultaneously to give users options.

View File

@ -2,6 +2,7 @@
"title": "Authentication",
"description": "Authentication flows and patterns for Python applications",
"pages": [
"index",
"api-setup",
"user-sessions",
"oauth-flows",

View File

@ -0,0 +1,378 @@
---
title: "User Authentication"
description: "Learn how to implement user authentication in your Python application using Stack Auth's REST API"
---
After creating a [helper function](../getting-started/setup.mdx) to make requests to the Stack Auth API, you can start using the API to authenticate users.
## User Authentication
Stack Auth supports multiple authentication methods:
- **Password Authentication** - Email and password
- **OTP Authentication** - Magic links and one-time passwords via email
- **OAuth Authentication** - Social logins (GitHub, Google, etc.)
- **Passkey Authentication** - WebAuthn/FIDO2 passkeys
- **Multi-Factor Authentication** - TOTP-based MFA
### Sign Up with Email and Password
To create a new user account with email and password:
```python
def sign_up_with_password(email, password, verification_callback_url):
"""
Sign up a new user with email and password
Returns access_token, refresh_token, and user_id
"""
response = stack_auth_request('POST', 'api/v1/auth/password/sign-up', json={
'email': email,
'password': password,
'verification_callback_url': verification_callback_url # URL where user will verify email
})
return {
'access_token': response['access_token'],
'refresh_token': response['refresh_token'],
'user_id': response['user_id']
}
# Example usage
user_data = sign_up_with_password(
email="user@example.com",
password="secure_password_123",
verification_callback_url="https://yourapp.com/verify-email"
)
```
### Sign In with Email and Password
To authenticate an existing user:
```python
def sign_in_with_password(email, password):
"""
Sign in an existing user with email and password
Returns access_token, refresh_token, and user_id
"""
response = stack_auth_request('POST', 'api/v1/auth/password/sign-in', json={
'email': email,
'password': password
})
return {
'access_token': response['access_token'],
'refresh_token': response['refresh_token'],
'user_id': response['user_id']
}
# Example usage
user_data = sign_in_with_password("user@example.com", "secure_password_123")
access_token = user_data['access_token']
refresh_token = user_data['refresh_token']
```
### Sign In with OTP (Magic Link)
For passwordless authentication using one-time passwords:
```python
def send_otp_code(email, callback_url):
"""
Send an OTP code to the user's email
Returns a nonce that must be stored for verification
"""
response = stack_auth_request('POST', 'api/v1/auth/otp/send-sign-in-code', json={
'email': email,
'callback_url': callback_url # URL where user will complete sign-in
})
return response['nonce']
def verify_otp_code(nonce, six_digit_code):
"""
Verify the OTP code and complete sign-in
The code parameter should be the 6-digit code + nonce concatenated
Returns access_token, refresh_token, and user_id
"""
# The verification code is the 6-digit code followed by the nonce
verification_code = six_digit_code + nonce
response = stack_auth_request('POST', 'api/v1/auth/otp/sign-in', json={
'code': verification_code
})
return {
'access_token': response['access_token'],
'refresh_token': response['refresh_token'],
'user_id': response['user_id'],
'is_new_user': response['is_new_user'] # True if this was a sign-up
}
# Example usage
nonce = send_otp_code("user@example.com", "https://yourapp.com/verify-otp")
# Store the nonce temporarily, user receives email with 6-digit code
# When user enters the code:
user_data = verify_otp_code(nonce, "123456")
```
### Get Current User Information
To retrieve information about the currently authenticated user:
```python
def get_current_user(access_token):
"""
Get the current user's information using their access token
"""
response = stack_auth_request('GET', 'api/v1/users/me', headers={
'x-stack-access-token': access_token
})
return {
'id': response['id'],
'display_name': response['display_name'],
'primary_email': response['primary_email'],
'primary_email_verified': response['primary_email_verified'],
'profile_image_url': response['profile_image_url'],
'signed_up_at_millis': response['signed_up_at_millis'],
'last_active_at_millis': response['last_active_at_millis'],
'oauth_providers': response['oauth_providers'],
'has_password': response['has_password'],
'auth_with_email': response['auth_with_email']
}
# Example usage
user_info = get_current_user(access_token)
print(f"Welcome, {user_info['display_name']}!")
```
### Refresh Access Token
Access tokens expire after a short time (typically 10 minutes). Use the refresh token to get a new access token:
```python
def refresh_access_token(refresh_token):
"""
Get a new access token using the refresh token
"""
response = stack_auth_request('POST', 'api/v1/auth/sessions/current/refresh', headers={
'x-stack-refresh-token': refresh_token
})
return response['access_token']
# Example usage
new_access_token = refresh_access_token(refresh_token)
```
### Sign Out (Revoke Session)
To sign out a user by revoking their session:
```python
def get_user_sessions(access_token):
"""
Get all active sessions for the current user
"""
response = stack_auth_request('GET', 'api/v1/auth/sessions', headers={
'x-stack-access-token': access_token
})
return response['items']
def sign_out_session(access_token, session_id):
"""
Sign out by deleting a specific session
"""
stack_auth_request('DELETE', f'api/v1/auth/sessions/{session_id}', headers={
'x-stack-access-token': access_token
})
def sign_out_current_user(access_token):
"""
Sign out the current user by finding and deleting their current session
"""
sessions = get_user_sessions(access_token)
current_session = next((s for s in sessions if s['is_current_session']), None)
if current_session:
# Note: This will fail with "CannotDeleteCurrentSession" error
# Instead, you should invalidate the tokens on your client side
pass
# In practice, you would typically just discard the tokens client-side
print("User signed out (tokens should be discarded client-side)")
# Example usage
sign_out_current_user(access_token)
```
## Complete Authentication Flow Example
Here's a complete example that demonstrates a full authentication flow:
```python
import os
import requests
# Setup (from setup guide)
stack_project_id = os.getenv("STACK_PROJECT_ID")
stack_publishable_client_key = os.getenv("STACK_PUBLISHABLE_CLIENT_KEY")
stack_secret_server_key = os.getenv("STACK_SECRET_SERVER_KEY")
def stack_auth_request(method, endpoint, **kwargs):
res = requests.request(
method,
f'https://api.stack-auth.com/{endpoint}',
headers={
'x-stack-access-type': 'server', # or 'client' if you're only accessing the client API
'x-stack-project-id': stack_project_id,
'x-stack-publishable-client-key': stack_publishable_client_key,
'x-stack-secret-server-key': stack_secret_server_key, # not necessary if access type is 'client'
**kwargs.pop('headers', {}),
},
**kwargs,
)
if res.status_code >= 400:
raise Exception(f"Stack Auth API request failed with {res.status_code}: {res.text}")
return res.json()
class StackAuthClient:
def __init__(self):
self.access_token = None
self.refresh_token = None
self.user_id = None
def sign_up(self, email, password, verification_callback_url):
"""Sign up a new user"""
response = stack_auth_request('POST', 'api/v1/auth/password/sign-up', json={
'email': email,
'password': password,
'verification_callback_url': verification_callback_url
})
self.access_token = response['access_token']
self.refresh_token = response['refresh_token']
self.user_id = response['user_id']
return response
def sign_in(self, email, password):
"""Sign in an existing user"""
response = stack_auth_request('POST', 'api/v1/auth/password/sign-in', json={
'email': email,
'password': password
})
self.access_token = response['access_token']
self.refresh_token = response['refresh_token']
self.user_id = response['user_id']
return response
def get_current_user(self):
"""Get current user information"""
if not self.access_token:
raise Exception("No access token available")
return stack_auth_request('GET', 'api/v1/users/me', headers={
'x-stack-access-token': self.access_token
})
def refresh_token_if_needed(self):
"""Refresh the access token"""
if not self.refresh_token:
raise Exception("No refresh token available")
response = stack_auth_request('POST', 'api/v1/auth/sessions/current/refresh', headers={
'x-stack-refresh-token': self.refresh_token
})
self.access_token = response['access_token']
return response
def sign_out(self):
"""Sign out by clearing tokens"""
self.access_token = None
self.refresh_token = None
self.user_id = None
# Example usage
auth_client = StackAuthClient()
# Sign up a new user
try:
auth_client.sign_up(
email="newuser@example.com",
password="secure_password_123",
verification_callback_url="https://yourapp.com/verify"
)
print("User signed up successfully!")
except Exception as e:
print(f"Sign up failed: {e}")
# Get user information
try:
user_info = auth_client.get_current_user()
print(f"Logged in as: {user_info['primary_email']}")
except Exception as e:
print(f"Failed to get user info: {e}")
# Refresh token when needed
try:
auth_client.refresh_token_if_needed()
print("Token refreshed successfully!")
except Exception as e:
print(f"Token refresh failed: {e}")
# Sign out
auth_client.sign_out()
print("User signed out!")
```
## Error Handling
Common errors you might encounter:
```python
def handle_auth_errors(func):
"""Decorator to handle common authentication errors"""
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
error_message = str(e)
if "EmailPasswordMismatch" in error_message:
print("Invalid email or password")
elif "AccessTokenExpired" in error_message:
print("Access token expired, please refresh")
elif "UserWithEmailAlreadyExists" in error_message:
print("User with this email already exists")
elif "PasswordAuthenticationNotEnabled" in error_message:
print("Password authentication is not enabled for this project")
else:
print(f"Authentication error: {error_message}")
raise e
return wrapper
@handle_auth_errors
def safe_sign_in(email, password):
return sign_in_with_password(email, password)
```
## Next Steps
Now that you have user authentication working, you can:
1. **[Manage Users](../user-management/index.mdx)** - Update user profiles, manage user data
2. **[Handle Teams](../team-management/index.mdx)** - Implement team functionality
3. **[Set up OAuth](../oauth/index.mdx)** - Add social login providers
4. **[Framework Integration](../integration/index.mdx)** - See examples for Flask, Django, and FastAPI
For more advanced authentication features, check out the [REST API documentation](../rest-api/overview.mdx).

View File

@ -0,0 +1,127 @@
---
title: Setup
---
<Info>
Welcome to the Python setup guide. If you're looking for guides for other frameworks, check out the [Next.js SDK Setup](/next/getting-started/setup), [React SDK Setup](/react/getting-started/setup), or the [JavaScript SDK Setup](/js/getting-started/setup).
</Info>
Our recommended way to use Stack Auth with Python is with the [REST API](../rest-api/overview.mdx). It provides a fully documented way to interact with Stack Auth from any Python framework, including Flask, FastAPI, Django, and others.
For the purpose of this guide, we will use the `requests` library to make HTTP requests to the Stack Auth API. If you haven't already, you can install it in your environment with `pip install requests`.
<Steps>
<Step>
### Create API keys
</Step>
First, create an account on [the Stack Auth dashboard](https://app.stack-auth.com/projects), and copy your project ID, publishable client key, and secret server key into a safe place (eg. environment variables).
From there, you can access them in your Python code. You can then read them like this:
```python
import os
stack_project_id = os.getenv("STACK_PROJECT_ID")
stack_publishable_client_key = os.getenv("STACK_PUBLISHABLE_CLIENT_KEY")
stack_secret_server_key = os.getenv("STACK_SECRET_SERVER_KEY")
```
<Step>
### Create a Stack Auth client
</Step>
Next, create a helper class to make requests to the Stack Auth API. This will handle authentication headers and error handling:
```python
import requests
import os
from typing import Optional, Dict, Any
class StackAuthClient:
def __init__(self):
self.base_url = "https://api.stack-auth.com"
self.project_id = os.getenv("STACK_PROJECT_ID")
self.publishable_client_key = os.getenv("STACK_PUBLISHABLE_CLIENT_KEY")
self.secret_server_key = os.getenv("STACK_SECRET_SERVER_KEY")
if not all([self.project_id, self.publishable_client_key]):
raise ValueError("Missing required Stack Auth environment variables")
def _make_request(
self,
method: str,
endpoint: str,
access_type: str = "server",
access_token: Optional[str] = None,
**kwargs
) -> Dict[str, Any]:
"""Make a request to the Stack Auth API"""
headers = {
'x-stack-access-type': access_type,
'x-stack-project-id': self.project_id,
'x-stack-publishable-client-key': self.publishable_client_key,
'Content-Type': 'application/json',
**kwargs.pop('headers', {}),
}
# Add server key for server access
if access_type == "server" and self.secret_server_key:
headers['x-stack-secret-server-key'] = self.secret_server_key
# Add access token for authenticated requests
if access_token:
headers['x-stack-access-token'] = access_token
url = f"{self.base_url}/{endpoint.lstrip('/')}"
response = requests.request(method, url, headers=headers, **kwargs)
if response.status_code >= 400:
raise Exception(f"Stack Auth API request failed with {response.status_code}: {response.text}")
return response.json() if response.content else {}
# Initialize the client
stack_auth = StackAuthClient()
```
<Step>
### Test the connection
</Step>
Test that your API keys work correctly by fetching the current project:
```python
# Test the connection
try:
project_info = stack_auth._make_request('GET', '/api/v1/projects/current')
print("✅ Successfully connected to Stack Auth!")
print(f"Project: {project_info.get('display_name', 'Unknown')}")
except Exception as e:
print(f"❌ Failed to connect: {e}")
```
<Step>
### Done!
</Step>
</Steps>
## What's Next?
Now that you have Stack Auth set up in your Python application, you can:
1. **[Implement Authentication](../authentication/index.mdx)** - Learn how to sign up and sign in users
2. **[Manage Users](../user-management/index.mdx)** - Create, update, and retrieve user information
3. **[Handle Teams](../team-management/index.mdx)** - Implement team functionality
4. **[Framework Integration](../integration/index.mdx)** - See specific examples for Flask, Django, and FastAPI
## Framework-Specific Setup
While the REST API approach works with any Python framework, we also provide specific integration guides:
- **[Flask Integration](../integration/flask.mdx)** - Complete Flask setup with middleware
- **[Django Integration](../integration/django.mdx)** - Django setup with custom authentication backend
- **[FastAPI Integration](../integration/fastapi.mdx)** - FastAPI setup with dependency injection
Choose the guide that matches your framework, or continue with the general REST API approach if you're using a different framework.