6. Frontend & Benutzererfahrung: AWS Cloud-basierte SaaS-Plattform-Architektur

6.1. AWS Cognito Authentifizierung

6.1.1. Was ist AWS Cognito?

AWS Cognito ist ein vollständig verwalteter Identitätsdienst für Web- und mobile Anwendungen. Es besteht aus User Pools (Authentifizierung) und Identity Pools (AWS-Ressourcen-Autorisierung) und bietet umfassende Sicherheitsfeatures wie Multi-Faktor-Authentifizierung, adaptive Authentifizierung und Social Login Integration.

AWS Cognito Architektur

6.1.2. Implementierung in unserem Projekt

Wir nutzen Cognito als zentrale Authentifizierungsschicht mit mandanten-bewusster Konfiguration für Multi-Tenant-SaaS.

Lambda-Trigger für Mandanten-Logik:

// Pre-Sign-up Trigger für Domain-Validierung und Mandanten-Zuordnung
exports.handler = async (event) => {
  const email = event.request.userAttributes.email;
  const domain = email.split('@')[1];

  // Domain-Whitelist aus DynamoDB abrufen
  const allowedDomains = await getDomainWhitelist();
  if (!allowedDomains.includes(domain)) {
    throw new Error('Domain nicht für Registrierung autorisiert');
  }

  // Mandanten-ID basierend auf Domain zuweisen
  const tenantId = await getTenantByDomain(domain);
  const subscriptionTier = await getSubscriptionTier(tenantId);

  // Custom Attributes setzen
  event.response.userAttributes = {
    'custom:tenant_id': tenantId,
    'custom:role': 'user',
    'custom:subscription_tier': subscriptionTier,
    'custom:department': 'general'
  };

  // Post-Confirmation Setup
  await createUserInTenantDB({
    cognitoId: event.request.userAttributes.sub,
    email: email,
    tenantId: tenantId,
    status: 'active'
  });

  return event;
};

Token-Management und Session-Sicherheit:

// Robuste Token-Verwaltung mit automatischer Erneuerung
class SecureSessionManager {
  constructor() {
    this.maxIdleTime = 30 * 60 * 1000; // 30 Minuten
    this.lastActivity = Date.now();
    this.setupActivityTracking();
  }

  async getAuthenticatedUserWithContext() {
    try {
      const user = await Auth.currentAuthenticatedUser();
      const session = await Auth.currentSession();

      // Mandanten-Kontext aus Token extrahieren
      const idToken = session.getIdToken();
      const tenantId = idToken.payload['custom:tenant_id'];
      const userRole = idToken.payload['custom:role'];

      return {
        user,
        context: { tenantId, userRole }
      };
    } catch (error) {
      await this.handleAuthError(error);
      return null;
    }
  }

  setupActivityTracking() {
    ['mousedown', 'keypress', 'scroll'].forEach(event => {
      document.addEventListener(event, () => {
        this.lastActivity = Date.now();
      }, { passive: true });
    });

    setInterval(() => this.validateSession(), 60000);
  }

  async validateSession() {
    const timeSinceLastActivity = Date.now() - this.lastActivity;
    if (timeSinceLastActivity > this.maxIdleTime) {
      await Auth.signOut();
      window.location.href = '/login?reason=idle_timeout';
    }
  }
}

6.2. React Router + SPA-Architektur

6.2.1. Was ist React Router und SPA-Architektur?

React Router ermöglicht Client-seitiges Routing in Single-Page-Applications (SPAs). SPAs laden einmal die Basis-HTML-Seite und navigieren zwischen „Seiten“ durch JavaScript-basierte URL-Manipulation, was deutlich schnellere Navigation und bessere UX bietet.

6.2.2. Implementierung in unserem Projekt

Unser mandanten-bewusstes Routing-System implementiert mehrstufige Validierung: Benutzerauthentifizierung, Mandanten-Zugehörigkeit, rollenspezifische Berechtigungen und Feature-Verfügbarkeit.

Route-Guard mit Multi-Level-Validierung:

