---
title: "Integrate & Decode JWT"
description: "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 ..."
last_updated: "2026-05-06T07:30:58.159818+00:00"
canonical_url: "https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/how-it-works/integrate-decode-jwt"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [apps/app/src/app/(app)/[orgId]/tasks/[taskId]/components/TaskIntegrationChecks.tsx](https://github.com/blade47/comp/blob/main/apps/app/src/app/(app)/%5BorgId%5D/tasks/%5BtaskId%5D/components/TaskIntegrationChecks.tsx)
- [apps/app/src/lib/evidence-download.ts](https://github.com/blade47/comp/blob/main/apps/app/src/lib/evidence-download.ts)
- [apps/app/src/utils/jwt-manager.ts](https://github.com/blade47/comp/blob/main/apps/app/src/utils/jwt-manager.ts)
</details>

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.

<Steps>
<Step>
### Initiate Automation PDF Download
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](https://github.com/blade47/comp/blob/main/apps/app/src/app/(app)/%5BorgId%5D/tasks/%5BtaskId%5D/components/TaskIntegrationChecks.tsx#L392-L404)
</Step>
<Step>
### Prepare Download Request
The `downloadAutomationPDF` function, located in `apps/app/src/lib/evidence-download.ts`, is responsible for constructing the specific API endpoint URL for the automation evidence PDF. It takes the `taskId`, `automationId`, `automationName`, and `organizationId` as input. After forming the complete URL, it delegates the actual file fetching and download process to a generic `downloadFile` utility function, along with fallback naming conventions for the downloaded file.

Sources: [apps/app/src/lib/evidence-download.ts:6-20](https://github.com/blade47/comp/blob/main/apps/app/src/lib/evidence-download.ts#L6-L20)
</Step>
<Step>
### Execute File Download with Authentication
The `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.

Sources: [apps/app/src/lib/evidence-download.ts:64-106](https://github.com/blade47/comp/blob/main/apps/app/src/lib/evidence-download.ts#L64-L106)
</Step>
<Step>
### Retrieve or Refresh JWT
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).
<Callout title="Token Validation Logic" variant="info">
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.
</Callout>

Sources: [apps/app/src/utils/jwt-manager.ts:16-30](https://github.com/blade47/comp/blob/main/apps/app/src/utils/jwt-manager.ts#L16-L30)
</Step>
<Step>
### Manage Token Refresh
The `refreshToken` method orchestrates the process of obtaining a new JWT. It includes important safeguards:
1.  **Concurrent Refresh Prevention:** If a token refresh is already in progress (indicated by `this.refreshPromise`), it waits for that existing refresh to complete instead of initiating a new one.
2.  **Cooldown Period:** It enforces a `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.

Sources: [apps/app/src/utils/jwt-manager.ts:34-60](https://github.com/blade47/comp/blob/main/apps/app/src/utils/jwt-manager.ts#L34-L60)
</Step>
<Step>
### Perform Actual Token Refresh
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:
1.  **Session API:** It first tries to get the JWT from the `authClient.getSession()` call, which might set a `set-auth-jwt` header in the response.
2.  **Token Endpoint:** If the session API doesn't provide a new token, it then makes a `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.

Sources: [apps/app/src/utils/jwt-manager.ts:64-100](https://github.com/blade47/comp/blob/main/apps/app/src/utils/jwt-manager.ts#L64-L100)
</Step>
<Step>
### Store New Token
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.

Sources: [apps/app/src/utils/jwt-manager.ts:104-114](https://github.com/blade47/comp/blob/main/apps/app/src/utils/jwt-manager.ts#L104-L114)
</Step>
<Step>
### Decode JWT Payload
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.

Sources: [apps/app/src/utils/jwt-manager.ts:145-152](https://github.com/blade47/comp/blob/main/apps/app/src/utils/jwt-manager.ts#L145-L152)
</Step>
</Steps>

```mermaid
sequenceDiagram
    participant UI as TaskIntegrationChecks.tsx
    participant ED as evidence-download.ts
    participant JM as jwt-manager.ts
    participant AuthAPI as /api/auth/token
    participant Browser as Browser/localStorage

    UI->>ED: downloadAutomationPDF(taskId, automationId, ...)
    ED->>ED: build API endpoint URL
    ED->>ED: downloadFile(url, organizationId, fallback)
    ED->>JM: getValidToken()
    JM->>Browser: getStoredToken()
    alt Token missing or expiring soon
        JM->>JM: refreshToken()
        JM->>JM: _doRefreshToken()
        JM->>AuthAPI: fetch('/api/auth/token')
        AuthAPI-->>JM: newToken (JWT)
        JM->>JM: storeToken(newToken)
        JM->>JM: decodeJWTPayload(newToken)
        JM->>Browser: store token & expiry
        JM-->>ED: newToken
    else Token valid
        JM-->>ED: storedToken
    end
    ED->>ED: add Authorization header with token
    ED->>AuthAPI: fetch(downloadUrl, {headers, credentials})
    AuthAPI-->>ED: fileBlob (PDF)
    ED->>Browser: createObjectURL, create <a>, click, revokeObjectURL
    Browser-->>UI: File Download Started
```



### Key Observations

*   **Cross-module Boundaries:** This flow demonstrates significant interaction across different modules:
    *   `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.
    *   The browser's `localStorage` is used for persistent token storage.
    *   External API endpoints (`/v1/tasks/.../pdf` for download, `/api/auth/token` for token refresh) are crucial for backend communication.
*   **Potential Failure Points and Handling:**
    *   **Network Errors:** `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`).
    *   **Expired/Invalid JWT:** The `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, `downloadFile` will throw an "Authentication failed" error.
    *   **Concurrent Refreshes:** The `refreshToken` method prevents multiple simultaneous token refresh requests, which could lead to race conditions or unnecessary load on the authentication service.
    *   **Cooldown Period:** A cooldown is enforced between refresh attempts to avoid hammering the server if refreshes repeatedly fail.
    *   **Invalid JWT Format:** `decodeJWTPayload` includes a `try-catch` block to handle malformed JWTs, preventing the application from crashing.
*   **Performance Considerations:**
    *   **Client-side Token Management:** Storing and managing JWTs in `localStorage` and performing client-side expiry checks (`isTokenExpiringSoon`) reduces the need for frequent server-side validation, improving responsiveness.
    *   **Proactive Refresh:** Refreshing tokens before they expire minimizes delays in authenticated API calls.
    *   **Optimized Refresh Logic:** The `refreshToken` method's handling of concurrent requests and cooldowns prevents performance degradation due to excessive authentication attempts.
    *   **Asynchronous Operations:** All network requests and token management operations are asynchronous, ensuring the UI remains responsive during these background tasks.

Sources:
[apps/app/src/app/(app)/[orgId]/tasks/[taskId]/components/TaskIntegrationChecks.tsx:392-404](https://github.com/blade47/comp/blob/main/apps/app/src/app/(app)/%5BorgId%5D/tasks/%5BtaskId%5D/components/TaskIntegrationChecks.tsx#L392-L404)
[apps/app/src/lib/evidence-download.ts:64-106](https://github.com/blade47/comp/blob/main/apps/app/src/lib/evidence-download.ts#L64-L106)
[apps/app/src/utils/jwt-manager.ts:16-152](https://github.com/blade47/comp/blob/main/apps/app/src/utils/jwt-manager.ts#L16-L152)

## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/llms.txt) for all pages in this wiki.
