mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-13 21:01:21 +08:00
Python prep
This commit is contained in:
parent
4d04d8e8ad
commit
7ad3ec577b
@ -259,3 +259,56 @@ pages:
|
||||
- path: others/cli-authentication.mdx
|
||||
platforms: ["python"] # Python only
|
||||
|
||||
# Python-specific content from templates-python/
|
||||
# Authentication section
|
||||
- path: authentication/api-setup.mdx
|
||||
platforms: ["python"]
|
||||
|
||||
- path: authentication/user-sessions.mdx
|
||||
platforms: ["python"]
|
||||
|
||||
- path: authentication/oauth-flows.mdx
|
||||
platforms: ["python"]
|
||||
|
||||
- path: authentication/server-validation.mdx
|
||||
platforms: ["python"]
|
||||
|
||||
# User Management section
|
||||
- path: user-management/create-users.mdx
|
||||
platforms: ["python"]
|
||||
|
||||
- path: user-management/user-operations.mdx
|
||||
platforms: ["python"]
|
||||
|
||||
- path: user-management/custom-user-data.mdx
|
||||
platforms: ["python"]
|
||||
|
||||
- path: user-management/user-permissions.mdx
|
||||
platforms: ["python"]
|
||||
|
||||
# Team Management section
|
||||
- path: team-management/team-operations.mdx
|
||||
platforms: ["python"]
|
||||
|
||||
- path: team-management/team-invitations.mdx
|
||||
platforms: ["python"]
|
||||
|
||||
- path: team-management/team-permissions.mdx
|
||||
platforms: ["python"]
|
||||
|
||||
- path: team-management/organization-management.mdx
|
||||
platforms: ["python"]
|
||||
|
||||
# Framework Integration section
|
||||
- path: integration/django.mdx
|
||||
platforms: ["python"]
|
||||
|
||||
- path: integration/flask.mdx
|
||||
platforms: ["python"]
|
||||
|
||||
- path: integration/fastapi.mdx
|
||||
platforms: ["python"]
|
||||
|
||||
- path: integration/standalone-scripts.mdx
|
||||
platforms: ["python"]
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ const __dirname = path.dirname(__filename);
|
||||
|
||||
// Configure paths
|
||||
const TEMPLATE_DIR = path.resolve(__dirname, '../templates');
|
||||
const PYTHON_TEMPLATE_DIR = path.resolve(__dirname, '../templates-python');
|
||||
const OUTPUT_BASE_DIR = path.resolve(__dirname, '../content/docs');
|
||||
const CONFIG_FILE = path.resolve(__dirname, '../docs-platform.yml');
|
||||
const PLATFORMS = ['next', 'react', 'js', 'python'];
|
||||
@ -147,11 +148,21 @@ function generateMetaFiles() {
|
||||
const folderName = getFolderName(platform);
|
||||
const platformDisplayName = getPlatformDisplayName(platform);
|
||||
|
||||
// Find all meta.json files in the template directory
|
||||
const metaFiles = glob.sync('**/meta.json', { cwd: TEMPLATE_DIR });
|
||||
// For Python platform, prioritize Python-specific templates, but also include shared templates
|
||||
const templateDir = (platform === 'python' && fs.existsSync(PYTHON_TEMPLATE_DIR)) ? PYTHON_TEMPLATE_DIR : TEMPLATE_DIR;
|
||||
|
||||
// Find all meta.json files in the appropriate template directory
|
||||
const metaFiles = glob.sync('**/meta.json', { cwd: templateDir });
|
||||
|
||||
// For Python, also get meta.json files from shared templates (excluding root meta.json to avoid conflicts)
|
||||
let sharedMetaFiles = [];
|
||||
if (platform === 'python' && fs.existsSync(PYTHON_TEMPLATE_DIR)) {
|
||||
sharedMetaFiles = glob.sync('**/meta.json', { cwd: TEMPLATE_DIR }).filter(file => file !== 'meta.json');
|
||||
}
|
||||
|
||||
// Process Python-specific meta files
|
||||
for (const metaFile of metaFiles) {
|
||||
const srcPath = path.join(TEMPLATE_DIR, metaFile);
|
||||
const srcPath = path.join(templateDir, metaFile);
|
||||
const destPath = path.join(OUTPUT_BASE_DIR, folderName, metaFile);
|
||||
|
||||
// If this is a nested meta.json (not root), check if the folder should exist for this platform
|
||||
@ -220,9 +231,15 @@ function generateMetaFiles() {
|
||||
// Regular page
|
||||
else {
|
||||
// Check if this is actually a folder reference vs a page reference
|
||||
// A folder reference should have a corresponding directory in templates
|
||||
const folderPath = path.join(TEMPLATE_DIR, page);
|
||||
const isActualFolder = fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory();
|
||||
// Check both template directories for Python
|
||||
let folderPath = path.join(templateDir, page);
|
||||
let isActualFolder = fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory();
|
||||
|
||||
// For Python, also check shared templates directory
|
||||
if (!isActualFolder && platform === 'python') {
|
||||
folderPath = path.join(TEMPLATE_DIR, page);
|
||||
isActualFolder = fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory();
|
||||
}
|
||||
|
||||
if (isActualFolder) {
|
||||
// This is a folder reference - check if folder has content for this platform
|
||||
@ -264,6 +281,32 @@ function generateMetaFiles() {
|
||||
fs.writeFileSync(destPath, JSON.stringify(metaData, null, 2));
|
||||
console.log(`Generated platform-specific meta.json for ${platform}: ${destPath}`);
|
||||
}
|
||||
|
||||
// For Python, also process shared meta.json files (but not root)
|
||||
for (const metaFile of sharedMetaFiles) {
|
||||
const folderPath = path.dirname(metaFile);
|
||||
|
||||
// Check if any pages in this folder are included for Python
|
||||
const hasContentInFolder = platformConfig.pages && platformConfig.pages.some(configPage =>
|
||||
configPage.path.startsWith(`${folderPath}/`) &&
|
||||
configPage.platforms.includes(platform)
|
||||
);
|
||||
|
||||
if (hasContentInFolder) {
|
||||
const srcPath = path.join(TEMPLATE_DIR, metaFile);
|
||||
const destPath = path.join(OUTPUT_BASE_DIR, folderName, metaFile);
|
||||
|
||||
// Read and copy the shared meta.json
|
||||
const templateContent = fs.readFileSync(srcPath, 'utf8');
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
||||
|
||||
// Write the shared meta.json
|
||||
fs.writeFileSync(destPath, templateContent);
|
||||
console.log(`Generated shared meta.json for ${platform}: ${destPath}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,6 +316,7 @@ function generateMetaFiles() {
|
||||
function copyAssets() {
|
||||
const assetDirs = ['imgs'];
|
||||
|
||||
// Copy assets from main templates directory
|
||||
for (const dir of assetDirs) {
|
||||
const srcDir = path.join(TEMPLATE_DIR, dir);
|
||||
|
||||
@ -295,13 +339,35 @@ function copyAssets() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy Python-specific assets if they exist
|
||||
if (fs.existsSync(PYTHON_TEMPLATE_DIR)) {
|
||||
for (const dir of assetDirs) {
|
||||
const srcDir = path.join(PYTHON_TEMPLATE_DIR, dir);
|
||||
|
||||
if (fs.existsSync(srcDir)) {
|
||||
const destDir = path.join(OUTPUT_BASE_DIR, 'python', dir);
|
||||
fs.mkdirSync(destDir, { recursive: true });
|
||||
|
||||
// Find and copy all files
|
||||
const files = glob.sync('**/*', { cwd: srcDir, nodir: true });
|
||||
for (const file of files) {
|
||||
const srcFile = path.join(srcDir, file);
|
||||
const destFile = path.join(destDir, file);
|
||||
fs.mkdirSync(path.dirname(destFile), { recursive: true });
|
||||
fs.copyFileSync(srcFile, destFile);
|
||||
console.log(`Copied Python-specific asset: ${srcFile} -> ${destFile}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function to generate platform-specific docs
|
||||
*/
|
||||
function generateDocs() {
|
||||
// Find all MDX files in the template directory
|
||||
// Find all MDX files in the main template directory
|
||||
const templateFiles = glob.sync('**/*.mdx', { cwd: TEMPLATE_DIR });
|
||||
|
||||
if (templateFiles.length === 0) {
|
||||
@ -309,9 +375,9 @@ function generateDocs() {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Found ${templateFiles.length} template files`);
|
||||
console.log(`Found ${templateFiles.length} shared template files`);
|
||||
|
||||
// Process for each platform
|
||||
// Process shared templates for each platform
|
||||
for (const platform of PLATFORMS) {
|
||||
const folderName = getFolderName(platform);
|
||||
const outputDir = path.join(OUTPUT_BASE_DIR, folderName);
|
||||
@ -319,7 +385,7 @@ function generateDocs() {
|
||||
// Create the output directory
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
|
||||
// Process each template file
|
||||
// Process each shared template file
|
||||
for (const file of templateFiles) {
|
||||
// Check if this file should be included for this platform
|
||||
if (!shouldIncludeFileForPlatform(platform, file)) {
|
||||
@ -346,6 +412,32 @@ function generateDocs() {
|
||||
}
|
||||
}
|
||||
|
||||
// Process Python-specific templates if they exist
|
||||
if (fs.existsSync(PYTHON_TEMPLATE_DIR)) {
|
||||
console.log(`Processing Python-specific templates from ${PYTHON_TEMPLATE_DIR}`);
|
||||
const pythonTemplateFiles = glob.sync('**/*.mdx', { cwd: PYTHON_TEMPLATE_DIR });
|
||||
|
||||
if (pythonTemplateFiles.length > 0) {
|
||||
const pythonOutputDir = path.join(OUTPUT_BASE_DIR, 'python');
|
||||
|
||||
for (const file of pythonTemplateFiles) {
|
||||
const inputFile = path.join(PYTHON_TEMPLATE_DIR, file);
|
||||
const outputFile = path.join(pythonOutputDir, file);
|
||||
|
||||
// Read the Python-specific template
|
||||
const templateContent = fs.readFileSync(inputFile, 'utf8');
|
||||
|
||||
// Create output directory if it doesn't exist
|
||||
fs.mkdirSync(path.dirname(outputFile), { recursive: true });
|
||||
|
||||
// Write the content (no platform processing needed for Python-specific files)
|
||||
fs.writeFileSync(outputFile, templateContent);
|
||||
|
||||
console.log(`Generated Python-specific: ${outputFile}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate meta.json files for navigation
|
||||
generateMetaFiles();
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { Code, FileText, Play, Puzzle } from 'lucide-react';
|
||||
import { Code, FileText, Link as LinkIcon, Play, Puzzle, Settings, Shield, UserCheck } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { type ReactNode } from 'react';
|
||||
import { cn } from '../../lib/cn';
|
||||
@ -11,6 +11,12 @@ const iconMap: Record<string, React.ComponentType<{ className?: string }>> = {
|
||||
'fa-solid fa-puzzle': Puzzle,
|
||||
'fa-regular fa-file-lines': FileText,
|
||||
'fa-solid fa-code': Code,
|
||||
|
||||
// Authentication-related icons
|
||||
'settings': Settings,
|
||||
'user-check': UserCheck,
|
||||
'link': LinkIcon,
|
||||
'shield-check': Shield,
|
||||
};
|
||||
|
||||
export type CardProps = {
|
||||
|
||||
165
docs/templates-python/authentication/api-setup.mdx
Normal file
165
docs/templates-python/authentication/api-setup.mdx
Normal file
@ -0,0 +1,165 @@
|
||||
---
|
||||
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).
|
||||
56
docs/templates-python/authentication/index.mdx
Normal file
56
docs/templates-python/authentication/index.mdx
Normal file
@ -0,0 +1,56 @@
|
||||
---
|
||||
title: "Authentication Flows"
|
||||
description: "Learn how to implement authentication flows in your Python application using Stack Auth's REST API"
|
||||
---
|
||||
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
|
||||
|
||||
<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>
|
||||
|
||||
## Authentication Patterns
|
||||
|
||||
Each authentication flow addresses specific use cases:
|
||||
|
||||
- **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
|
||||
|
||||
## Getting Started
|
||||
|
||||
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.
|
||||
|
||||
For framework-specific integration examples, see the [Framework Integration](../integration) section.
|
||||
10
docs/templates-python/authentication/meta.json
Normal file
10
docs/templates-python/authentication/meta.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"title": "Authentication",
|
||||
"description": "Authentication flows and patterns for Python applications",
|
||||
"pages": [
|
||||
"api-setup",
|
||||
"user-sessions",
|
||||
"oauth-flows",
|
||||
"server-validation"
|
||||
]
|
||||
}
|
||||
6
docs/templates-python/integration/django.mdx
Normal file
6
docs/templates-python/integration/django.mdx
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: Django Integration
|
||||
description: Integrate Stack Auth with Django applications using middleware and decorators
|
||||
---
|
||||
|
||||
This guide is coming soon.
|
||||
10
docs/templates-python/integration/meta.json
Normal file
10
docs/templates-python/integration/meta.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"title": "Framework Integration",
|
||||
"description": "Stack Auth integration patterns for Python frameworks",
|
||||
"pages": [
|
||||
"django",
|
||||
"flask",
|
||||
"fastapi",
|
||||
"standalone-scripts"
|
||||
]
|
||||
}
|
||||
23
docs/templates-python/meta.json
Normal file
23
docs/templates-python/meta.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"title": "Python Documentation",
|
||||
"description": "Stack Auth for Python applications using the REST API",
|
||||
"pages": [
|
||||
"overview",
|
||||
"---Getting Started---",
|
||||
"getting-started/setup",
|
||||
"getting-started/auth-providers",
|
||||
"---Authentication Flows---",
|
||||
"authentication",
|
||||
"---User Management---",
|
||||
"user-management",
|
||||
"---Team & Organization Management---",
|
||||
"team-management",
|
||||
"---Framework Integration---",
|
||||
"integration",
|
||||
"---Backend Concepts---",
|
||||
"concepts/backend-integration",
|
||||
"---Other Resources---",
|
||||
"others",
|
||||
"faq"
|
||||
]
|
||||
}
|
||||
10
docs/templates-python/team-management/meta.json
Normal file
10
docs/templates-python/team-management/meta.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"title": "Team Management",
|
||||
"description": "Team and organization operations via the REST API",
|
||||
"pages": [
|
||||
"team-operations",
|
||||
"team-invitations",
|
||||
"team-permissions",
|
||||
"organization-management"
|
||||
]
|
||||
}
|
||||
10
docs/templates-python/user-management/meta.json
Normal file
10
docs/templates-python/user-management/meta.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"title": "User Management",
|
||||
"description": "User operations and data management via the REST API",
|
||||
"pages": [
|
||||
"create-users",
|
||||
"user-operations",
|
||||
"custom-user-data",
|
||||
"user-permissions"
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user