fix auth endpoint
This commit is contained in:
parent
5f190fbf02
commit
336bfa0c99
File diff suppressed because one or more lines are too long
60
.env
Normal file
60
.env
Normal 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/
|
67
.env.example
67
.env.example
@ -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
1
.gitignore
vendored
@ -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
25
src/pages/auth/login.ts
Normal 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
24
src/pages/auth/status.ts
Normal 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' }
|
||||||
|
});
|
||||||
|
};
|
@ -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}`
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user