System Architecture
Core Features
Data Management
Frontend Components
Extensibility
The following files were used as context for generating this wiki page:
This document outlines the execution flow when a user initiates the download of task evidence from the SingleTask component, leading to the decoding of a JSON Web Token (JWT) payload. The primary goal of this process is to securely fetch a file from the backend API, which requires a valid authentication token.
The flow begins with a user interaction in the SingleTask UI, triggering a file download. To ensure the request is authenticated, the system relies on a jwtManager utility. This utility is responsible for providing a valid JWT, proactively refreshing it if it's missing or nearing expiration. A crucial part of this refresh mechanism involves decoding the JWT's payload to extract its expiration timestamp, thereby allowing the system to manage token validity and schedule future refreshes efficiently. This ensures that authenticated operations, like downloading sensitive evidence, proceed seamlessly without requiring the user to re-authenticate frequently.
The process begins within the SingleTask React component when a user clicks the "Download task evidence" button. This action triggers an asynchronous operation to fetch a ZIP archive containing all evidence related to the current task. The component catches potential errors during the download and provides user feedback via toast notifications.
The SingleTask component is responsible for rendering the task details and providing interactive elements, including the download button for task evidence.
Upon the user's action, the downloadTaskEvidenceZip function is called. This function is part of the evidence-download utility library and is responsible for constructing the correct API endpoint for the task evidence ZIP file. It takes the taskId, taskTitle, organizationId, and an includeJson flag as parameters. It then delegates the actual download operation to a more generic downloadFile function.
Inputs:
taskId: The ID of the task for which evidence is being downloaded.taskTitle: The title of the task, used for generating a user-friendly filename.organizationId: The ID of the organization, required for API calls.includeJson: A boolean indicating whether to include JSON metadata in the ZIP.Output: A call to downloadFile with the constructed URL and metadata.
The downloadFile function, also within the evidence-download utility, handles the core logic of fetching a file from a given URL. Before making the fetch request, it prepares the necessary HTTP headers, including the X-Organization-Id. Crucially, it attempts to retrieve a valid JWT token for authentication. If successful, this token is added to the Authorization header as a Bearer token. The function then performs the fetch request, handles potential HTTP errors, extracts the filename from the Content-Disposition header (or generates a fallback), and finally initiates the client-side download by creating a temporary <a> element.
Inputs:
url: The full API endpoint for the file to be downloaded.organizationId: The organization ID for the X-Organization-Id header.fallback: An optional object containing fallbackBaseName and fallbackExtension for filename generation.Data Flow:
jwtManager.getValidToken() to obtain an authentication token.headers object with X-Organization-Id and Authorization (if token is available).fetch(url, { method: 'GET', headers, credentials: 'include' }).response.blob() and Content-Disposition header to get the file and its name.Error Handling:
jwtManager.getValidToken() fails, an error is logged, and a new error "Authentication failed" is thrown.fetch response is not ok, an error is thrown with the response text or status.The jwtManager.getValidToken() method is invoked to ensure that any outgoing API request is authenticated with a current and valid JWT. It first checks if a token is already stored in localStorage and if that token is still valid or not expiring soon (within a REFRESH_THRESHOLD of 5 minutes).
Branching Logic:
this.refreshToken() to acquire a new token.Error Handling:
null is returned, indicating that no valid token could be obtained.The jwtManager.refreshToken() method is responsible for managing the token refresh process. It includes logic to prevent multiple concurrent refresh attempts and enforces a cooldown period between refreshes to avoid overwhelming the authentication service.
Concurrency and Cooldown:
this.refreshPromise: If a refresh is already in progress, it waits for that existing promise to resolve.this.lastRefreshAttempt and REFRESH_COOLDOWN: If a refresh was attempted too recently, it waits for the cooldown period to pass. During this wait, if a valid token is already available, it might return that token.Once these checks pass, it records the lastRefreshAttempt and sets this.refreshPromise to the result of this._doRefreshToken(), ensuring that subsequent calls wait for this refresh to complete.
Output: Returns the new token obtained from _doRefreshToken or null if the refresh fails.
The jwtManager._doRefreshToken() method executes the actual network requests to obtain a new JWT. It attempts two primary strategies:
authClient.getSession() call. The onSuccess callback inspects the response headers for a set-auth-jwt header.fetch request to the /api/auth/token endpoint.Data Flow:
newToken string if successful.Subsequent Actions:
newToken is successfully acquired, it calls this.storeToken(newToken) to save the new token and its expiry.this.scheduleRefresh(newToken) to set up an automatic refresh before the new token expires.Error Handling:
null.The jwtManager.storeToken() method is responsible for persisting the newly acquired JWT and its expiration timestamp in localStorage. This allows the application to retrieve the token quickly for subsequent authenticated requests without needing to refresh it every time.
Data Flow:
token string as input.this.decodeJWTPayload(token) to extract the expiration time (exp) from the token's payload.token under this.STORAGE_KEY and the expiresAt (converted to milliseconds) under this.EXPIRY_KEY in localStorage.Error Handling:
decodeJWTPayload fails.The jwtManager.decodeJWTPayload() method is a utility function used to parse the base64-encoded payload of a JWT. This is a client-side operation and does not involve cryptographic verification, as its purpose is simply to extract information like the expiration timestamp (exp) for local token management.
Inputs:
token: The full JWT string.Process:
. delimiter.atob() to base64-decode the payload string.Output: The parsed JSON object representing the JWT payload.
Error Handling:
Error('Invalid JWT token format').SingleTask.tsx: Handles UI interaction and initiates the high-level action.evidence-download.ts: Manages the specifics of file downloading, including API endpoint construction and client-side download mechanics.jwt-manager.ts: Centralizes all JWT-related operations, such as token retrieval, refresh, storage, and decoding, abstracting authentication details from the download logic.jwtManager.getValidToken() or _doRefreshToken() fails to acquire a token, the downloadFile function will throw an "Authentication failed" error, which is then caught by SingleTask and displayed as a toast.error.fetch requests in downloadFile and _doRefreshToken can fail due to network issues or API unavailability. These are caught and reported to the user via toast.error.decodeJWTPayload explicitly checks for valid JWT structure and throws an error if parsing fails, preventing corrupted tokens from being used.jwtManager uses refreshPromise and REFRESH_COOLDOWN to prevent multiple token refresh requests from being sent simultaneously, which could lead to race conditions or unnecessary load on the authentication service.localStorage and reused, reducing the need for frequent authentication requests.scheduleRefresh mechanism attempts to refresh the token a few minutes before its actual expiration (REFRESH_THRESHOLD), ensuring that a valid token is usually available when an API call is made, minimizing latency for authenticated requests.refreshToken method's handling of refreshPromise means that if multiple parts of the application simultaneously request a token refresh, only one actual refresh operation is performed, and all callers await its result.