fix auth endpoint

This commit is contained in:
overcuriousity 2025-07-31 10:26:00 +02:00
parent 5f190fbf02
commit 336bfa0c99
7 changed files with 192 additions and 30 deletions

File diff suppressed because one or more lines are too long

60
.env Normal file
View File

@ -0,0 +1,60 @@
# ===========================================
# ForensicPathways Environment Configuration
# ===========================================
# === Authentication Configuration ===
AUTHENTICATION_NECESSARY=false
AUTHENTICATION_NECESSARY_CONTRIBUTIONS=false
AUTHENTICATION_NECESSARY_AI=false
AUTH_SECRET=your-secret-key-change-in-production
# OIDC Configuration (if authentication enabled)
OIDC_ENDPOINT=https://cloud.cc24.dev/index.php/apps/oidc/
OIDC_CLIENT_ID=your-client-id
OIDC_CLIENT_SECRET=your-client-secret
# === AI Configuration ===
# Main AI API (for analysis stage, choose a good model)
AI_API_ENDPOINT=https://llm.mikoshi.de/
AI_API_KEY=69feb44e7567babf1bef9df9ccc34d64ce1660547ba2c1b70e88571289fa713f
AI_MODEL=mistral/mistral-medium-latest
# Selector AI (for selection stage, choode a good model)
AI_SELECTOR_ENDPOINT=https://llm.mikoshi.de/
AI_SELECTOR_API_KEY=69feb44e7567babf1bef9df9ccc34d64ce1660547ba2c1b70e88571289fa713f
AI_SELECTOR_MODEL=mistral/mistral-medium-latest
# Analyzer AI (for analysis stage, smaller model sufficient)
AI_ANALYZER_ENDPOINT=https://llm.mikoshi.de/
AI_ANALYZER_API_KEY=69feb44e7567babf1bef9df9ccc34d64ce1660547ba2c1b70e88571289fa713f
AI_ANALYZER_MODEL=mistral/mistral-small-latest
# === Embeddings Configuration ===
# Enable/disable semantic embeddings pre-selection
AI_EMBEDDINGS_ENABLED=true
# Embeddings API (Mistral recommended)
AI_EMBEDDINGS_ENDPOINT=https://api.mistral.ai/v1/embeddings
AI_EMBEDDINGS_API_KEY=ZSpt6VsczlGttlGnugm5Vbh5m9w423wL
AI_EMBEDDINGS_MODEL=mistral-embed
# Embeddings performance settings
AI_EMBEDDINGS_BATCH_SIZE=20
AI_EMBEDDINGS_BATCH_DELAY_MS=1000
AI_EMBEDDING_CANDIDATES=30
AI_SIMILARITY_THRESHOLD=0.3
# === AI Processing Configuration ===
AI_MAX_SELECTED_ITEMS=15
AI_RATE_LIMIT_DELAY_MS=2000
# === Application Configuration ===
PUBLIC_BASE_URL=http://localhost:4321
NODE_ENV=development
# Nextcloud Integration (Optional)
NEXTCLOUD_ENDPOINT=https://your-nextcloud.com
NEXTCLOUD_USERNAME=your-username
NEXTCLOUD_PASSWORD=your-password
NEXTCLOUD_UPLOAD_PATH=/kb-media
NEXTCLOUD_PUBLIC_URL=https://your-nextcloud.com/s/

View File

