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 that occurs when a user initiates the download of an automation evidence PDF from the TaskIntegrationChecks component. The primary goal of this process is to securely fetch a PDF document from the backend API, ensuring that the request is authenticated with a valid JSON Web Token (JWT).
The flow begins with a user interaction in the UI, leading to a series of function calls that prepare the download request. A critical part of this preparation involves obtaining a current and unexpired JWT. If the existing token is missing or nearing expiration, a refresh mechanism is triggered to acquire a new token from the authentication service. The trace concludes with the decoding of this JWT to extract its payload, primarily for managing its expiry and storage. This robust authentication mechanism ensures that only authorized users can access and download sensitive evidence documents.
The process begins within the TaskIntegrationChecks React component, which is responsible for displaying and managing automated checks for a specific task. When a user clicks the "Download evidence PDF" button associated with a particular automation check, an onClick event handler is triggered. This handler invokes the downloadAutomationPDF function, passing along necessary identifiers such as the taskId, automationId (which corresponds to the check.checkId), automationName (check.checkName), and organizationId. This action signals the start of the evidence download sequence.
Sources: apps/app/src/app/(app)/[orgId]/tasks/[taskId]/components/TaskIntegrationChecks.tsx:392-404
The downloadAutomationPDF function, located in , is responsible for constructing the specific API endpoint URL for the automation evidence PDF. It takes the , , , and as input. After forming the complete URL, it delegates the actual file fetching and download process to a generic utility function, along with fallback naming conventions for the downloaded file.
TaskIntegrationChecks.tsx (React UI component) initiates the action.evidence-download.ts (utility for file downloads) handles the API request construction and execution.jwt-manager.ts (authentication utility) manages the JWT lifecycle.localStorage is used for persistent token storage./v1/tasks/.../pdf for download, /api/auth/token for token refresh) are crucial for backend communication.fetch calls in downloadFile and _doRefreshToken can fail due to network issues. These are caught and result in error messages (e.g., toast.error in TaskIntegrationChecks, console.error in downloadFile and jwt-manager).jwtManager is specifically designed to handle this by proactively refreshing tokens before they expire and by attempting to refresh if an API call indicates an invalid token. If refresh fails, will throw an "Authentication failed" error.Sources: apps/app/src/app/(app)/[orgId]/tasks/[taskId]/components/TaskIntegrationChecks.tsx:392-404 apps/app/src/lib/evidence-download.ts:64-106 apps/app/src/utils/jwt-manager.ts:16-152
apps/app/src/lib/evidence-download.tstaskIdautomationIdautomationNameorganizationIddownloadFileThe downloadFile function in apps/app/src/lib/evidence-download.ts handles the core logic for downloading a file from a given URL. Before making the fetch request, it constructs the necessary HTTP headers. Crucially, it adds an X-Organization-Id header and an Authorization header. To obtain the value for the Authorization header, it calls jwtManager.getValidToken(). This ensures that the download request is properly authenticated with a valid JWT. If getValidToken throws an error (e.g., authentication failed), the download process is aborted, and an error is thrown. Upon successful retrieval of the token, it proceeds to make the fetch request, processes the response, extracts the filename from the Content-Disposition header (or uses a fallback), and then initiates the browser-based file download.
The getValidToken method of the jwtManager (a singleton instance of JWTManager from apps/app/src/utils/jwt-manager.ts) is invoked to provide an up-to-date JWT. This method first attempts to retrieve a token and its expiry information from localStorage. It then checks if the stored token is still valid or if it's expiring soon (within a REFRESH_THRESHOLD of 5 minutes).
If no token is found, or if the stored token is deemed to be expiring soon, getValidToken proceeds to call refreshToken() to acquire a fresh token. Otherwise, it returns the existing, valid token directly. This proactive refresh mechanism minimizes the chances of an API request failing due to an expired token.
The refreshToken method orchestrates the process of obtaining a new JWT. It includes important safeguards:
this.refreshPromise), it waits for that existing refresh to complete instead of initiating a new one.REFRESH_COOLDOWN (2 seconds) between refresh attempts to prevent excessive API calls. If an attempt is made too soon after the last, it will wait.
After these checks, it sets this.refreshPromise to the result of _doRefreshToken() and awaits its completion. This ensures that only one refresh operation is active at any given time.The private _doRefreshToken method is where the actual fetching of a new JWT occurs. It attempts to acquire a new token through two primary mechanisms:
authClient.getSession() call, which might set a set-auth-jwt header in the response.fetch request to the /api/auth/token endpoint.
If a new token is successfully obtained from either method, it calls storeToken() to persist the new token and its expiry, and scheduleRefresh() to set up the next automatic refresh.Once a new JWT is acquired, the storeToken method is called. Its purpose is to securely store the token and its associated expiry information in localStorage. To determine the expiry time, it first calls decodeJWTPayload() to parse the token and extract the exp (expiration) claim. The exp value, which is a Unix timestamp in seconds, is converted to milliseconds and stored alongside the token itself. This ensures that getValidToken can efficiently check the token's validity without re-decoding it every time.
The final step in this trace is the decodeJWTPayload method. This utility function is responsible for parsing the JWT string to extract its payload. It performs a client-side, unverified decoding by splitting the token into its three parts (header, payload, signature), base64-decoding the payload part, and then parsing the resulting string as JSON. This decoded payload contains claims such as the token's expiration time (exp), which is crucial for the storeToken method to manage token lifecycle.
downloadFilerefreshToken method prevents multiple simultaneous token refresh requests, which could lead to race conditions or unnecessary load on the authentication service.decodeJWTPayload includes a try-catch block to handle malformed JWTs, preventing the application from crashing.localStorage and performing client-side expiry checks (isTokenExpiringSoon) reduces the need for frequent server-side validation, improving responsiveness.refreshToken method's handling of concurrent requests and cooldowns prevents performance degradation due to excessive authentication attempts.