// ProtectedRoute-Komponente mit umfassender Zugriffskontrolle
interface ProtectedRouteProps {
  children: React.ReactNode;
  requiredRole?: string[];
  requiredPermissions?: string[];
  requiredFeatures?: string[];
  allowedSubscriptionTiers?: string[];
}

const ProtectedRoute: React.FC<ProtectedRouteProps> = ({
  children,
  requiredRole = [],
  requiredPermissions = [],
  requiredFeatures = [],
  allowedSubscriptionTiers = []
}) => {
  const { user, loading } = useAuth();
  const { tenant, permissions, features } = useTenant();
  const navigate = useNavigate();
  const location = useLocation();

  useEffect(() => {
    if (!loading && !user) {
      navigate('/login', {
        state: { from: location.pathname },
        replace: true
      });
      return;
    }

    if (user && tenant) {
      // Rollen-basierte Zugriffskontrolle
      if (requiredRole.length > 0 && !requiredRole.includes(user.role)) {
        navigate('/unauthorized', { replace: true });
        return;
      }

      // Berechtigungs-basierte Kontrolle
      if (requiredPermissions.length > 0) {
        const hasAllPermissions = requiredPermissions.every(
          permission => permissions.includes(permission)
        );
        if (!hasAllPermissions) {
          navigate('/access-denied', { replace: true });
          return;
        }
      }

      // Feature-Flag-Kontrolle
      if (requiredFeatures.length > 0) {
        const hasAllFeatures = requiredFeatures.every(
          feature => features[feature]
        );
        if (!hasAllFeatures) {
          navigate('/upgrade-required', { replace: true });
          return;
        }
      }
    }
  }, [user, tenant, permissions, features, loading]);

  if (loading) return <RouteLoadingSpinner />;
  if (!user || !tenant) return null;

  return <>{children}</>;
};

Hierarchische Route-Struktur:

// Verschachtelte Route-Struktur mit Mandanten-Kontext
function AppRoutes() {
  const { hasFeature, subscriptionTier } = useTenant();

  return (
    <Routes>
      <Route path="/" element={<PublicLayout />}>
        <Route index element={<LandingPage />} />
        <Route path="login" element={<LoginPage />} />
        <Route path="register" element={<RegisterPage />} />
      </Route>

      <Route path="/app" element={
        <ProtectedRoute><AppLayout /></ProtectedRoute>
      }>
        <Route index element={<Dashboard />} />

        <Route path="projects" element={<ProjectLayout />}>
          <Route index element={<ProjectList />} />
          <Route path="new" element={<CreateProject />} />
          <Route path=":projectId" element={<ProjectDetail />}>
            <Route index element={<ProjectOverview />} />
            <Route path="tasks" element={<TaskManager />} />

            {hasFeature('advanced-analytics') && (
              <Route path="analytics" element={<AdvancedAnalytics />} />
            )}
          </Route>
        </Route>

        {(subscriptionTier === 'premium' || subscriptionTier === 'enterprise') && (
          <Route path="team" element={
            <ProtectedRoute requiredFeatures={['team-management']}>
              <TeamLayout />
            </ProtectedRoute>
          }>
            <Route index element={<TeamOverview />} />
            <Route path="members" element={<TeamMembers />} />
          </Route>
        )}

        <Route path="admin" element={
          <ProtectedRoute
            requiredRole={['admin', 'super_admin']}
            requiredPermissions={['admin_access']}
          >
            <AdminLayout />
          </ProtectedRoute>
        }>
          <Route index element={<AdminDashboard />} />
          <Route path="users" element={<UserManagement />} />
          <Route path="settings" element={<TenantSettings />} />
        </Route>
      </Route>

      <Route path="/unauthorized" element={<UnauthorizedPage />} />
      <Route path="*" element={<NotFoundPage />} />
    </Routes>
  );
}

6.3. AWS Amplify Integration

6.3.1. Was ist AWS Amplify?

AWS Amplify ist eine Entwicklungsplattform für moderne Web- und mobile Anwendungen, die AWS-Service-Komplexität abstrahiert und CLI für Infrastruktur-Management, Client-Bibliotheken und Hosting-Services bietet.

6.3.2. Implementierung in unserem Projekt