@ -2,34 +2,55 @@
# ForensicPathways Environment Configuration # ForensicPathways Environment Configuration
# =========================================== # ===========================================
# Authentication & OIDC (Required) # === Authentication Configuration ===
AUTH_SECRET=change-this-to-a-strong-secret-key-in-production AUTHENTICATION_NECESSARY=false
AUTHENTICATION_NECESSARY_CONTRIBUTIONS=false
AUTHENTICATION_NECESSARY_AI=false
AUTH_SECRET=your-secret-key-change-in-production
# OIDC Configuration (if authentication enabled)
OIDC_ENDPOINT=https://your-oidc-provider.com OIDC_ENDPOINT=https://your-oidc-provider.com
OIDC_CLIENT_ID=your-oidc-client-id OIDC_CLIENT_ID=your-client-id
OIDC_CLIENT_SECRET=your-oidc-client-secret OIDC_CLIENT_SECRET=your-client-secret
# Auth Scopes - set to true in prod # === AI Configuration ===
AUTHENTICATION_NECESSARY_CONTRIBUTIONS=true # Main AI API (for analysis stage, choose a good model)
AUTHENTICATION_NECESSARY_AI=true
# Application Configuration (Required)
PUBLIC_BASE_URL=https://your-domain.com
NODE_ENV=production
# AI Service Configuration (Required for AI features)
AI_MODEL=mistral-large-latest
AI_API_ENDPOINT=https://api.mistral.ai AI_API_ENDPOINT=https://api.mistral.ai
AI_API_KEY=your-mistral-api-key AI_API_KEY=your-anthropic-api-key
AI_RATE_LIMIT_DELAY_MS=1000 AI_MODEL=claude-sonnet-4-20250514
# Git Integration (Required for contributions) # Selector AI (for selection stage, choode a good model)
GIT_REPO_URL=https://git.cc24.dev/mstoeck3/forensic-pathways AI_SELECTOR_ENDPOINT=https://api.mistral.ai
GIT_PROVIDER=gitea AI_SELECTOR_API_KEY=your-anthropic-api-key
GIT_API_ENDPOINT=https://git.cc24.dev/api/v1 AI_SELECTOR_MODEL=claude-sonnet-4-20250514
GIT_API_TOKEN=your-git-api-token
# File Upload Configuration (Optional) # Analyzer AI (for analysis stage, smaller model sufficient)
LOCAL_UPLOAD_PATH=./public/uploads AI_ANALYZER_ENDPOINT=https://api.mistral.ai
AI_ANALYZER_API_KEY=your-anthropic-api-key
AI_ANALYZER_MODEL=claude-sonnet-4-20250514
# === Embeddings Configuration ===
# Enable/disable semantic embeddings pre-selection
AI_EMBEDDINGS_ENABLED=true
# Embeddings API (Mistral recommended)
AI_EMBEDDINGS_ENDPOINT=https://api.mistral.ai/v1/embeddings
AI_EMBEDDINGS_API_KEY=your-mistral-api-key
AI_EMBEDDINGS_MODEL=mistral-embed
# Embeddings performance settings
AI_EMBEDDINGS_BATCH_SIZE=20
AI_EMBEDDINGS_BATCH_DELAY_MS=1000
AI_EMBEDDING_CANDIDATES=30
AI_SIMILARITY_THRESHOLD=0.3
# === AI Processing Configuration ===
AI_MAX_SELECTED_ITEMS=15
AI_RATE_LIMIT_DELAY_MS=2000
# === Application Configuration ===
PUBLIC_BASE_URL=http://localhost:4321
NODE_ENV=development
# Nextcloud Integration (Optional) # Nextcloud Integration (Optional)
NEXTCLOUD_ENDPOINT=https://your-nextcloud.com NEXTCLOUD_ENDPOINT=https://your-nextcloud.com

1
.gitignore vendored
View File

@ -85,3 +85,4 @@ temp/
.astro/data-store.json .astro/data-store.json
.astro/content.d.ts .astro/content.d.ts
prompt.md prompt.md
.env

25
src/pages/auth/login.ts Normal file
View File

@ -0,0 +1,25 @@
import type { APIRoute } from 'astro';
import { getAuthRequirementForContext } from '../../utils/auth.js';
export const prerender = false;
export const GET: APIRoute = async ({ url }) => {
const returnTo = url.searchParams.get('returnTo') || '/';
const authRequired = getAuthRequirementForContext('general') ||
getAuthRequirementForContext('contributions') ||
getAuthRequirementForContext('ai');
if (!authRequired) {
return new Response(null, {
status: 302,
headers: { 'Location': returnTo }
});
}
if (!process.env.OIDC_ENDPOINT) {
return new Response('Authentication is enabled but not configured', { status: 500 });
}
return new Response('OIDC login not implemented yet', { status: 501 });
};

24
src/pages/auth/status.ts Normal file
View File

