@salesforce/webapp-experimental 1.25.2 → 1.27.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.
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/proxy/handler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGjE,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAM/D;;GAEG;AACH,eAAO,MAAM,yBAAyB,uBAAuB,CAAC;AAE9D;;GAEG;AACH,eAAO,MAAM,mBAAmB,8BAA8B,CAAC;AAE/D;;GAEG;AACH,MAAM,WAAW,YAAY;IAE5B,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB,cAAc,CAAC,EAAE,CAAC,cAAc,EAAE,OAAO,KAAK,IAAI,CAAC;CACnD;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,CAC1B,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,CAAC,EAAE,MAAM,IAAI,KACb,OAAO,CAAC,IAAI,CAAC,CAAC;AA6UnB;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CACjC,QAAQ,EAAE,cAAc,EACxB,OAAO,CAAC,EAAE,OAAO,EACjB,MAAM,CAAC,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,YAAY,GACpB,YAAY,CAGd"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/proxy/handler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGjE,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAM/D;;GAEG;AACH,eAAO,MAAM,yBAAyB,uBAAuB,CAAC;AAE9D;;GAEG;AACH,eAAO,MAAM,mBAAmB,8BAA8B,CAAC;AAO/D;;GAEG;AACH,MAAM,WAAW,YAAY;IAE5B,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB,cAAc,CAAC,EAAE,CAAC,cAAc,EAAE,OAAO,KAAK,IAAI,CAAC;CACnD;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,CAC1B,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,CAAC,EAAE,MAAM,IAAI,KACb,OAAO,CAAC,IAAI,CAAC,CAAC;AAqanB;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CACjC,QAAQ,EAAE,cAAc,EACxB,OAAO,CAAC,EAAE,OAAO,EACjB,MAAM,CAAC,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,YAAY,GACpB,YAAY,CAGd"}
@@ -16,6 +16,10 @@ export const WEBAPP_HEALTH_CHECK_PARAM = "sfProxyHealthCheck";
16
16
  * Response header indicating the proxy is active
17
17
  */
18
18
  export const WEBAPP_PROXY_HEADER = "X-Salesforce-WebApp-Proxy";
19
+ /**
20
+ * Endpoint for Lightning Out frontdoor URL generation
21
+ */
22
+ const LIGHTNING_OUT_SINGLE_ACCESS_PATH = "/services/oauth2/singleaccess";
19
23
  /**
20
24
  * Handles all proxy routing and forwarding for WebApps
21
25
  */
@@ -41,6 +45,10 @@ class WebAppProxyHandler {
41
45
  return;
42
46
  }
43
47
  let pathname = url.pathname;
48
+ if (pathname === "/__lo/frontdoor") {
49
+ await this.handleLightningOutFrontdoor(req, res, url);
50
+ return;
51
+ }
44
52
  if (this.options?.debug) {
45
53
  console.log(`[webapps-proxy] ${req.method} ${pathname}`);
46
54
  }
@@ -104,6 +112,73 @@ class WebAppProxyHandler {
104
112
  message: "No default Salesforce org found. Run 'sf org login web --set-default' to authenticate.",
105
113
  }));
106
114
  }
115
+ sendJson(res, statusCode, body) {
116
+ res.writeHead(statusCode, { "Content-Type": "application/json" });
117
+ res.end(JSON.stringify(body));
118
+ }
119
+ async handleLightningOutFrontdoor(req, res, _url) {
120
+ try {
121
+ if (!this.orgInfo) {
122
+ this.sendNoOrgError(res);
123
+ return;
124
+ }
125
+ if (req.method && req.method !== "GET") {
126
+ this.sendJson(res, 405, { error: "METHOD_NOT_ALLOWED", message: "Use GET" });
127
+ return;
128
+ }
129
+ const { rawInstanceUrl, accessToken, orgId } = this.orgInfo;
130
+ const frontdoorUrl = await this.fetchLightningOutFrontdoorUrl(rawInstanceUrl, accessToken);
131
+ if (!frontdoorUrl) {
132
+ this.sendJson(res, 502, {
133
+ error: "FRONTDOOR_FAILED",
134
+ message: "No frontdoor URL returned from Salesforce",
135
+ });
136
+ return;
137
+ }
138
+ this.sendJson(res, 200, {
139
+ frontdoorUrl,
140
+ instanceUrl: rawInstanceUrl,
141
+ orgId,
142
+ });
143
+ }
144
+ catch (error) {
145
+ console.error("[webapps-proxy] Frontdoor request failed:", error);
146
+ this.sendJson(res, 502, {
147
+ error: "GATEWAY_ERROR",
148
+ message: "Failed to generate frontdoor URL",
149
+ });
150
+ }
151
+ }
152
+ async fetchLightningOutFrontdoorUrl(rawInstanceUrl, accessToken) {
153
+ let baseUrl = rawInstanceUrl.replace(/\/$/, "");
154
+ let response = await fetch(`${baseUrl}${LIGHTNING_OUT_SINGLE_ACCESS_PATH}`, {
155
+ method: "POST",
156
+ headers: {
157
+ Authorization: `Bearer ${accessToken}`,
158
+ "Content-Type": "application/x-www-form-urlencoded",
159
+ },
160
+ });
161
+ if (response.status === 401 || response.status === 403) {
162
+ const updatedOrgInfo = await this.refreshToken();
163
+ if (!updatedOrgInfo) {
164
+ throw new Error("Failed to refresh token");
165
+ }
166
+ baseUrl = updatedOrgInfo.rawInstanceUrl.replace(/\/$/, "");
167
+ response = await fetch(`${baseUrl}${LIGHTNING_OUT_SINGLE_ACCESS_PATH}`, {
168
+ method: "POST",
169
+ headers: {
170
+ Authorization: `Bearer ${updatedOrgInfo.accessToken}`,
171
+ "Content-Type": "application/x-www-form-urlencoded",
172
+ },
173
+ });
174
+ }
175
+ if (!response.ok) {
176
+ const errorBody = await response.text();
177
+ throw new Error(`Frontdoor exchange failed: ${response.status} ${errorBody}`);
178
+ }
179
+ const data = (await response.json());
180
+ return data.frontdoor_uri ?? null;
181
+ }
107
182
  async handleSalesforceApi(req, res) {
108
183
  try {
109
184
  if (!this.orgInfo) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@salesforce/webapp-experimental",
3
3
  "description": "[experimental] Core package for Salesforce Web Applications",
4
- "version": "1.25.2",
4
+ "version": "1.27.0",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
@@ -55,5 +55,5 @@
55
55
  "publishConfig": {
56
56
  "access": "public"
57
57
  },
58
- "gitHead": "6e4299053c2bdad6b1eb6d81c983368bd539e711"
58
+ "gitHead": "b162807f5062f6aee52605127b787de508035fc9"
59
59
  }