Amplify übernimmt die gesamte AWS-Service-Orchestrierung mit automatischem JWT-Token-Management und transparenten Authorization-Headern.

Mandanten-sichere API und Storage Integration:

// Umfassende Amplify-Konfiguration für Multi-Tenant-Setup
const amplifyConfig = {
  Auth: {
    userPoolId: 'us-east-1_iHANOSeYr',
    userPoolWebClientId: '44b2tbimdgd352292qhcacin06',
    mandatorySignIn: true,
    authenticationFlowType: 'USER_SRP_AUTH',
    attributes: {
      email: 'email',
      'custom:tenant_id': 'tenantId',
      'custom:role': 'role',
      'custom:subscription_tier': 'subscriptionTier'
    }
  },
  API: {
    endpoints: [
      {
        name: 'tenantAPI',
        endpoint: 'https://api.yourdomain.com/tenant',
        custom_header: async () => {
          const session = await Auth.currentSession();
          const token = session.getIdToken().getJwtToken();
          const tenantId = session.getIdToken().payload['custom:tenant_id'];

          return {
            Authorization: `Bearer ${token}`,
            'X-Tenant-ID': tenantId,
            'Content-Type': 'application/json'
          };
        }
      }
    ]
  },
  Storage: {
    AWSS3: {
      bucket: 'tenant-file-storage',
      region: 'us-east-1',
      level: 'private'
    }
  }
};

// Mandanten-spezifischer Datei-Upload mit automatischer Pfad-Trennung
const uploadTenantFile = async (file: File, tenantId: string) => {
  const fileName = `tenant-${tenantId}/${Date.now()}-${file.name}`;

  try {
    const result = await Storage.put(fileName, file, {
      level: 'private',
      metadata: {
        tenantId: tenantId,
        uploadedBy: (await Auth.currentUserInfo()).username,
        originalName: file.name
      }
    });

    return {
      success: true,
      key: result.key,
      location: `https://${amplifyConfig.Storage.AWSS3.bucket}.s3.amazonaws.com/${result.key}`
    };
  } catch (error) {
    console.error('File upload failed:', error);
    throw new Error('Datei-Upload fehlgeschlagen');
  }
};

// API-Aufrufe mit automatischer Mandanten-Kontextualisierung
const tenantAPIService = {
  async getUsers() {
    return await API.get('tenantAPI', '/users', {});
  },

  async createProject(projectData) {
    return await API.post('tenantAPI', '/projects', {
      body: {
        ...projectData,
        createdBy: (await Auth.currentUserInfo()).username
      }
    });
  },

  async getProjectsByTenant() {
    const session = await Auth.currentSession();
    const tenantId = session.getIdToken().payload['custom:tenant_id'];

    return await API.get('tenantAPI', `/projects?tenantId=${tenantId}`, {});
  }
};
Amplify Service Architektur

6.4. CloudFront + S3 Content Delivery

6.4.1. Was ist CloudFront + S3?

Amazon CloudFront ist ein CDN mit über 200 Edge-Standorten weltweit. In Kombination mit S3 entsteht eine hochperformante Lösung für globale Web-Anwendungsbereitstellung mit automatischer SSL/TLS-Verschlüsselung.

6.4.2. Implementierung in unserem Projekt

React-SPA wird vollständig über CloudFront ausgeliefert mit S3 als Origin und Origin Access Identity für Security.

Cache-Strategien und Deployment:

# CloudFormation Cache-Behaviors für optimale Performance
CacheBehaviors:
  - PathPattern: "*.js"
    CachePolicyId: "4135ea2d-6df8-44a3-9df3-4b5a84be39ad"  # CachingOptimized
    TTL: 31536000  # 1 Jahr für JavaScript mit Fingerprints
    Compress: true

  - PathPattern: "*.css"
    CachePolicyId: "4135ea2d-6df8-44a3-9df3-4b5a84be39ad"
    TTL: 31536000  # 1 Jahr für CSS mit Fingerprints
    Compress: true

  - PathPattern: "*.png|*.jpg|*.gif|*.svg"
    CachePolicyId: "4135ea2d-6df8-44a3-9df3-4b5a84be39ad"
    TTL: 86400  # 1 Tag für Bilder
    Compress: true

  - PathPattern: "index.html"
    CachePolicyId: "4135ea2d-6df8-44a3-9df3-4b5a84be39ad"
    TTL: 0  # Kein Cache für HTML - SPA-Routing

  - PathPattern: "*"
    CachePolicyId: "658327ea-f89d-4fab-a63d-7e88639e58f6"  # CachingDisabled
    TTL: 0  # Fallback ohne Cache
