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
# ===========================================
# Authentication & OIDC (Required)
AUTH_SECRET=change-this-to-a-strong-secret-key-in-production
# === 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://your-oidc-provider.com
OIDC_CLIENT_ID=your-oidc-client-id
OIDC_CLIENT_SECRET=your-oidc-client-secret
OIDC_CLIENT_ID=your-client-id
OIDC_CLIENT_SECRET=your-client-secret
# Auth Scopes - set to true in prod
AUTHENTICATION_NECESSARY_CONTRIBUTIONS=true
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 Configuration ===
# Main AI API (for analysis stage, choose a good model)
AI_API_ENDPOINT=https://api.mistral.ai
AI_API_KEY=your-mistral-api-key
AI_RATE_LIMIT_DELAY_MS=1000
AI_API_KEY=your-anthropic-api-key
AI_MODEL=claude-sonnet-4-20250514
# Git Integration (Required for contributions)
GIT_REPO_URL=https://git.cc24.dev/mstoeck3/forensic-pathways
GIT_PROVIDER=gitea
GIT_API_ENDPOINT=https://git.cc24.dev/api/v1
GIT_API_TOKEN=your-git-api-token
# Selector AI (for selection stage, choode a good model)
AI_SELECTOR_ENDPOINT=https://api.mistral.ai
AI_SELECTOR_API_KEY=your-anthropic-api-key
AI_SELECTOR_MODEL=claude-sonnet-4-20250514
# File Upload Configuration (Optional)
LOCAL_UPLOAD_PATH=./public/uploads
# Analyzer AI (for analysis stage, smaller model sufficient)
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_ENDPOINT=https://your-nextcloud.com

1
.gitignore vendored
View File

@ -85,3 +85,4 @@ temp/
.astro/data-store.json
.astro/content.d.ts
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 {
const value = process.env[key];
if (!value) {
throw new Error(`Missing environment variable: ${key}`);
console.warn(`[AUTH] Missing environment variable: ${key}`);
return '';
}
return value;
}
function isAnyAuthEnabled(): boolean {
return getAuthRequirement('general') ||
getAuthRequirement('contributions') ||
getAuthRequirement('ai');
}
export function getSessionFromRequest(request: Request): string | null {
const cookieHeader = request.headers.get('cookie');
console.log('[DEBUG] Cookie header:', cookieHeader ? 'present' : 'missing');
@ -141,9 +148,17 @@ export function generateState(): string {
}
export function generateAuthUrl(state: string): string {
if (!isAnyAuthEnabled()) {
throw new Error('Authentication is disabled');
}
const oidcEndpoint = getEnv('OIDC_ENDPOINT');
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({
response_type: 'code',
@ -153,16 +168,24 @@ export function generateAuthUrl(state: string): string {
state: state
});
return `${oidcEndpoint}/apps/oidc/authorize?${params.toString()}`;
return `${oidcEndpoint}/authorize?${params.toString()}`;
}
export async function exchangeCodeForTokens(code: string): Promise<{ access_token: string }> {
if (!isAnyAuthEnabled()) {
throw new Error('Authentication is disabled');
}
const oidcEndpoint = getEnv('OIDC_ENDPOINT');
const clientId = getEnv('OIDC_CLIENT_ID');
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',
headers: {
'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> {
if (!isAnyAuthEnabled()) {
throw new Error('Authentication is disabled');
}
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: {
'Authorization': `Bearer ${accessToken}`
}