@ -0,0 +1,24 @@
import type { APIRoute } from 'astro';
import { withAPIAuth, getAuthRequirementForContext } from '../../utils/auth.js';
export const prerender = false;
export const GET: APIRoute = async ({ request }) => {
const contributionAuth = await withAPIAuth(request, 'contributions');
const aiAuth = await withAPIAuth(request, 'ai');
const generalAuth = await withAPIAuth(request, 'general');
return new Response(JSON.stringify({
authenticated: generalAuth.authenticated,
authRequired: getAuthRequirementForContext('general'),
contributionAuthenticated: contributionAuth.authenticated,
contributionAuthRequired: getAuthRequirementForContext('contributions'),
aiAuthenticated: aiAuth.authenticated,
aiAuthRequired: getAuthRequirementForContext('ai'),
userId: contributionAuth.userId || 'anonymous',
expires: contributionAuth.session?.exp ? new Date(contributionAuth.session.exp * 1000).toISOString() : null
}), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
};

View File

@ -45,11 +45,18 @@ export interface AuthStateData {
function getEnv(key: string): string { function getEnv(key: string): string {
const value = process.env[key]; const value = process.env[key];
if (!value) { if (!value) {
throw new Error(`Missing environment variable: ${key}`); console.warn(`[AUTH] Missing environment variable: ${key}`);
return '';
} }
return value; return value;
} }
function isAnyAuthEnabled(): boolean {
return getAuthRequirement('general') ||
getAuthRequirement('contributions') ||
getAuthRequirement('ai');
}
export function getSessionFromRequest(request: Request): string | null { export function getSessionFromRequest(request: Request): string | null {
const cookieHeader = request.headers.get('cookie'); const cookieHeader = request.headers.get('cookie');
console.log('[DEBUG] Cookie header:', cookieHeader ? 'present' : 'missing'); console.log('[DEBUG] Cookie header:', cookieHeader ? 'present' : 'missing');
@ -141,9 +148,17 @@ export function generateState(): string {
} }
export function generateAuthUrl(state: string): string { export function generateAuthUrl(state: string): string {
if (!isAnyAuthEnabled()) {
throw new Error('Authentication is disabled');
}
const oidcEndpoint = getEnv('OIDC_ENDPOINT'); const oidcEndpoint = getEnv('OIDC_ENDPOINT');
const clientId = getEnv('OIDC_CLIENT_ID'); const clientId = getEnv('OIDC_CLIENT_ID');
const publicBaseUrl = getEnv('PUBLIC_BASE_URL'); const publicBaseUrl = getEnv('PUBLIC_BASE_URL') || 'http://localhost:4321';
if (!oidcEndpoint || !clientId) {
throw new Error('OIDC configuration incomplete');
}
const params = new URLSearchParams({ const params = new URLSearchParams({
response_type: 'code', response_type: 'code',
@ -153,16 +168,24 @@ export function generateAuthUrl(state: string): string {
state: state state: state
}); });
return `${oidcEndpoint}/apps/oidc/authorize?${params.toString()}`; return `${oidcEndpoint}/authorize?${params.toString()}`;
} }
export async function exchangeCodeForTokens(code: string): Promise<{ access_token: string }> { export async function exchangeCodeForTokens(code: string): Promise<{ access_token: string }> {
if (!isAnyAuthEnabled()) {
throw new Error('Authentication is disabled');
}
const oidcEndpoint = getEnv('OIDC_ENDPOINT'); const oidcEndpoint = getEnv('OIDC_ENDPOINT');
const clientId = getEnv('OIDC_CLIENT_ID'); const clientId = getEnv('OIDC_CLIENT_ID');
const clientSecret = getEnv('OIDC_CLIENT_SECRET'); const clientSecret = getEnv('OIDC_CLIENT_SECRET');
const publicBaseUrl = getEnv('PUBLIC_BASE_URL'); const publicBaseUrl = getEnv('PUBLIC_BASE_URL') || 'http://localhost:4321';
const response = await fetch(`${oidcEndpoint}/apps/oidc/token`, { if (!oidcEndpoint || !clientId || !clientSecret) {
throw new Error('OIDC configuration incomplete');
}
const response = await fetch(`${oidcEndpoint}/token`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded', 'Content-Type': 'application/x-www-form-urlencoded',
@ -185,9 +208,17 @@ export async function exchangeCodeForTokens(code: string): Promise<{ access_toke
} }
export async function getUserInfo(accessToken: string): Promise<UserInfo> { export async function getUserInfo(accessToken: string): Promise<UserInfo> {
if (!isAnyAuthEnabled()) {
throw new Error('Authentication is disabled');
}
const oidcEndpoint = getEnv('OIDC_ENDPOINT'); const oidcEndpoint = getEnv('OIDC_ENDPOINT');
const response = await fetch(`${oidcEndpoint}/apps/oidc/userinfo`, { if (!oidcEndpoint) {
throw new Error('OIDC configuration incomplete');
}
const response = await fetch(`${oidcEndpoint}/userinfo`, {
headers: { headers: {
'Authorization': `Bearer ${accessToken}` 'Authorization': `Bearer ${accessToken}`
} }