⚠️ Draft documentation
Work in progress — some sections may be incomplete, typos possible. Last updated: 2026-05-10.
EN | DE | RU | FR | ES
DE

Quelldurchlauf: src/actions/authentication.js

Datei: authentication.js auf develop (85 Zeilen)

Stand: unverändert zwischen develop → unserem Branch

Getestet durch: actions/authentication.test.js

Zugehörig: reducers/authentication.js – verarbeitet die hier dispatchten Actions

1. Importe (Zeile 1)

import { getServiceURL, handleHTTPErrors } from '../utilities/rest';

Zwei Hilfsfunktionen aus rest.js:

Keine weiteren Abhängigkeiten. Die Datei ist mit diesen beiden Hilfsfunktionen komplett eigenständig.

2. Konstanten für Action-Typen (Zeilen 3–5)

export const USER_LOGIN_BEGIN   = 'USER_LOGIN_BEGIN';
export const USER_LOGIN_SUCCESS = 'USER_LOGIN_SUCCESS';
export const USER_LOGIN_FAILURE = 'USER_LOGIN_FAILURE';

Drei String-Konstanten, die für die Verwendung im Reducer exportiert werden (reducers/authentication.js). Der switch(type)-Fall im Reducer matcht exakt auf diese Strings.

Warum Strings, keine Symbole? Redux-Konvention – Strings sind serialisierbar, in Redux DevTools debugbar und funktionieren modulübergreifend ohne geteilte Referenzen.

Hinweis: USER_LOGOUT war früher hier – entfernt in Commit 7c60c2fbb (Jul 2019), als das Logout auf das reine Dispatch von USER_LOGIN_BEGIN (Reset des States) vereinfacht wurde.

3. Action Creator (Zeilen 7–26)

export const userLoginBegin = () => ({
    type: USER_LOGIN_BEGIN,
});

export const userLoginSuccess = (user, version, buildTimestamp, alertMessage) => ({
    type: USER_LOGIN_SUCCESS,
    payload: { user, version, buildTimestamp, alertMessage },
});

export const userLoginFailure = (error) => ({
    type: USER_LOGIN_FAILURE,
    payload: { error },
});

Action Creator – reine Funktionen, die Plain-Objects zurückgeben. Die synchronen „Nachrichten“, die an den Reducer gesendet werden.

CreatorSignaturWas der Reducer damit macht
userLoginBegin() () → { type: BEGIN } Setzt zurück auf { loading: true, error: null, user: null } – Spinner aktiv, alte Daten gelöscht
userLoginSuccess(user, version, buildTimestamp, alertMessage) (obj, str, str, str?) → { type, payload } Schreibt alle vier Felder in den State – user, version, buildTimestamp, alertMessage. loading → false
userLoginFailure(error) (str) → { type, payload: { error } } Speichert Fehlermeldung, löscht user, loading → false

Diese werden von den Thunks unten aufgerufen – niemals direkt von Komponenten dispatcht.

4. catchError-Hilfsfunktion (Zeile 28)

const catchError = (dispatch) => (error) => dispatch(userLoginFailure(error.message));

Gecurriede Funktion: catchError(dispatch)(error).

Wird in .catch()-Ketten sowohl von login() als auch loadUserStatus() verwendet:

5. loadUserStatus – Sitzungsprüfung (Zeilen 30–63)

export const loadUserStatus = () => (dispatch) => {
    dispatch(userLoginBegin());                                    // (a)

    return fetch(                                                   // (b)
        getServiceURL('userStatus'),
        { method: 'GET', credentials: 'include' },
    )
        .then(handleHTTPErrors)                                    // (c)
        .then((response) => response.json())                       // (d)
        .then(({ userData, systemData, alertMessage }) => {        // (e)
            dispatch(userLoginSuccess(
                userData,
                systemData.version,
                systemData.buildTimestamp,
                alertMessage,
            ));
        })
        .catch(() => {                                             // (f)
            const { pathname, search } = window.location;
            const href = pathname + search;
            if (!pathname.startsWith('/react/public/login')
                && !pathname.startsWith('/react/public/datatransfer/')) {
                window.location.href =
                    `/react/public/login?url=${encodeURIComponent(href)}`;
            }
            catchError(dispatch)({ message: undefined });
        });
};

Zweck: „Wer bin ich?“ – wird beim App-Start und nach dem Login aufgerufen, um zu prüfen, ob der Benutzer eine gültige Sitzung hat.