# Automated Deployment Pipeline mit Cache-Invalidierung
#!/bin/bash

# Build React Application
echo "Building React application..."
npm run build

if [ $? -ne 0 ]; then
  echo "Build failed!"
  exit 1
fi

# Sync to S3 mit optimierten Optionen
echo "Syncing to S3..."
aws s3 sync build/ s3://frontend-bucket \
  --delete \
  --cache-control "public,max-age=31536000" \
  --exclude "*.html" \
  --exclude "service-worker.js"

# HTML-Dateien ohne Cache
aws s3 sync build/ s3://frontend-bucket \
  --cache-control "no-cache,no-store,must-revalidate" \
  --include "*.html" \
  --include "service-worker.js"

# CloudFront Cache Invalidation
echo "Invalidating CloudFront cache..."
INVALIDATION_ID=$(aws cloudfront create-invalidation \
  --distribution-id $CLOUDFRONT_DISTRIBUTION_ID \
  --paths "/*" \
  --query 'Invalidation.Id' \
  --output text)

echo "Waiting for invalidation $INVALIDATION_ID to complete..."
aws cloudfront wait invalidation-completed \
  --distribution-id $CLOUDFRONT_DISTRIBUTION_ID \
  --id $INVALIDATION_ID

echo "Deployment completed successfully!"
CloudFront Cache Behaviors

6.5. Multi-Tenant Frontend-Architektur

6.5.1. Was ist Multi-Tenant Frontend?

Multi-Tenant-Architektur bedeutet, dass eine Anwendungsinstanz mehrere Kunden (Mandanten) bedient. Jeder Mandant erhält individuelles Branding, Feature-Kontrolle und Konfigurationen bei gleichzeitiger Entwicklungskonsistenz.

Multi-Tenant Dashboard Vergleich

6.5.2. Implementierung in unserem Projekt

TenantContext verwaltet Mandanten-Zustand mit JWT-Token-Daten und Custom Hooks für transparente API-Integration.

TenantContext und Dynamic Theming:

// TenantContext für mandanten-bewusste Komponenten
const TenantContext = createContext<TenantContextType | null>(null);

export const TenantProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
  const [tenant, setTenant] = useState<Tenant | null>(null);
  const [theme, setTheme] = useState<ThemeConfig | null>(null);

  useEffect(() => {
    if (tenant) {
      // Dynamisches Theming anwenden
      document.documentElement.style.setProperty('--primary-color', tenant.primaryColor);
      document.documentElement.style.setProperty('--secondary-color', tenant.secondaryColor);
      document.querySelector('link[rel="icon"]')?.setAttribute('href', tenant.favicon);
      document.title = `${tenant.name} - SaaS Platform`;
    }
  }, [tenant]);

  const contextValue = useMemo(() => ({
    tenant,
    theme,
    setTenant,
    hasFeature: (feature: string) => tenant?.features?.includes(feature) || false,
    subscriptionTier: tenant?.subscriptionTier || 'basic',
    isFeatureEnabled: (feature: string) => {
      const tierFeatures = {
        basic: ['projects', 'users'],
        premium: ['projects', 'users', 'analytics', 'team-management'],
        enterprise: ['projects', 'users', 'analytics', 'team-management', 'api-access', 'white-label']
      };
      return tierFeatures[tenant?.subscriptionTier || 'basic']?.includes(feature) || false;
    }
  }), [tenant, theme]);

  return (
    <TenantContext.Provider value={contextValue}>
      {children}
    </TenantContext.Provider>
  );
};

// Custom Hook für Mandanten-Features
export const useTenant = () => {
  const context = useContext(TenantContext);
  if (!context) {
    throw new Error('useTenant must be used within TenantProvider');
  }
  return context;
};

