Fix jwt-token

This commit is contained in:
2025-09-12 12:11:14 +02:00
parent 8b0234986d
commit d8bba047bb
14 changed files with 3236 additions and 1 deletions

View File

@@ -0,0 +1,305 @@
/**
* Enhanced Login Component with Multi-Tenant Support
* Handles different authentication providers based on tenant configuration
*/
import React, { useState, useEffect } from 'react';
import { Navigate, useSearchParams } from 'react-router-dom';
import { useMultiTenantAuth } from '../contexts/MultiTenantAuthContext';
import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline';
import toast from 'react-hot-toast';
const MultiTenantLogin = () => {
const [searchParams] = useSearchParams();
const [credentials, setCredentials] = useState({
username: '',
password: ''
});
const [showPassword, setShowPassword] = useState(false);
const {
login,
loginWithSSO,
loading,
isAuthenticated,
tenant,
authConfig,
error
} = useMultiTenantAuth();
const tenantParam = searchParams.get('tenant');
const errorParam = searchParams.get('error');
useEffect(() => {
// Handle SSO errors from URL parameters
if (errorParam) {
const errorMessages = {
'auth_failed': 'Authentication failed. Please try again.',
'auth_cancelled': 'Authentication was cancelled.',
'user_creation_failed': 'Failed to create user account.',
'saml_error': 'SAML authentication error.',
'oauth_error': 'OAuth authentication error.'
};
toast.error(errorMessages[errorParam] || 'Authentication error occurred.');
}
}, [errorParam]);
if (isAuthenticated) {
return <Navigate to="/" replace />;
}
const handleSubmit = async (e) => {
e.preventDefault();
if (!credentials.username || !credentials.password) {
toast.error('Please fill in all fields');
return;
}
const result = await login(credentials);
if (result.success && !result.redirect) {
toast.success('Login successful!');
} else if (!result.success) {
toast.error(result.error || 'Login failed');
}
// If redirect is true, the page will redirect to SSO provider
};
const handleChange = (e) => {
setCredentials({
...credentials,
[e.target.name]: e.target.value
});
};
const handleSSOLogin = () => {
loginWithSSO();
};
const renderAuthenticationMethod = () => {
if (!authConfig) {
return (
<div className="text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600 mx-auto"></div>
<p className="mt-2 text-sm text-gray-600">Loading authentication configuration...</p>
</div>
);
}
const { provider, features } = authConfig;
// SSO-only providers
if (provider === 'saml' || provider === 'oauth') {
return (
<div className="space-y-4">
<div className="text-center">
<h3 className="text-lg font-medium text-gray-900 mb-2">
Single Sign-On Required
</h3>
<p className="text-sm text-gray-600 mb-4">
Please sign in using your organization's identity provider.
</p>
</div>
<button
onClick={handleSSOLogin}
disabled={loading}
className="group relative w-full flex justify-center py-3 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 disabled:opacity-50 disabled:cursor-not-allowed"
>
{loading ? (
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white"></div>
) : (
<>
{provider === 'saml' ? '🔐 Sign in with SAML' : '🔑 Sign in with OAuth'}
</>
)}
</button>
{tenant && tenant !== 'default' && (
<div className="text-center">
<p className="text-xs text-gray-500">
Organization: <span className="font-medium">{tenant}</span>
</p>
</div>
)}
</div>
);
}
// Local or LDAP authentication with form
return (
<form className="mt-8 space-y-6" onSubmit={handleSubmit}>
<div className="rounded-md shadow-sm -space-y-px">
<div>
<label htmlFor="username" className="sr-only">
Username or Email
</label>
<input
id="username"
name="username"
type="text"
required
className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-primary-500 focus:border-primary-500 focus:z-10 sm:text-sm"
placeholder="Username or Email"
value={credentials.username}
onChange={handleChange}
disabled={loading}
/>
</div>
<div className="relative">
<label htmlFor="password" className="sr-only">
Password
</label>
<input
id="password"
name="password"
type={showPassword ? 'text' : 'password'}
required
className="appearance-none rounded-none relative block w-full px-3 py-2 pr-10 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-primary-500 focus:border-primary-500 focus:z-10 sm:text-sm"
placeholder="Password"
value={credentials.password}
onChange={handleChange}
disabled={loading}
/>
<button
type="button"
className="absolute inset-y-0 right-0 pr-3 flex items-center"
onClick={() => setShowPassword(!showPassword)}
>
{showPassword ? (
<EyeSlashIcon className="h-4 w-4 text-gray-400" />
) : (
<EyeIcon className="h-4 w-4 text-gray-400" />
)}
</button>
</div>
</div>
<div>
<button
type="submit"
disabled={loading}
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 disabled:opacity-50 disabled:cursor-not-allowed"
>
{loading ? (
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white"></div>
) : (
'Sign in'
)}
</button>
</div>
{/* Show additional SSO option if available */}
{features?.sso_login && (
<div className="mt-4">
<div className="relative">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-gray-300" />
</div>
<div className="relative flex justify-center text-sm">
<span className="px-2 bg-white text-gray-500">Or</span>
</div>
</div>
<button
type="button"
onClick={handleSSOLogin}
className="mt-3 w-full flex justify-center py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
>
🔑 Sign in with SSO
</button>
</div>
)}
{/* Demo credentials for local auth */}
{provider === 'local' && (
<div className="text-center">
<p className="text-sm text-gray-600">
Demo credentials: <br />
Username: <code className="bg-gray-100 px-1 rounded">admin</code> <br />
Password: <code className="bg-gray-100 px-1 rounded">admin123</code>
</p>
</div>
)}
{/* LDAP info */}
{provider === 'ldap' && (
<div className="text-center">
<p className="text-sm text-gray-600">
Use your Active Directory credentials
</p>
</div>
)}
</form>
);
};
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-md w-full space-y-8">
<div>
<div className="mx-auto h-12 w-12 flex items-center justify-center rounded-full bg-primary-100">
<svg className="h-8 w-8 text-primary-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
</svg>
</div>
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
Drone Detection System
</h2>
<p className="mt-2 text-center text-sm text-gray-600">
Sign in to your account
</p>
{/* Tenant indicator */}
{tenant && tenant !== 'default' && (
<div className="mt-2 text-center">
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-primary-100 text-primary-800">
Organization: {tenant}
</span>
</div>
)}
</div>
{error && (
<div className="rounded-md bg-red-50 p-4">
<div className="text-sm text-red-700">{error}</div>
</div>
)}
{renderAuthenticationMethod()}
{/* Tenant switcher for development */}
{process.env.NODE_ENV === 'development' && (
<div className="text-center">
<details className="text-xs text-gray-500">
<summary className="cursor-pointer">Development Options</summary>
<div className="mt-2 space-y-1">
<button
onClick={() => window.location.href = '/login?tenant=default'}
className="block w-full text-left hover:text-primary-600"
>
Switch to Default Tenant
</button>
<button
onClick={() => window.location.href = '/login?tenant=demo-saml'}
className="block w-full text-left hover:text-primary-600"
>
Switch to SAML Demo
</button>
<button
onClick={() => window.location.href = '/login?tenant=demo-oauth'}
className="block w-full text-left hover:text-primary-600"
>
Switch to OAuth Demo
</button>
</div>
</details>
</div>
)}
</div>
</div>
);
};
export default MultiTenantLogin;