(a) Dispatcht USER_LOGIN_BEGIN – Spinner aktiv, alter State gelöscht.

(b) GET /rs/userStatus – sendet das Session-Cookie (credentials: 'include'). Trifft auf UserStatusRest.kt:111.

(c) handleHTTPErrors – wenn Status ≠ 2xx, wird Error('Fetch failed: Error {status}') ausgelöst. Landet im .catch()-Block.

(d) JSON-Antwort parsen. Erwartete Struktur: { userData: {...}, systemData: {version, buildTimestamp}, alertMessage?: string }.

(e) Destructuring und Dispatch von USER_LOGIN_SUCCESS mit allen Feldern. Der Reducer schreibt sie in den State.

(f) Sitzung ungültig oder abgelaufen. Zwei Fälle:

Dispatch: catchError(dispatch)({ message: undefined })error.message ist undefined. Der Reducer speichert { error: undefined } – löscht effektiv vorherige Fehler.

Wird aufgerufen:

6. login – Authentifizierung (Zeilen 65–85)

export const login = (username, password, keepSignedIn) => (dispatch) => {
    dispatch(userLoginBegin());                                    // (a)

    return fetch(                                                   // (b)
        getServiceURL('/rsPublic/login'),
        {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                username,
                password,
                stayLoggedIn: keepSignedIn,                        // (c)
            }),
            credentials: 'include',
        },
    )
        .then(handleHTTPErrors)                                    // (d)
        .then(() => loadUserStatus()(dispatch))                    // (e)
        .catch(catchError(dispatch));                              // (f)
};

Zweck: Sendet Anmeldeinformationen an den Server. Bei Erfolg wird die Sitzung abgefragt, um Benutzerdaten zu erhalten.

(a) Dispatcht USER_LOGIN_BEGIN – Spinner aktiv.

(b) POST /rsPublic/login mit JSON-Body. Trifft auf LoginPageRest.kt:93LoginService.authenticate(). Der Server setzt beim Erfolg das JSESSIONID-Cookie.

(c) stayLoggedIn: keepSignedIn – das „Angemeldet bleiben“-Flag. Wenn true, setzt der Server zusätzlich ein stayLoggedIn-Cookie (CookieService.kt:201) gültig für 30 Tage. Beim nächsten Besuch erneuert dieses Cookie automatisch die Sitzung ohne Passwort.

(d) handleHTTPErrors – wenn der Login fehlschlägt (401, 403 usw.), wird eine Ausnahme ausgelöst und landet im .catch().

(e) Bei Erfolg (200): Weiterleitung in loadUserStatus(). Login authentifiziert nur – wir müssen immer noch wissen, wer sich angemeldet hat. loadUserStatus()(dispatch) ruft den Thunk manuell auf (er gibt (dispatch) => {...} zurück, also wird er mit dispatch aufgerufen). Daher erwartet der Test 3 Actions: BEGIN (Login) + BEGIN (loadUserStatus) + SUCCESS (loadUserStatus).

(f) Bei Fehler: catchError(dispatch) dispatcht USER_LOGIN_FAILURE mit der Fehlermeldung – z. B. 'Fetch failed: Error 401' bei falschem Passwort.

Zusammenfassung: Action-Flow

APP-START:
  ProjectForge.jsx → dispatch(loadUserStatus())
    └─ GET /rs/userStatus
         ├─ 200 → SUCCESS(userData, systemData, alertMessage)
         └─ 401 → Weiterleitung zu /react/public/login

LOGIN:
  login(username, password, keepSignedIn)
    ├─ dispatch(BEGIN)
    ├─ POST /rsPublic/login
    ├─ Server setzt JSESSIONID (+ stayLoggedIn-Cookie wenn keepSignedIn)
    ├─ 200 → loadUserStatus()(dispatch)
    │   └─ GET /rs/userStatus → BEGIN → SUCCESS
    └─ 401/Fehler → catchError → FAILURE

NACH DEM LOGIN:
  Benutzer kehrt zur ursprünglichen Seite zurück. ProjectForge.jsx wird neu gemountet.
  loadUserStatus() läuft erneut – dieses Mal ist JSESSIONID gültig → SUCCESS

Zugehörige Docs: Dokumentation zur authentication.test.js-Diff | Dokumentation zur reducers/authentication.js-Diff