@conduit-client/salesforce-lightning-service-worker 3.3.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -37
- package/dist/index.js +128 -36
- package/dist/index.js.map +1 -1
- package/dist/types/fetch.d.ts +29 -0
- package/dist/types/index.d.ts +40 -8
- package/package.json +2 -2
- package/dist/types/csrf.d.ts +0 -1
- /package/dist/types/__tests__/{csrf.spec.d.ts → fetch.spec.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Salesforce Lightning Service Worker
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A specialized HTTP client and service worker for Salesforce Lightning applications that provides automatic CSRF (Cross-Site Request Forgery) protection for API requests. This package ensures secure communication with Salesforce APIs by automatically managing CSRF tokens.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,72 +10,107 @@ npm install @conduit-client/salesforce-lightning-service-worker
|
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
|
-
This package
|
|
13
|
+
This package provides two ways to use CSRF protection:
|
|
14
14
|
|
|
15
|
-
### 1.
|
|
15
|
+
### 1. Using ConduitClient (Recommended)
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
The `ConduitClient` provides a convenient wrapper around fetch with automatic CSRF protection:
|
|
18
18
|
|
|
19
19
|
```typescript
|
|
20
|
-
import {
|
|
20
|
+
import { ConduitClient } from '@conduit-client/salesforce-lightning-service-worker';
|
|
21
21
|
|
|
22
|
-
//
|
|
23
|
-
const
|
|
22
|
+
// Create a client instance
|
|
23
|
+
const client = ConduitClient.create();
|
|
24
|
+
|
|
25
|
+
// Make API calls - CSRF protection is automatic for protected endpoints
|
|
26
|
+
const response = await client.fetch('/services/data/v65.0/sobjects/Account', {
|
|
27
|
+
method: 'POST',
|
|
28
|
+
body: JSON.stringify({ Name: 'Test Account' }),
|
|
29
|
+
});
|
|
24
30
|
```
|
|
25
31
|
|
|
26
|
-
|
|
32
|
+
### 2. Using Service Worker (Advanced)
|
|
33
|
+
|
|
34
|
+
For applications that desire service worker-level CSRF protection:
|
|
27
35
|
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
#### Register the Service Worker
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { ConduitClient } from '@conduit-client/salesforce-lightning-service-worker';
|
|
30
40
|
|
|
31
|
-
|
|
41
|
+
// Register service worker for enhanced protection
|
|
42
|
+
await ConduitClient.registerServiceWorker('./sw.js');
|
|
43
|
+
```
|
|
32
44
|
|
|
33
|
-
|
|
45
|
+
#### Create the Service Worker File
|
|
34
46
|
|
|
35
47
|
```typescript
|
|
36
|
-
// sw.js
|
|
37
|
-
import {
|
|
48
|
+
// sw.js - served statically by your bundler
|
|
49
|
+
import { ConduitClient } from '@conduit-client/salesforce-lightning-service-worker';
|
|
38
50
|
|
|
39
|
-
|
|
51
|
+
// Define service worker behavior
|
|
52
|
+
ConduitClient.defineServiceWorker({ debug: true });
|
|
40
53
|
```
|
|
41
54
|
|
|
55
|
+
**NOTE: ** Note, if service worker registration fails the wrapper approach will remain in
|
|
56
|
+
place in order to maintain CSRF protection.
|
|
57
|
+
|
|
42
58
|
**Important Configuration Notes:**
|
|
43
59
|
|
|
44
|
-
1. **Static File Name**: The service worker file must have a static name (e.g., `sw.js`) without hash tokens
|
|
45
|
-
2. **Module Type**:
|
|
46
|
-
3. **Scope**:
|
|
47
|
-
|
|
48
|
-
|
|
60
|
+
1. **Static File Name**: The service worker file must have a static name (e.g., `sw.js`) without hash tokens
|
|
61
|
+
2. **Module Type**: Use ES6 modules for modern bundlers
|
|
62
|
+
3. **Scope**: Service worker scope determines which requests it can intercept
|
|
63
|
+
|
|
64
|
+
## CSRF Protection Features
|
|
65
|
+
|
|
66
|
+
This package provides automatic CSRF protection with the following features:
|
|
49
67
|
|
|
50
|
-
|
|
68
|
+
### Automatic Token Management
|
|
51
69
|
|
|
52
|
-
|
|
70
|
+
- **Token Caching**: CSRF tokens are cached using the Cache API for performance
|
|
71
|
+
- **Token Refresh**: Automatically refreshes tokens when they become invalid
|
|
72
|
+
- **Retry Logic**: Retries requests once with fresh tokens on authentication failures
|
|
73
|
+
|
|
74
|
+
### Protected Endpoints
|
|
75
|
+
|
|
76
|
+
- **Method Protection**: Automatically protects data-mutating methods (POST, PUT, PATCH, DELETE)
|
|
77
|
+
- **URL Protection**: Currently protects all Salesforce API endpoints under `/services`
|
|
78
|
+
- **Intelligent Detection**: Only applies CSRF protection where needed
|
|
79
|
+
|
|
80
|
+
### Service Worker Integration
|
|
53
81
|
|
|
54
82
|
- **Install Handler**: Skips waiting to activate immediately
|
|
55
83
|
- **Activate Handler**: Claims all clients immediately
|
|
56
|
-
- **Fetch
|
|
84
|
+
- **Fetch Interception**: Intercepts and enhances requests with CSRF tokens
|
|
57
85
|
|
|
58
|
-
## Example
|
|
86
|
+
## Complete Example
|
|
59
87
|
|
|
60
88
|
```typescript
|
|
61
|
-
// Main
|
|
62
|
-
import {
|
|
63
|
-
|
|
64
|
-
async function
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
89
|
+
// Main application code
|
|
90
|
+
import { ConduitClient } from '@conduit-client/salesforce-lightning-service-worker';
|
|
91
|
+
|
|
92
|
+
async function setupApiClient() {
|
|
93
|
+
// Optionally register service worker for enhanced protection
|
|
94
|
+
await ConduitClient.registerServiceWorker('./sw.js');
|
|
95
|
+
|
|
96
|
+
// Create client for API calls
|
|
97
|
+
const client = ConduitClient.create();
|
|
98
|
+
|
|
99
|
+
// Make API calls - CSRF protection is automatic
|
|
100
|
+
const account = await client.fetch('/services/data/v65.0/sobjects/Account', {
|
|
101
|
+
method: 'POST',
|
|
102
|
+
body: JSON.stringify({ Name: 'New Account' }),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return client;
|
|
71
106
|
}
|
|
72
107
|
```
|
|
73
108
|
|
|
74
109
|
```typescript
|
|
75
|
-
// sw.js
|
|
76
|
-
import {
|
|
110
|
+
// sw.js - Service worker file if `registerServiceWorker` is used
|
|
111
|
+
import { ConduitClient } from '@conduit-client/salesforce-lightning-service-worker';
|
|
77
112
|
|
|
78
|
-
|
|
113
|
+
ConduitClient.defineServiceWorker({ debug: false });
|
|
79
114
|
```
|
|
80
115
|
|
|
81
116
|
## Development
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
const CACHE_VERSION = 1;
|
|
7
7
|
const CACHE_NAME = `salesforce-lightning-service-worker-${CACHE_VERSION}`;
|
|
8
|
+
const CSRF_HEADER = "X-CSRF-Token";
|
|
8
9
|
async function withCache(callback) {
|
|
9
10
|
if (caches) {
|
|
10
11
|
const cache = await caches.open(CACHE_NAME);
|
|
@@ -19,7 +20,7 @@ function isProtectedMethod(method) {
|
|
|
19
20
|
}
|
|
20
21
|
function isProtectedUrl(urlString) {
|
|
21
22
|
const url = new URL(urlString);
|
|
22
|
-
return url.pathname.
|
|
23
|
+
return url.pathname.includes("/services/data/v");
|
|
23
24
|
}
|
|
24
25
|
async function isTokenInvalid(response) {
|
|
25
26
|
var _a;
|
|
@@ -29,61 +30,152 @@ async function isTokenInvalid(response) {
|
|
|
29
30
|
}
|
|
30
31
|
return false;
|
|
31
32
|
}
|
|
32
|
-
function
|
|
33
|
-
const
|
|
33
|
+
function createLightningFetch(config = {}) {
|
|
34
|
+
const { fireEvent = () => {
|
|
35
|
+
}, tokenSource } = config;
|
|
36
|
+
let tokenUrl = "/services/data/v65.0/ui-api/session/csrf";
|
|
37
|
+
let tokenProvider = obtainToken;
|
|
38
|
+
if (tokenSource) {
|
|
39
|
+
if (typeof tokenSource === "string" || tokenSource instanceof URL) {
|
|
40
|
+
tokenUrl = tokenSource;
|
|
41
|
+
} else if (typeof tokenSource === "function") {
|
|
42
|
+
tokenProvider = tokenSource;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function generateId() {
|
|
46
|
+
return Date.now().toString(36);
|
|
47
|
+
}
|
|
34
48
|
async function obtainToken() {
|
|
35
|
-
|
|
49
|
+
const id = generateId();
|
|
50
|
+
fireEvent("csrf_token_obtain_start", id);
|
|
51
|
+
let response = await withCache((cache) => cache.match(tokenUrl));
|
|
36
52
|
if (!response) {
|
|
37
|
-
|
|
53
|
+
fireEvent("csrf_token_fetch_start", id);
|
|
54
|
+
response = await fetch(tokenUrl, { method: "get" });
|
|
55
|
+
fireEvent("csrf_token_fetch_complete", id, { status: response.status });
|
|
56
|
+
} else {
|
|
57
|
+
fireEvent("csrf_token_cache_hit", id);
|
|
38
58
|
}
|
|
39
59
|
const csrfToken = (await response.clone().json()).csrfToken;
|
|
40
|
-
await withCache((cache) => cache.put(
|
|
60
|
+
await withCache((cache) => cache.put(tokenUrl, response));
|
|
61
|
+
fireEvent("csrf_token_obtain_complete", id);
|
|
41
62
|
return csrfToken;
|
|
42
63
|
}
|
|
43
|
-
let tokenPromise =
|
|
64
|
+
let tokenPromise = tokenProvider();
|
|
44
65
|
async function refreshToken() {
|
|
45
|
-
|
|
46
|
-
|
|
66
|
+
const id = generateId();
|
|
67
|
+
fireEvent("csrf_token_refresh_start", id);
|
|
68
|
+
await withCache((cache) => cache.delete(tokenUrl));
|
|
69
|
+
tokenPromise = tokenProvider();
|
|
70
|
+
fireEvent("csrf_token_refresh_complete", id);
|
|
47
71
|
}
|
|
48
72
|
async function fetchWithToken(request) {
|
|
49
73
|
const headers = new Headers(request.headers);
|
|
50
|
-
headers.
|
|
74
|
+
if (!headers.has(CSRF_HEADER)) {
|
|
75
|
+
headers.set(CSRF_HEADER, await tokenPromise);
|
|
76
|
+
}
|
|
51
77
|
return fetch(request, { headers });
|
|
52
78
|
}
|
|
53
|
-
return async (
|
|
79
|
+
return async function lightningFetch2(input, init) {
|
|
80
|
+
const id = generateId();
|
|
81
|
+
const request = new Request(input, init);
|
|
54
82
|
if (isProtectedMethod(request.method) && isProtectedUrl(request.url)) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
83
|
+
fireEvent("protected_request_start", id, { method: request.method, url: request.url });
|
|
84
|
+
const response = await fetchWithToken(request.clone());
|
|
85
|
+
if (await isTokenInvalid(response)) {
|
|
86
|
+
fireEvent("csrf_token_invalid", id, { status: response.status });
|
|
87
|
+
await refreshToken();
|
|
88
|
+
const retryResponse = await fetchWithToken(request.clone());
|
|
89
|
+
fireEvent("protected_request_complete", id, {
|
|
90
|
+
method: request.method,
|
|
91
|
+
url: request.url,
|
|
92
|
+
status: retryResponse.status,
|
|
93
|
+
retried: true
|
|
94
|
+
});
|
|
95
|
+
return retryResponse;
|
|
96
|
+
} else {
|
|
97
|
+
fireEvent("protected_request_complete", id, {
|
|
98
|
+
method: request.method,
|
|
99
|
+
url: request.url,
|
|
100
|
+
status: response.status,
|
|
101
|
+
retried: false
|
|
102
|
+
});
|
|
103
|
+
return response;
|
|
65
104
|
}
|
|
66
105
|
} else {
|
|
106
|
+
fireEvent("unprotected_request", id, { method: request.method, url: request.url });
|
|
67
107
|
return fetch(request);
|
|
68
108
|
}
|
|
69
109
|
};
|
|
70
110
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
111
|
+
const lightningFetch = createLightningFetch();
|
|
112
|
+
let clientFetch = lightningFetch;
|
|
113
|
+
class ConduitClient {
|
|
114
|
+
constructor() {
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Makes an HTTP request
|
|
118
|
+
*
|
|
119
|
+
* @param input - The URL, Request object, or relative path to request
|
|
120
|
+
* @param init - Optional request configuration that will be merged with defaults
|
|
121
|
+
* @returns Promise that resolves to the Response object
|
|
122
|
+
*/
|
|
123
|
+
fetch(input, init = {}) {
|
|
124
|
+
return clientFetch(input, init);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Factory method to create a new ConduitClient instance
|
|
128
|
+
*
|
|
129
|
+
* @returns A new ConduitClient instance
|
|
130
|
+
*/
|
|
131
|
+
static create() {
|
|
132
|
+
return new ConduitClient();
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Registers a service worker for enhanced CSRF protection and caching.
|
|
136
|
+
* When successfully registered, the client will switch to using native fetch
|
|
137
|
+
* as the service worker will handle CSRF protection.
|
|
138
|
+
*
|
|
139
|
+
* The script URL must identify a source file that calls `defineServiceWorker`.
|
|
140
|
+
*
|
|
141
|
+
* @param scriptURL - URL or path to the service worker script
|
|
142
|
+
*/
|
|
143
|
+
static async registerServiceWorker(scriptURL) {
|
|
144
|
+
if ("serviceWorker" in navigator) {
|
|
145
|
+
try {
|
|
146
|
+
const registration = await navigator.serviceWorker.register(scriptURL, {
|
|
147
|
+
type: "module"
|
|
148
|
+
});
|
|
149
|
+
clientFetch = fetch;
|
|
150
|
+
console.log("[Conduit Client] Service registration succeeded:", registration);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error(
|
|
153
|
+
"[Conduit Client] Service Worker registration failed (using decorated `fetch`):",
|
|
154
|
+
error
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Defines the service worker behavior for CSRF protection.
|
|
161
|
+
*
|
|
162
|
+
* This method must be called within a service worker script whose URL is supplied to
|
|
163
|
+
* `registerServiceWorker`
|
|
164
|
+
*/
|
|
165
|
+
static defineServiceWorker() {
|
|
166
|
+
const scope = self;
|
|
167
|
+
scope.addEventListener("install", (event) => {
|
|
168
|
+
event.waitUntil(scope.skipWaiting());
|
|
169
|
+
});
|
|
170
|
+
scope.addEventListener("activate", (event) => {
|
|
171
|
+
event.waitUntil(scope.clients.claim());
|
|
172
|
+
});
|
|
173
|
+
scope.addEventListener("fetch", (event) => {
|
|
174
|
+
event.respondWith(lightningFetch(event.request));
|
|
175
|
+
});
|
|
176
|
+
}
|
|
85
177
|
}
|
|
86
178
|
export {
|
|
87
|
-
|
|
179
|
+
ConduitClient
|
|
88
180
|
};
|
|
89
181
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/csrf.ts","../src/index.ts"],"sourcesContent":["const CACHE_VERSION = 1;\nconst CACHE_NAME = `salesforce-lightning-service-worker-${CACHE_VERSION}`;\n\n/**\n * Retrieves the cache and supplies it to a callback.\n *\n * @param callback\n */\nasync function withCache<T>(callback: (cache: Cache) => Promise<T>): Promise<T | undefined> {\n // defend against the cache API not being available\n if (caches) {\n const cache = await caches.open(CACHE_NAME);\n return callback(cache);\n } else {\n return undefined;\n }\n}\n\n/**\n * Determine if an HTTP method is one that mutates data.\n */\nfunction isProtectedMethod(method: string) {\n const normalizedMethod = method.toLowerCase();\n return (\n normalizedMethod === 'post' ||\n normalizedMethod === 'put' ||\n normalizedMethod === 'patch' ||\n normalizedMethod === 'delete'\n );\n}\n\n/**\n * Determine if the URL is for a path that has CSRF protection.\n *\n * Note: Could allow customization\n */\nfunction isProtectedUrl(urlString: string) {\n const url = new URL(urlString);\n return url.pathname.startsWith('/services');\n}\n\n/**\n * Check response for status and error code of an invalid token\n */\nasync function isTokenInvalid(response: Response) {\n if (response.status === 400) {\n // clone response to read body without consuming it\n const body = await response.clone().json();\n\n // check for specific error code\n return body[0]?.errorCode === 'INVALID_ACCESS_TOKEN';\n }\n\n return false;\n}\n\nexport function createCsrfHandler(version: string) {\n const CSRF_TOKEN_URL = `/services/data/v${version}/ui-api/session/csrf`;\n\n async function obtainToken(): Promise<string> {\n // look up response in cache\n let response = await withCache((cache) => cache.match(CSRF_TOKEN_URL));\n\n if (!response) {\n // cached response not available to fetch\n response = await fetch(CSRF_TOKEN_URL, { method: 'get' });\n }\n\n // extract token using clone so caching can still process\n const csrfToken: string = (await response.clone().json()).csrfToken;\n\n // store token response\n await withCache((cache) => cache.put(CSRF_TOKEN_URL, response));\n\n return csrfToken;\n }\n\n let tokenPromise = obtainToken();\n\n /**\n * Clear any cached token and retrieve a new one.\n */\n async function refreshToken() {\n await withCache((cache) => cache.delete(CSRF_TOKEN_URL));\n\n tokenPromise = obtainToken();\n }\n\n /**\n * Make a request with the token header based on the supplied request\n */\n async function fetchWithToken(request: Request) {\n // combine original headers with new csrf header\n const headers = new Headers(request.headers);\n headers.set('X-CSRF-Token', await tokenPromise!);\n\n // make request with updated headers\n return fetch(request, { headers });\n }\n\n /**\n * Applies a valid CSRF token to targeted requests that modify data\n */\n return async (request: Request): Promise<Response> => {\n // see if the method and url qualify for CSRF\n if (isProtectedMethod(request.method) && isProtectedUrl(request.url)) {\n try {\n // make request with token, clone so that any retry isn't based on a consumed request\n const response = await fetchWithToken(request.clone());\n\n // see if token was bad\n if (await isTokenInvalid(response)) {\n // it was, so refresh and try again\n await refreshToken();\n return fetchWithToken(request.clone());\n } else {\n // use response\n return response;\n }\n } catch (error) {\n // always need to return a response\n return new Response(JSON.stringify({ error }), { status: 500 });\n }\n } else {\n // protection not required, run as is\n return fetch(request);\n }\n };\n}\n","import { createCsrfHandler } from './csrf';\n\nexport type Config = {\n version: string;\n debug?: boolean;\n};\n\n/**\n * Adds event listeners for setting up service worker.\n *\n * @param version\n * @param debug\n */\nexport function createServiceWorker({ version, debug }: Config) {\n const scope = self as any as ServiceWorkerGlobalScope;\n\n scope.addEventListener('install', (event) => {\n if (debug) console.log('[Service Worker] Installed');\n\n // Skip waiting to activate immediately\n event.waitUntil(scope.skipWaiting());\n });\n\n scope.addEventListener('activate', (event) => {\n if (debug) console.log('[Service Worker] Activated');\n\n // Claim all clients immediately\n event.waitUntil(scope.clients.claim());\n });\n\n const applyCsrfProtection = createCsrfHandler(version);\n scope.addEventListener('fetch', (event) => {\n // Apply CSRF protection\n event.respondWith(applyCsrfProtection(event.request));\n });\n}\n"],"names":[],"mappings":";;;;;AAAA,MAAM,gBAAgB;AACtB,MAAM,aAAa,uCAAuC,aAAa;AAOvE,eAAe,UAAa,UAAgE;AAExF,MAAI,QAAQ;AACR,UAAM,QAAQ,MAAM,OAAO,KAAK,UAAU;AAC1C,WAAO,SAAS,KAAK;AAAA,EACzB,OAAO;AACH,WAAO;AAAA,EACX;AACJ;AAKA,SAAS,kBAAkB,QAAgB;AACvC,QAAM,mBAAmB,OAAO,YAAA;AAChC,SACI,qBAAqB,UACrB,qBAAqB,SACrB,qBAAqB,WACrB,qBAAqB;AAE7B;AAOA,SAAS,eAAe,WAAmB;AACvC,QAAM,MAAM,IAAI,IAAI,SAAS;AAC7B,SAAO,IAAI,SAAS,WAAW,WAAW;AAC9C;AAKA,eAAe,eAAe,UAAoB;;AAC9C,MAAI,SAAS,WAAW,KAAK;AAEzB,UAAM,OAAO,MAAM,SAAS,MAAA,EAAQ,KAAA;AAGpC,aAAO,UAAK,CAAC,MAAN,mBAAS,eAAc;AAAA,EAClC;AAEA,SAAO;AACX;AAEO,SAAS,kBAAkB,SAAiB;AAC/C,QAAM,iBAAiB,mBAAmB,OAAO;AAEjD,iBAAe,cAA+B;AAE1C,QAAI,WAAW,MAAM,UAAU,CAAC,UAAU,MAAM,MAAM,cAAc,CAAC;AAErE,QAAI,CAAC,UAAU;AAEX,iBAAW,MAAM,MAAM,gBAAgB,EAAE,QAAQ,OAAO;AAAA,IAC5D;AAGA,UAAM,aAAqB,MAAM,SAAS,MAAA,EAAQ,QAAQ;AAG1D,UAAM,UAAU,CAAC,UAAU,MAAM,IAAI,gBAAgB,QAAQ,CAAC;AAE9D,WAAO;AAAA,EACX;AAEA,MAAI,eAAe,YAAA;AAKnB,iBAAe,eAAe;AAC1B,UAAM,UAAU,CAAC,UAAU,MAAM,OAAO,cAAc,CAAC;AAEvD,mBAAe,YAAA;AAAA,EACnB;AAKA,iBAAe,eAAe,SAAkB;AAE5C,UAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;AAC3C,YAAQ,IAAI,gBAAgB,MAAM,YAAa;AAG/C,WAAO,MAAM,SAAS,EAAE,SAAS;AAAA,EACrC;AAKA,SAAO,OAAO,YAAwC;AAElD,QAAI,kBAAkB,QAAQ,MAAM,KAAK,eAAe,QAAQ,GAAG,GAAG;AAClE,UAAI;AAEA,cAAM,WAAW,MAAM,eAAe,QAAQ,OAAO;AAGrD,YAAI,MAAM,eAAe,QAAQ,GAAG;AAEhC,gBAAM,aAAA;AACN,iBAAO,eAAe,QAAQ,OAAO;AAAA,QACzC,OAAO;AAEH,iBAAO;AAAA,QACX;AAAA,MACJ,SAAS,OAAO;AAEZ,eAAO,IAAI,SAAS,KAAK,UAAU,EAAE,MAAA,CAAO,GAAG,EAAE,QAAQ,KAAK;AAAA,MAClE;AAAA,IACJ,OAAO;AAEH,aAAO,MAAM,OAAO;AAAA,IACxB;AAAA,EACJ;AACJ;ACnHO,SAAS,oBAAoB,EAAE,SAAS,SAAiB;AAC5D,QAAM,QAAQ;AAEd,QAAM,iBAAiB,WAAW,CAAC,UAAU;AACzC,QAAI,MAAO,SAAQ,IAAI,4BAA4B;AAGnD,UAAM,UAAU,MAAM,aAAa;AAAA,EACvC,CAAC;AAED,QAAM,iBAAiB,YAAY,CAAC,UAAU;AAC1C,QAAI,MAAO,SAAQ,IAAI,4BAA4B;AAGnD,UAAM,UAAU,MAAM,QAAQ,MAAA,CAAO;AAAA,EACzC,CAAC;AAED,QAAM,sBAAsB,kBAAkB,OAAO;AACrD,QAAM,iBAAiB,SAAS,CAAC,UAAU;AAEvC,UAAM,YAAY,oBAAoB,MAAM,OAAO,CAAC;AAAA,EACxD,CAAC;AACL;"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/fetch.ts","../src/index.ts"],"sourcesContent":["// current version of the cache for token storage\nconst CACHE_VERSION = 1;\n// name of the cache used to store CSRF tokens\nconst CACHE_NAME = `salesforce-lightning-service-worker-${CACHE_VERSION}`;\n// header name\nconst CSRF_HEADER = 'X-CSRF-Token';\n\n/**\n * Provides a safe way to interact with the Cache API with fallback for unsupported environments.\n *\n * @param callback - Function that receives the cache instance and returns a promise\n * @returns The result of the callback, or undefined if caches API is not available\n */\nasync function withCache<T>(callback: (cache: Cache) => Promise<T>): Promise<T | undefined> {\n // Defend against the cache API not being available (e.g., in some test environments)\n if (caches) {\n const cache = await caches.open(CACHE_NAME);\n return callback(cache);\n } else {\n return undefined;\n }\n}\n\n/**\n * Determines if an HTTP method is one that mutates data and requires CSRF protection.\n *\n * @param method - The HTTP method to check\n * @returns true if the method requires CSRF protection (POST, PUT, PATCH, DELETE)\n */\nfunction isProtectedMethod(method: string) {\n const normalizedMethod = method.toLowerCase();\n return (\n normalizedMethod === 'post' ||\n normalizedMethod === 'put' ||\n normalizedMethod === 'patch' ||\n normalizedMethod === 'delete'\n );\n}\n\n/**\n * Determines if the URL is for a path that requires CSRF protection.\n * Currently protects all Salesforce API endpoints under '/services'.\n *\n * @param urlString - The full URL to check\n * @returns true if the URL requires CSRF protection\n * @note This could be made configurable in the future to support custom protected paths\n */\nfunction isProtectedUrl(urlString: string) {\n const url = new URL(urlString);\n // Agentforce Vibes IDE has the form `absproxy/PORT/services/data/...`\n return url.pathname.includes('/services/data/v');\n}\n\n/**\n * Checks if a response indicates that the CSRF token is invalid.\n * Salesforce returns a 400 status with a specific error code when tokens are invalid.\n *\n * @param response - The HTTP response to check\n * @returns true if the response indicates an invalid CSRF token\n */\nasync function isTokenInvalid(response: Response) {\n if (response.status === 400) {\n // clone response to read body without consuming the original stream\n const body = await response.clone().json();\n\n // check for Salesforce's specific invalid token error code\n return body[0]?.errorCode === 'INVALID_ACCESS_TOKEN';\n }\n\n return false;\n}\n\n/**\n * Configuration options for the Lightning fetch creation.\n */\nexport interface LightningFetchConfig {\n /**\n * Optional source for CSRF tokens. Can be:\n * - string: URL path to token endpoint (e.g., '/custom/csrf-endpoint')\n * - URL: Full URL object for token endpoint\n * - function: Custom async function that returns a token string\n *\n * As a string or URL, default fetching and caching (if Cache API is\n * available) will be used to obtain tokens\n */\n tokenSource?: string | URL | (() => Promise<string>);\n\n /**\n * Optional callback for firing events related to fetch operations.\n * Can be used for instrumentation, logging, and monitoring.\n */\n fireEvent?: (eventName: string, id: string, data?: unknown) => void;\n}\n\n/**\n * Creates an enhanced fetch function with automatic CSRF token handling.\n * The returned function automatically adds CSRF tokens to protected requests\n * and handles token refresh when tokens become invalid.\n *\n * @param config - Optional configuration object\n * @returns An enhanced fetch function that handles CSRF protection\n */\nexport function createLightningFetch(config: LightningFetchConfig = {}): typeof fetch {\n const { fireEvent = () => {}, tokenSource } = config;\n\n // default url and provider\n let tokenUrl: string | URL = '/services/data/v65.0/ui-api/session/csrf';\n let tokenProvider = obtainToken;\n\n if (tokenSource) {\n if (typeof tokenSource === 'string' || tokenSource instanceof URL) {\n // use supplied URL with built-in provider\n tokenUrl = tokenSource;\n } else if (typeof tokenSource === 'function') {\n // use external provider\n tokenProvider = tokenSource;\n }\n }\n\n /**\n * Creates a unique identifier to correlate a series of related events.\n */\n function generateId() {\n return Date.now().toString(36);\n }\n\n /**\n * Obtains a CSRF token, using cache when available or fetching a new one.\n *\n * @returns Promise that resolves to the CSRF token string\n */\n async function obtainToken(): Promise<string> {\n const id = generateId();\n fireEvent('csrf_token_obtain_start', id);\n\n // try to get cached token response first\n let response = await withCache((cache) => cache.match(tokenUrl));\n\n if (!response) {\n // no cached response available, fetch a new token\n fireEvent('csrf_token_fetch_start', id);\n response = await fetch(tokenUrl, { method: 'get' });\n fireEvent('csrf_token_fetch_complete', id, { status: response.status });\n } else {\n fireEvent('csrf_token_cache_hit', id);\n }\n\n // extract token from response (clone to avoid consuming original stream)\n const csrfToken: string = (await response.clone().json()).csrfToken;\n\n // cache the response for future use\n await withCache((cache) => cache.put(tokenUrl, response));\n\n fireEvent('csrf_token_obtain_complete', id);\n return csrfToken;\n }\n\n let tokenPromise = tokenProvider();\n\n /**\n * Clears any cached token and initiates retrieval of a fresh one.\n * Used when the current token becomes invalid.\n */\n async function refreshToken() {\n const id = generateId();\n fireEvent('csrf_token_refresh_start', id);\n\n // remove the invalid token from cache\n await withCache((cache) => cache.delete(tokenUrl));\n\n // start obtaining a new token\n tokenPromise = tokenProvider();\n\n fireEvent('csrf_token_refresh_complete', id);\n }\n\n /**\n * Makes a request with the CSRF token header added.\n *\n * @param request - The original request to enhance with CSRF token\n * @returns Promise that resolves to the response\n */\n async function fetchWithToken(request: Request) {\n // clone original headers\n const headers = new Headers(request.headers);\n\n // either use provided token or add one that's been loaded\n if (!headers.has(CSRF_HEADER)) {\n headers.set(CSRF_HEADER, await tokenPromise!);\n }\n\n // execute request with CSRF token header\n return fetch(request, { headers });\n }\n\n /**\n * Enhanced fetch function that applies CSRF token protection to qualifying requests.\n * Automatically adds CSRF tokens to data-mutating requests to protected URLs,\n * with automatic token refresh when tokens become invalid.\n *\n * @param input - The request input (URL, Request, etc.)\n * @param init - Optional request initialization options\n * @returns Promise that resolves to the response\n */\n return async function lightningFetch(\n input: RequestInfo | URL,\n init?: RequestInit\n ): Promise<Response> {\n const id = generateId();\n const request = new Request(input, init);\n\n // check if this request requires CSRF protection (mutating method + protected URL)\n if (isProtectedMethod(request.method) && isProtectedUrl(request.url)) {\n fireEvent('protected_request_start', id, { method: request.method, url: request.url });\n\n // make request with CSRF token (clone to allow retry with fresh request)\n const response = await fetchWithToken(request.clone());\n\n // check if the token was rejected\n if (await isTokenInvalid(response)) {\n fireEvent('csrf_token_invalid', id, { status: response.status });\n\n // token is invalid, refresh and retry once\n await refreshToken();\n const retryResponse = await fetchWithToken(request.clone());\n\n fireEvent('protected_request_complete', id, {\n method: request.method,\n url: request.url,\n status: retryResponse.status,\n retried: true,\n });\n\n return retryResponse;\n } else {\n fireEvent('protected_request_complete', id, {\n method: request.method,\n url: request.url,\n status: response.status,\n retried: false,\n });\n\n // token was valid, return the response\n return response;\n }\n } else {\n fireEvent('unprotected_request', id, { method: request.method, url: request.url });\n\n // no CSRF protection required, use standard fetch\n return fetch(request);\n }\n };\n}\n","import { createLightningFetch } from './fetch';\n\n/**\n * Type alias for the native fetch function\n */\ntype Fetch = typeof fetch;\n\nconst lightningFetch = createLightningFetch();\n\n/**\n * The fetch function used by the client. Defaults to enhanced fetch for CSRF protection.\n * Will be switched to native fetch when CSRF-based service worker is successfully registered.\n */\nlet clientFetch: Fetch = lightningFetch;\n\n/**\n * A client for making HTTP requests with CSRF protection. By default, protection is provided by\n * wrapping the native `fetch` API with functionality that will apply a CSRF token to appropriate\n * requests. This includes functionality to detect expired tokens, triggering a token refresh and\n * retry of the request.\n *\n * Optionally, CSRF protection can be offloaded to a service worker by making the appropriate calls\n * to `registerServiceWorker` and `defineServiceWorker`\n */\nexport class ConduitClient {\n private constructor() {}\n\n /**\n * Makes an HTTP request\n *\n * @param input - The URL, Request object, or relative path to request\n * @param init - Optional request configuration that will be merged with defaults\n * @returns Promise that resolves to the Response object\n */\n fetch(input: string | URL | Request, init: RequestInit = {}): Promise<Response> {\n return clientFetch(input, init);\n }\n\n /**\n * Factory method to create a new ConduitClient instance\n *\n * @returns A new ConduitClient instance\n */\n static create() {\n return new ConduitClient();\n }\n\n /**\n * Registers a service worker for enhanced CSRF protection and caching.\n * When successfully registered, the client will switch to using native fetch\n * as the service worker will handle CSRF protection.\n *\n * The script URL must identify a source file that calls `defineServiceWorker`.\n *\n * @param scriptURL - URL or path to the service worker script\n */\n static async registerServiceWorker(scriptURL: string | URL) {\n // check if service workers are supported in this environment\n if ('serviceWorker' in navigator) {\n try {\n const registration = await navigator.serviceWorker.register(scriptURL, {\n type: 'module',\n });\n\n // successful registration, so switch to native fetch since service worker handles CSRF\n clientFetch = fetch;\n\n console.log('[Conduit Client] Service registration succeeded:', registration);\n } catch (error) {\n console.error(\n '[Conduit Client] Service Worker registration failed (using decorated `fetch`):',\n error\n );\n }\n }\n }\n\n /**\n * Defines the service worker behavior for CSRF protection.\n *\n * This method must be called within a service worker script whose URL is supplied to\n * `registerServiceWorker`\n */\n static defineServiceWorker() {\n const scope = self as any as ServiceWorkerGlobalScope;\n\n // handle service worker installation\n scope.addEventListener('install', (event) => {\n // skip waiting phase to activate immediately\n event.waitUntil(scope.skipWaiting());\n });\n\n // handle service worker activation\n scope.addEventListener('activate', (event) => {\n // take control of all clients immediately\n event.waitUntil(scope.clients.claim());\n });\n\n // intercept all fetch requests and apply CSRF protection\n scope.addEventListener('fetch', (event) => {\n // use enhanced fetch to automatically handle CSRF tokens for protected requests\n event.respondWith(lightningFetch(event.request));\n });\n }\n}\n"],"names":["lightningFetch"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration options for the Lightning fetch creation.
|
|
3
|
+
*/
|
|
4
|
+
export interface LightningFetchConfig {
|
|
5
|
+
/**
|
|
6
|
+
* Optional source for CSRF tokens. Can be:
|
|
7
|
+
* - string: URL path to token endpoint (e.g., '/custom/csrf-endpoint')
|
|
8
|
+
* - URL: Full URL object for token endpoint
|
|
9
|
+
* - function: Custom async function that returns a token string
|
|
10
|
+
*
|
|
11
|
+
* As a string or URL, default fetching and caching (if Cache API is
|
|
12
|
+
* available) will be used to obtain tokens
|
|
13
|
+
*/
|
|
14
|
+
tokenSource?: string | URL | (() => Promise<string>);
|
|
15
|
+
/**
|
|
16
|
+
* Optional callback for firing events related to fetch operations.
|
|
17
|
+
* Can be used for instrumentation, logging, and monitoring.
|
|
18
|
+
*/
|
|
19
|
+
fireEvent?: (eventName: string, id: string, data?: unknown) => void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Creates an enhanced fetch function with automatic CSRF token handling.
|
|
23
|
+
* The returned function automatically adds CSRF tokens to protected requests
|
|
24
|
+
* and handles token refresh when tokens become invalid.
|
|
25
|
+
*
|
|
26
|
+
* @param config - Optional configuration object
|
|
27
|
+
* @returns An enhanced fetch function that handles CSRF protection
|
|
28
|
+
*/
|
|
29
|
+
export declare function createLightningFetch(config?: LightningFetchConfig): typeof fetch;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,11 +1,43 @@
|
|
|
1
|
-
export type Config = {
|
|
2
|
-
version: string;
|
|
3
|
-
debug?: boolean;
|
|
4
|
-
};
|
|
5
1
|
/**
|
|
6
|
-
*
|
|
2
|
+
* A client for making HTTP requests with CSRF protection. By default, protection is provided by
|
|
3
|
+
* wrapping the native `fetch` API with functionality that will apply a CSRF token to appropriate
|
|
4
|
+
* requests. This includes functionality to detect expired tokens, triggering a token refresh and
|
|
5
|
+
* retry of the request.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Optionally, CSRF protection can be offloaded to a service worker by making the appropriate calls
|
|
8
|
+
* to `registerServiceWorker` and `defineServiceWorker`
|
|
10
9
|
*/
|
|
11
|
-
export declare
|
|
10
|
+
export declare class ConduitClient {
|
|
11
|
+
private constructor();
|
|
12
|
+
/**
|
|
13
|
+
* Makes an HTTP request
|
|
14
|
+
*
|
|
15
|
+
* @param input - The URL, Request object, or relative path to request
|
|
16
|
+
* @param init - Optional request configuration that will be merged with defaults
|
|
17
|
+
* @returns Promise that resolves to the Response object
|
|
18
|
+
*/
|
|
19
|
+
fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
|
|
20
|
+
/**
|
|
21
|
+
* Factory method to create a new ConduitClient instance
|
|
22
|
+
*
|
|
23
|
+
* @returns A new ConduitClient instance
|
|
24
|
+
*/
|
|
25
|
+
static create(): ConduitClient;
|
|
26
|
+
/**
|
|
27
|
+
* Registers a service worker for enhanced CSRF protection and caching.
|
|
28
|
+
* When successfully registered, the client will switch to using native fetch
|
|
29
|
+
* as the service worker will handle CSRF protection.
|
|
30
|
+
*
|
|
31
|
+
* The script URL must identify a source file that calls `defineServiceWorker`.
|
|
32
|
+
*
|
|
33
|
+
* @param scriptURL - URL or path to the service worker script
|
|
34
|
+
*/
|
|
35
|
+
static registerServiceWorker(scriptURL: string | URL): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Defines the service worker behavior for CSRF protection.
|
|
38
|
+
*
|
|
39
|
+
* This method must be called within a service worker script whose URL is supplied to
|
|
40
|
+
* `registerServiceWorker`
|
|
41
|
+
*/
|
|
42
|
+
static defineServiceWorker(): void;
|
|
43
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@conduit-client/salesforce-lightning-service-worker",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Service worker for accessing Salesforce data",
|
|
6
6
|
"type": "module",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"size-limit": [
|
|
33
33
|
{
|
|
34
34
|
"path": "dist/index.js",
|
|
35
|
-
"limit": "
|
|
35
|
+
"limit": "1.65 kB"
|
|
36
36
|
}
|
|
37
37
|
]
|
|
38
38
|
}
|
package/dist/types/csrf.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function createCsrfHandler(version: string): (request: Request) => Promise<Response>;
|
|
File without changes
|