// Feature-Flag-basierte Komponenten-Rendering
const Dashboard = () => {
  const { hasFeature, tenant, subscriptionTier } = useTenant();

  return (
    <div className="dashboard">
      <h1>Welcome to {tenant?.name}</h1>
      <BasicDashboard />

      {hasFeature('advanced-analytics') && (
        <Suspense fallback={<AnalyticsLoading />}>
          <AdvancedAnalytics />
        </Suspense>
      )}

      {subscriptionTier === 'enterprise' && hasFeature('api-management') && (
        <APIManagementPanel />
      )}

      {!hasFeature('team-management') && (
        <UpgradePrompt feature="team-management" />
      )}
    </div>
  );
};

6.6. Fazit

Die Implementierung einer modernen Frontend-Architektur für Multi-Tenant-SaaS-Anwendungen auf AWS stellt einen komplexen aber zukunftsweisenden Ansatz dar, der erhebliche Vorteile in Bezug auf Skalierbarkeit, Sicherheit und Benutzererfahrung bietet. Die Kombination aus AWS Cognito für Authentifizierung, React Router für SPA-Navigation, AWS Amplify für Service-Integration und CloudFront mit S3 für Content Delivery schafft eine hochperformante und sichere Plattform, die den aktuellen Stand der Technik für enterprise-grade SaaS-Anwendungen repräsentiert.

Besonders hervorzuheben ist die nahtlose Integration dieser Services, die es ermöglicht, komplexe Authentifizierungs- und Autorisierungslogik elegant zu abstrahieren. Die JWT-basierte Token-Verwaltung mit mandanten-spezifischen Custom Attributes zeigt exemplarisch, wie moderne Identitätsmanagement-Systeme effektiv für Multi-Tenant-Szenarien genutzt werden können. Die implementierte Multi-Tenant-Frontend-Architektur demonstriert dabei die Möglichkeit, eine einzige Anwendungsinstanz für multiple Kunden zu betreiben, ohne dabei Sicherheit oder Individualisierung zu kompromittieren. Der TenantContext und die dynamische Theming-Funktionalität ermöglichen es, jedem Mandanten eine personalisierte Erfahrung zu bieten, während gleichzeitig die Entwicklungs- und Wartungskosten minimiert werden.

Die CloudFront-Integration mit intelligenten Cache-Strategien verdeutlicht, wie moderne CDN-Technologien genutzt werden können, um weltweite Performance zu optimieren. Die differenzierten Cache-Policies für verschiedene Asset-Typen demonstrieren tiefes Verständnis für Web-Performance-Optimierung und SPA-spezifische Anforderungen. Gleichzeitig erfüllt die implementierte Sicherheitsarchitektur mit mehrstufiger Autorisierung moderne Compliance-Anforderungen und bietet granulare Zugriffskontrolle durch Feature-Flags und subscription-basierte Zugriffskontrolle, die zusätzlich flexible Monetarisierungsmodelle schaffen.

Trotz der technischen Exzellenz bleiben jedoch Herausforderungen bestehen: Die Komplexität der Service-Orchestrierung erfordert tiefes AWS-Fachwissen, und die Abhängigkeit von Cloud-Provider-spezifischen Services kann Vendor-Lock-in-Risiken schaffen. Zukünftige Entwicklungen sollten daher Edge Computing mit AWS Lambda@Edge, Progressive Web App-Features und erweiterte Offline-Funktionalitäten berücksichtigen, um die Plattform weiterzuentwickeln.

Zusammenfassend bietet die vorgestellte Architektur eine solide Grundlage für skalierbare, sichere und benutzerfreundliche Multi-Tenant-Plattformen. Die Investition in diesen Technologie-Stack zahlt sich langfristig durch reduzierte Betriebskosten, verbesserte Sicherheit und erhöhte Entwicklungsgeschwindigkeit aus. Für Unternehmen, die eine zukunftssichere SaaS-Plattform entwickeln möchten, stellt diese Architektur eine empfehlenswerte Lösung dar, die sowohl aktuelle Anforderungen erfüllt als auch Raum für zukünftige Erweiterungen bietet.

6.7. Quellen