feat: Entra ID login + settings page for integrations

- Add SQLite settings table with getSetting/setSetting/getAllSettings helpers
- Implement Azure OAuth2 authorization code flow via @azure/msal-node
- Add public GET /api/auth/config endpoint for frontend activation check
- Add admin-only GET/PUT /api/settings API with masked secret fields
- CheckMK sync reads credentials from DB settings (env vars as fallback)
- New Settings.tsx: Entra ID and CheckMK configuration cards
- LoginPage: "Sign in with Microsoft" button, shown only when Azure is active
- App.tsx: OAuth callback handling (?token=/?auth_error=), Settings tab for admins
This commit is contained in:
Brückner
2026-06-03 16:02:47 +02:00
parent eed01b9665
commit d364aea4c1
5 changed files with 569 additions and 11 deletions

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import { GhostGridLogo } from './Header';
import { authFetch, saveSession } from '../lib/auth';
import { User } from '../types';
@ -7,14 +7,23 @@ import { LogIn, Eye, EyeOff, AlertCircle } from 'lucide-react';
interface LoginPageProps {
onLogin: (user: User) => void;
onNavigateToRegister: () => void;
authError?: string;
}
export default function LoginPage({ onLogin, onNavigateToRegister }: LoginPageProps) {
export default function LoginPage({ onLogin, onNavigateToRegister, authError }: LoginPageProps) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [showPassword, setShowPassword] = useState(false);
const [error, setError] = useState('');
const [error, setError] = useState(authError || '');
const [loading, setLoading] = useState(false);
const [azureEnabled, setAzureEnabled] = useState(false);
useEffect(() => {
fetch('/api/auth/config')
.then(r => r.json())
.then(d => setAzureEnabled(Boolean(d.azureEnabled)))
.catch(() => {});
}, []);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
@ -137,6 +146,30 @@ export default function LoginPage({ onLogin, onNavigateToRegister }: LoginPagePr
</button>
</form>
{azureEnabled && (
<>
<div className="flex items-center gap-3">
<div className="flex-1 h-px bg-slate-800" />
<span className="text-[10px] font-mono text-slate-500 uppercase tracking-widest">oder</span>
<div className="flex-1 h-px bg-slate-800" />
</div>
<button
type="button"
onClick={() => { window.location.href = '/api/auth/azure'; }}
className="w-full flex items-center justify-center gap-3 bg-slate-800 hover:bg-slate-700 border border-slate-700 hover:border-slate-600 text-white font-semibold text-sm py-2.5 rounded-lg transition-all"
>
{/* Microsoft M logo */}
<svg width="18" height="18" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<rect x="1" y="1" width="9" height="9" fill="#F25022" />
<rect x="11" y="1" width="9" height="9" fill="#7FBA00" />
<rect x="1" y="11" width="9" height="9" fill="#00A4EF" />
<rect x="11" y="11" width="9" height="9" fill="#FFB900" />
</svg>
Mit Microsoft anmelden
</button>
</>
)}
<p className="text-center text-xs text-slate-400">
No account yet?{' '}
<button