Implement proper CORS configuration for secure cross-origin usage
- Add flask-cors dependency and configure CORS with security best practices - Support configurable CORS origins via environment variables - Separate admin endpoint CORS configuration for enhanced security - Create comprehensive CORS configuration documentation - Add apiClient utility for CORS-aware frontend requests - Include CORS test page for validation - Update README with CORS configuration instructions 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
dc3e67e17b
commit
b08574efe5
152
CORS_CONFIG.md
Normal file
152
CORS_CONFIG.md
Normal file
@ -0,0 +1,152 @@
|
||||
# CORS Configuration Guide
|
||||
|
||||
This document explains how to configure Cross-Origin Resource Sharing (CORS) for the Talk2Me application.
|
||||
|
||||
## Overview
|
||||
|
||||
CORS is configured using Flask-CORS to enable secure cross-origin usage of the API endpoints. This allows the Talk2Me application to be embedded in other websites or accessed from different domains while maintaining security.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
### `CORS_ORIGINS`
|
||||
|
||||
Controls which domains are allowed to access the API endpoints.
|
||||
|
||||
- **Default**: `*` (allows all origins - use only for development)
|
||||
- **Production Example**: `https://yourdomain.com,https://app.yourdomain.com`
|
||||
- **Format**: Comma-separated list of allowed origins
|
||||
|
||||
```bash
|
||||
# Development (allows all origins)
|
||||
export CORS_ORIGINS="*"
|
||||
|
||||
# Production (restrict to specific domains)
|
||||
export CORS_ORIGINS="https://talk2me.example.com,https://app.example.com"
|
||||
```
|
||||
|
||||
### `ADMIN_CORS_ORIGINS`
|
||||
|
||||
Controls which domains can access admin endpoints (more restrictive).
|
||||
|
||||
- **Default**: `http://localhost:*` (allows all localhost ports)
|
||||
- **Production Example**: `https://admin.yourdomain.com`
|
||||
- **Format**: Comma-separated list of allowed admin origins
|
||||
|
||||
```bash
|
||||
# Development
|
||||
export ADMIN_CORS_ORIGINS="http://localhost:*"
|
||||
|
||||
# Production
|
||||
export ADMIN_CORS_ORIGINS="https://admin.talk2me.example.com"
|
||||
```
|
||||
|
||||
## Configuration Details
|
||||
|
||||
The CORS configuration includes:
|
||||
|
||||
- **Allowed Methods**: GET, POST, OPTIONS
|
||||
- **Allowed Headers**: Content-Type, Authorization, X-Requested-With, X-Admin-Token
|
||||
- **Exposed Headers**: Content-Range, X-Content-Range
|
||||
- **Credentials Support**: Enabled (supports cookies and authorization headers)
|
||||
- **Max Age**: 3600 seconds (preflight requests cached for 1 hour)
|
||||
|
||||
## Endpoints
|
||||
|
||||
All endpoints have CORS enabled with the following configuration:
|
||||
|
||||
### Regular API Endpoints
|
||||
- `/api/*`
|
||||
- `/transcribe`
|
||||
- `/translate`
|
||||
- `/translate/stream`
|
||||
- `/speak`
|
||||
- `/get_audio/*`
|
||||
- `/check_tts_server`
|
||||
- `/update_tts_config`
|
||||
- `/health/*`
|
||||
|
||||
### Admin Endpoints (More Restrictive)
|
||||
- `/admin/*` - Uses `ADMIN_CORS_ORIGINS` instead of general `CORS_ORIGINS`
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **Never use `*` in production** - Always specify exact allowed origins
|
||||
2. **Use HTTPS** - Always use HTTPS URLs in production CORS origins
|
||||
3. **Separate admin origins** - Keep admin endpoints on a separate, more restrictive origin list
|
||||
4. **Review regularly** - Periodically review and update allowed origins
|
||||
|
||||
## Example Configurations
|
||||
|
||||
### Local Development
|
||||
```bash
|
||||
export CORS_ORIGINS="*"
|
||||
export ADMIN_CORS_ORIGINS="http://localhost:*"
|
||||
```
|
||||
|
||||
### Staging Environment
|
||||
```bash
|
||||
export CORS_ORIGINS="https://staging.talk2me.com,https://staging-app.talk2me.com"
|
||||
export ADMIN_CORS_ORIGINS="https://staging-admin.talk2me.com"
|
||||
```
|
||||
|
||||
### Production Environment
|
||||
```bash
|
||||
export CORS_ORIGINS="https://talk2me.com,https://app.talk2me.com"
|
||||
export ADMIN_CORS_ORIGINS="https://admin.talk2me.com"
|
||||
```
|
||||
|
||||
### Mobile App Integration
|
||||
```bash
|
||||
# Include mobile app schemes if needed
|
||||
export CORS_ORIGINS="https://talk2me.com,https://app.talk2me.com,capacitor://localhost,ionic://localhost"
|
||||
```
|
||||
|
||||
## Testing CORS Configuration
|
||||
|
||||
You can test CORS configuration using curl:
|
||||
|
||||
```bash
|
||||
# Test preflight request
|
||||
curl -X OPTIONS https://your-api.com/api/transcribe \
|
||||
-H "Origin: https://allowed-origin.com" \
|
||||
-H "Access-Control-Request-Method: POST" \
|
||||
-H "Access-Control-Request-Headers: Content-Type" \
|
||||
-v
|
||||
|
||||
# Test actual request
|
||||
curl -X POST https://your-api.com/api/transcribe \
|
||||
-H "Origin: https://allowed-origin.com" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"test": "data"}' \
|
||||
-v
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### CORS Errors in Browser Console
|
||||
|
||||
If you see CORS errors:
|
||||
|
||||
1. Check that the origin is included in `CORS_ORIGINS`
|
||||
2. Ensure the URL protocol matches (http vs https)
|
||||
3. Check for trailing slashes in origins
|
||||
4. Verify environment variables are set correctly
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **"No 'Access-Control-Allow-Origin' header"**
|
||||
- Origin not in allowed list
|
||||
- Check `CORS_ORIGINS` environment variable
|
||||
|
||||
2. **"CORS policy: The request client is not a secure context"**
|
||||
- Using HTTP instead of HTTPS
|
||||
- Update to use HTTPS in production
|
||||
|
||||
3. **"CORS policy: Credentials flag is true, but Access-Control-Allow-Credentials is not 'true'"**
|
||||
- This should not occur with current configuration
|
||||
- Check that `supports_credentials` is True in CORS config
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [MDN CORS Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
|
||||
- [Flask-CORS Documentation](https://flask-cors.readthedocs.io/)
|
14
README.md
14
README.md
@ -64,6 +64,20 @@ A mobile-friendly web application that translates spoken language between multip
|
||||
- Ollama provides access to the Gemma 3 model for translation
|
||||
- OpenAI Edge TTS delivers natural-sounding speech output
|
||||
|
||||
## CORS Configuration
|
||||
|
||||
The application supports Cross-Origin Resource Sharing (CORS) for secure cross-origin usage. See [CORS_CONFIG.md](CORS_CONFIG.md) for detailed configuration instructions.
|
||||
|
||||
Quick setup:
|
||||
```bash
|
||||
# Development (allow all origins)
|
||||
export CORS_ORIGINS="*"
|
||||
|
||||
# Production (restrict to specific domains)
|
||||
export CORS_ORIGINS="https://yourdomain.com,https://app.yourdomain.com"
|
||||
export ADMIN_CORS_ORIGINS="https://admin.yourdomain.com"
|
||||
```
|
||||
|
||||
## Mobile Support
|
||||
|
||||
The interface is fully responsive and designed to work well on mobile devices.
|
||||
|
28
app.py
28
app.py
@ -5,6 +5,7 @@ import requests
|
||||
import json
|
||||
import logging
|
||||
from flask import Flask, render_template, request, jsonify, Response, send_file, send_from_directory, stream_with_context
|
||||
from flask_cors import CORS, cross_origin
|
||||
import whisper
|
||||
import torch
|
||||
import ollama
|
||||
@ -48,6 +49,33 @@ def with_error_boundary(func):
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# Configure CORS with security best practices
|
||||
cors_config = {
|
||||
"origins": os.environ.get('CORS_ORIGINS', '*').split(','), # Default to * for development, restrict in production
|
||||
"methods": ["GET", "POST", "OPTIONS"],
|
||||
"allow_headers": ["Content-Type", "Authorization", "X-Requested-With", "X-Admin-Token"],
|
||||
"expose_headers": ["Content-Range", "X-Content-Range"],
|
||||
"supports_credentials": True,
|
||||
"max_age": 3600 # Cache preflight requests for 1 hour
|
||||
}
|
||||
|
||||
# Apply CORS configuration
|
||||
CORS(app, resources={
|
||||
r"/api/*": cors_config,
|
||||
r"/transcribe": cors_config,
|
||||
r"/translate": cors_config,
|
||||
r"/translate/stream": cors_config,
|
||||
r"/speak": cors_config,
|
||||
r"/get_audio/*": cors_config,
|
||||
r"/check_tts_server": cors_config,
|
||||
r"/update_tts_config": cors_config,
|
||||
r"/health/*": cors_config,
|
||||
r"/admin/*": {
|
||||
**cors_config,
|
||||
"origins": os.environ.get('ADMIN_CORS_ORIGINS', 'http://localhost:*').split(',')
|
||||
}
|
||||
})
|
||||
|
||||
# Configure upload folder - use environment variable or default to secure temp directory
|
||||
default_upload_folder = os.path.join(tempfile.gettempdir(), 'talk2me_uploads')
|
||||
upload_folder = os.environ.get('UPLOAD_FOLDER', default_upload_folder)
|
||||
|
@ -1,4 +1,5 @@
|
||||
flask
|
||||
flask-cors
|
||||
requests
|
||||
openai-whisper
|
||||
torch
|
||||
|
1097
static/js/app.js
1097
static/js/app.js
File diff suppressed because it is too large
Load Diff
155
static/js/src/apiClient.ts
Normal file
155
static/js/src/apiClient.ts
Normal file
@ -0,0 +1,155 @@
|
||||
// API Client with CORS support
|
||||
export interface ApiClientConfig {
|
||||
baseUrl?: string;
|
||||
credentials?: RequestCredentials;
|
||||
headers?: HeadersInit;
|
||||
}
|
||||
|
||||
export class ApiClient {
|
||||
private static instance: ApiClient;
|
||||
private config: ApiClientConfig;
|
||||
|
||||
private constructor() {
|
||||
// Default configuration
|
||||
this.config = {
|
||||
baseUrl: '', // Use same origin by default
|
||||
credentials: 'same-origin', // Change to 'include' for cross-origin requests
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest' // Identify as AJAX request
|
||||
}
|
||||
};
|
||||
|
||||
// Check if we're in a cross-origin context
|
||||
this.detectCrossOrigin();
|
||||
}
|
||||
|
||||
static getInstance(): ApiClient {
|
||||
if (!ApiClient.instance) {
|
||||
ApiClient.instance = new ApiClient();
|
||||
}
|
||||
return ApiClient.instance;
|
||||
}
|
||||
|
||||
// Detect if we're making cross-origin requests
|
||||
private detectCrossOrigin(): void {
|
||||
// Check if the app is loaded from a different origin
|
||||
const currentScript = document.currentScript as HTMLScriptElement | null;
|
||||
const scriptSrc = currentScript?.src || '';
|
||||
if (scriptSrc && !scriptSrc.startsWith(window.location.origin)) {
|
||||
// We're likely in a cross-origin context
|
||||
this.config.credentials = 'include';
|
||||
console.log('Cross-origin context detected, enabling credentials');
|
||||
}
|
||||
|
||||
// Also check for explicit configuration in meta tags
|
||||
const corsOrigin = document.querySelector('meta[name="cors-origin"]');
|
||||
if (corsOrigin) {
|
||||
const origin = corsOrigin.getAttribute('content');
|
||||
if (origin && origin !== window.location.origin) {
|
||||
this.config.baseUrl = origin;
|
||||
this.config.credentials = 'include';
|
||||
console.log(`Using CORS origin: ${origin}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the API client
|
||||
configure(config: Partial<ApiClientConfig>): void {
|
||||
this.config = { ...this.config, ...config };
|
||||
}
|
||||
|
||||
// Make a fetch request with CORS support
|
||||
async fetch(url: string, options: RequestInit = {}): Promise<Response> {
|
||||
// Construct full URL
|
||||
const fullUrl = this.config.baseUrl ? `${this.config.baseUrl}${url}` : url;
|
||||
|
||||
// Merge headers
|
||||
const headers = new Headers(options.headers);
|
||||
if (this.config.headers) {
|
||||
const configHeaders = new Headers(this.config.headers);
|
||||
configHeaders.forEach((value, key) => {
|
||||
if (!headers.has(key)) {
|
||||
headers.set(key, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Merge options with defaults
|
||||
const fetchOptions: RequestInit = {
|
||||
...options,
|
||||
headers,
|
||||
credentials: options.credentials || this.config.credentials
|
||||
};
|
||||
|
||||
// Add CORS mode if cross-origin
|
||||
if (this.config.baseUrl && this.config.baseUrl !== window.location.origin) {
|
||||
fetchOptions.mode = 'cors';
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(fullUrl, fetchOptions);
|
||||
|
||||
// Check for CORS errors
|
||||
if (!response.ok && response.type === 'opaque') {
|
||||
throw new Error('CORS request failed - check server CORS configuration');
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
// Enhanced error handling for CORS issues
|
||||
if (error instanceof TypeError && error.message.includes('Failed to fetch')) {
|
||||
console.error('CORS Error: Failed to fetch. Check that:', {
|
||||
requestedUrl: fullUrl,
|
||||
origin: window.location.origin,
|
||||
credentials: fetchOptions.credentials,
|
||||
mode: fetchOptions.mode
|
||||
});
|
||||
throw new Error('CORS request failed. The server may not allow requests from this origin.');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience methods
|
||||
async get(url: string, options?: RequestInit): Promise<Response> {
|
||||
return this.fetch(url, { ...options, method: 'GET' });
|
||||
}
|
||||
|
||||
async post(url: string, body?: any, options?: RequestInit): Promise<Response> {
|
||||
const init: RequestInit = { ...options, method: 'POST' };
|
||||
|
||||
if (body) {
|
||||
if (body instanceof FormData) {
|
||||
init.body = body;
|
||||
} else {
|
||||
init.headers = {
|
||||
...init.headers,
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
init.body = JSON.stringify(body);
|
||||
}
|
||||
}
|
||||
|
||||
return this.fetch(url, init);
|
||||
}
|
||||
|
||||
// JSON convenience methods
|
||||
async getJSON<T>(url: string, options?: RequestInit): Promise<T> {
|
||||
const response = await this.get(url, options);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async postJSON<T>(url: string, body?: any, options?: RequestInit): Promise<T> {
|
||||
const response = await this.post(url, body, options);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
}
|
||||
|
||||
// Export a singleton instance
|
||||
export const apiClient = ApiClient.getInstance();
|
@ -21,10 +21,15 @@ import { Validator } from './validator';
|
||||
import { StreamingTranslation } from './streamingTranslation';
|
||||
import { PerformanceMonitor } from './performanceMonitor';
|
||||
import { SpeakerManager } from './speakerManager';
|
||||
// import { apiClient } from './apiClient'; // Available for cross-origin requests
|
||||
|
||||
// Initialize error boundary
|
||||
const errorBoundary = ErrorBoundary.getInstance();
|
||||
|
||||
// Configure API client if needed for cross-origin requests
|
||||
// import { apiClient } from './apiClient';
|
||||
// apiClient.configure({ baseUrl: 'https://api.talk2me.com', credentials: 'include' });
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Set up global error handler
|
||||
errorBoundary.setGlobalErrorHandler((error, errorInfo) => {
|
||||
|
228
test-cors.html
Normal file
228
test-cors.html
Normal file
@ -0,0 +1,228 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>CORS Test for Talk2Me</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 50px auto;
|
||||
padding: 20px;
|
||||
}
|
||||
.test-result {
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.success {
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
.error {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
button {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
}
|
||||
#results {
|
||||
margin-top: 20px;
|
||||
}
|
||||
pre {
|
||||
background-color: #f8f9fa;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>CORS Test for Talk2Me API</h1>
|
||||
|
||||
<p>This page tests CORS configuration for the Talk2Me API. Open this file from a different origin (e.g., file:// or a different port) to test cross-origin requests.</p>
|
||||
|
||||
<div>
|
||||
<label for="apiUrl">API Base URL:</label>
|
||||
<input type="text" id="apiUrl" placeholder="http://localhost:5005" value="http://localhost:5005">
|
||||
</div>
|
||||
|
||||
<h2>Tests:</h2>
|
||||
|
||||
<button onclick="testHealthEndpoint()">Test Health Endpoint</button>
|
||||
<button onclick="testPreflightRequest()">Test Preflight Request</button>
|
||||
<button onclick="testTranscribeEndpoint()">Test Transcribe Endpoint (OPTIONS)</button>
|
||||
<button onclick="testWithCredentials()">Test With Credentials</button>
|
||||
|
||||
<div id="results"></div>
|
||||
|
||||
<script>
|
||||
function addResult(test, success, message, details = null) {
|
||||
const resultsDiv = document.getElementById('results');
|
||||
const resultDiv = document.createElement('div');
|
||||
resultDiv.className = `test-result ${success ? 'success' : 'error'}`;
|
||||
|
||||
let html = `<strong>${test}:</strong> ${message}`;
|
||||
if (details) {
|
||||
html += `<pre>${JSON.stringify(details, null, 2)}</pre>`;
|
||||
}
|
||||
resultDiv.innerHTML = html;
|
||||
resultsDiv.appendChild(resultDiv);
|
||||
}
|
||||
|
||||
function getApiUrl() {
|
||||
return document.getElementById('apiUrl').value.trim();
|
||||
}
|
||||
|
||||
async function testHealthEndpoint() {
|
||||
const apiUrl = getApiUrl();
|
||||
try {
|
||||
const response = await fetch(`${apiUrl}/health`, {
|
||||
method: 'GET',
|
||||
mode: 'cors',
|
||||
headers: {
|
||||
'Origin': window.location.origin
|
||||
}
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Check CORS headers
|
||||
const corsHeaders = {
|
||||
'Access-Control-Allow-Origin': response.headers.get('Access-Control-Allow-Origin'),
|
||||
'Access-Control-Allow-Credentials': response.headers.get('Access-Control-Allow-Credentials')
|
||||
};
|
||||
|
||||
addResult('Health Endpoint GET', true, 'Request successful', {
|
||||
status: response.status,
|
||||
data: data,
|
||||
corsHeaders: corsHeaders
|
||||
});
|
||||
} catch (error) {
|
||||
addResult('Health Endpoint GET', false, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function testPreflightRequest() {
|
||||
const apiUrl = getApiUrl();
|
||||
try {
|
||||
const response = await fetch(`${apiUrl}/api/push-public-key`, {
|
||||
method: 'OPTIONS',
|
||||
mode: 'cors',
|
||||
headers: {
|
||||
'Origin': window.location.origin,
|
||||
'Access-Control-Request-Method': 'GET',
|
||||
'Access-Control-Request-Headers': 'content-type'
|
||||
}
|
||||
});
|
||||
|
||||
const corsHeaders = {
|
||||
'Access-Control-Allow-Origin': response.headers.get('Access-Control-Allow-Origin'),
|
||||
'Access-Control-Allow-Methods': response.headers.get('Access-Control-Allow-Methods'),
|
||||
'Access-Control-Allow-Headers': response.headers.get('Access-Control-Allow-Headers'),
|
||||
'Access-Control-Max-Age': response.headers.get('Access-Control-Max-Age')
|
||||
};
|
||||
|
||||
addResult('Preflight Request', response.ok, `Status: ${response.status}`, corsHeaders);
|
||||
} catch (error) {
|
||||
addResult('Preflight Request', false, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function testTranscribeEndpoint() {
|
||||
const apiUrl = getApiUrl();
|
||||
try {
|
||||
const response = await fetch(`${apiUrl}/transcribe`, {
|
||||
method: 'OPTIONS',
|
||||
mode: 'cors',
|
||||
headers: {
|
||||
'Origin': window.location.origin,
|
||||
'Access-Control-Request-Method': 'POST',
|
||||
'Access-Control-Request-Headers': 'content-type'
|
||||
}
|
||||
});
|
||||
|
||||
const corsHeaders = {
|
||||
'Access-Control-Allow-Origin': response.headers.get('Access-Control-Allow-Origin'),
|
||||
'Access-Control-Allow-Methods': response.headers.get('Access-Control-Allow-Methods'),
|
||||
'Access-Control-Allow-Headers': response.headers.get('Access-Control-Allow-Headers'),
|
||||
'Access-Control-Allow-Credentials': response.headers.get('Access-Control-Allow-Credentials')
|
||||
};
|
||||
|
||||
addResult('Transcribe Endpoint OPTIONS', response.ok, `Status: ${response.status}`, corsHeaders);
|
||||
} catch (error) {
|
||||
addResult('Transcribe Endpoint OPTIONS', false, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function testWithCredentials() {
|
||||
const apiUrl = getApiUrl();
|
||||
try {
|
||||
const response = await fetch(`${apiUrl}/health`, {
|
||||
method: 'GET',
|
||||
mode: 'cors',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Origin': window.location.origin
|
||||
}
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
addResult('Request with Credentials', true, 'Request successful', {
|
||||
status: response.status,
|
||||
credentialsIncluded: true,
|
||||
data: data
|
||||
});
|
||||
} catch (error) {
|
||||
addResult('Request with Credentials', false, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear results before running new tests
|
||||
function clearResults() {
|
||||
document.getElementById('results').innerHTML = '';
|
||||
}
|
||||
|
||||
// Add event listeners
|
||||
document.querySelectorAll('button').forEach(button => {
|
||||
button.addEventListener('click', (e) => {
|
||||
if (!e.target.textContent.includes('Test')) return;
|
||||
clearResults();
|
||||
});
|
||||
});
|
||||
|
||||
// Show current origin
|
||||
window.addEventListener('load', () => {
|
||||
const info = document.createElement('div');
|
||||
info.style.marginBottom = '20px';
|
||||
info.style.padding = '10px';
|
||||
info.style.backgroundColor = '#e9ecef';
|
||||
info.style.borderRadius = '5px';
|
||||
info.innerHTML = `<strong>Current Origin:</strong> ${window.location.origin}<br>
|
||||
<strong>Protocol:</strong> ${window.location.protocol}<br>
|
||||
<strong>Note:</strong> For effective CORS testing, open this file from a different origin than your API server.`;
|
||||
document.body.insertBefore(info, document.querySelector('h2'));
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user