@base44-preview/sdk 0.8.46-pr.277.118e9c8 → 0.8.46-pr.277.5cc15f3
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/dist/client.types.d.ts
CHANGED
|
@@ -144,16 +144,16 @@ export interface Base44Client {
|
|
|
144
144
|
*
|
|
145
145
|
* Base44 keeps the user's access token in the browser's local storage, so a plain `fetch()` to your app's server routes arrives without it and the route sees an anonymous caller. `fetchWithAuth()` is the same `fetch()` with the `Authorization: Bearer <token>` header added, which is what lets a server route act on behalf of the signed-in user.
|
|
146
146
|
*
|
|
147
|
-
* Requests are restricted to your app's own origin so the token is never sent to a third party: pass a path such as `/api/orders
|
|
147
|
+
* Requests are restricted to your app's own origin so the token is never sent to a third party: pass a relative path beginning with a single `/`, such as `/api/orders`. An absolute URL, a protocol-relative `//host`, or anything else that a URL parser would read as another origin throws. To call a Base44 backend function, use {@linkcode FunctionsModule.fetch | functions.fetch()}; for another origin, use plain `fetch()`.
|
|
148
148
|
*
|
|
149
|
-
*
|
|
149
|
+
* The path is passed to `fetch` unchanged, so this also works in server code, where the runtime's `fetch` decides what a relative path means — a server-side client from {@linkcode createClientFromRequest | createClientFromRequest()} carries the caller's own token. Note that only the `Authorization` header is added: a route that builds its own client from the incoming request also needs the platform's `Base44-App-Id` and `Base44-Api-Url`, which a request you construct yourself does not have.
|
|
150
150
|
*
|
|
151
|
-
*
|
|
151
|
+
* When no user is signed in the request is sent without an `Authorization` header, so routes that allow anonymous access keep working.
|
|
152
152
|
*
|
|
153
|
-
* @param path - A path on your app's own origin, such as `/api/orders`.
|
|
153
|
+
* @param path - A relative path on your app's own origin, such as `/api/orders`.
|
|
154
154
|
* @param init - Optional [`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) options such as `method`, `headers`, `body`, and `signal`. The auth header is added automatically; an `Authorization` header you set yourself is kept.
|
|
155
155
|
* @returns Promise resolving to a native [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response).
|
|
156
|
-
* @throws {Error} When `path`
|
|
156
|
+
* @throws {Error} When `path` is not a relative path on your app's own origin.
|
|
157
157
|
*
|
|
158
158
|
* @example
|
|
159
159
|
* ```typescript
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { AxiosInstance } from "axios";
|
|
2
2
|
/**
|
|
3
3
|
* Builds the client's `fetchWithAuth`: a `fetch` that attaches the signed-in
|
|
4
|
-
* user's access token
|
|
4
|
+
* user's access token to a request for the app's own origin.
|
|
5
5
|
*
|
|
6
6
|
* @param axios - The user-scoped axios instance. Its `Authorization` default is
|
|
7
7
|
* the live token: it follows `setToken()` and is deleted on `logout()`, so a
|
|
8
|
-
* request never carries a token the user no longer has.
|
|
8
|
+
* request never carries a token the user no longer has. In a server-side client
|
|
9
|
+
* from `createClientFromRequest()` it holds the caller's own token.
|
|
9
10
|
* @internal
|
|
10
11
|
*/
|
|
11
12
|
export declare function createFetchWithAuth(axios: AxiosInstance): (path: string, init?: RequestInit) => Promise<Response>;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Builds the client's `fetchWithAuth`: a `fetch` that attaches the signed-in
|
|
3
|
-
* user's access token
|
|
3
|
+
* user's access token to a request for the app's own origin.
|
|
4
4
|
*
|
|
5
5
|
* @param axios - The user-scoped axios instance. Its `Authorization` default is
|
|
6
6
|
* the live token: it follows `setToken()` and is deleted on `logout()`, so a
|
|
7
|
-
* request never carries a token the user no longer has.
|
|
7
|
+
* request never carries a token the user no longer has. In a server-side client
|
|
8
|
+
* from `createClientFromRequest()` it holds the caller's own token.
|
|
8
9
|
* @internal
|
|
9
10
|
*/
|
|
10
11
|
export function createFetchWithAuth(axios) {
|
|
@@ -16,37 +17,33 @@ export function createFetchWithAuth(axios) {
|
|
|
16
17
|
return header.slice("Bearer ".length) || null;
|
|
17
18
|
};
|
|
18
19
|
return async function fetchWithAuth(path, init = {}) {
|
|
19
|
-
|
|
20
|
+
assertOwnOriginPath(path);
|
|
20
21
|
const headers = new Headers(init.headers);
|
|
21
22
|
const token = currentToken();
|
|
22
23
|
if (token && !headers.has("Authorization")) {
|
|
23
24
|
headers.set("Authorization", `Bearer ${token}`);
|
|
24
25
|
}
|
|
25
|
-
|
|
26
|
+
// Passed through untouched: resolving it here would need a document, and a
|
|
27
|
+
// root-relative path is already what a runtime that dispatches in-process
|
|
28
|
+
// (Nitro's `fetch`) expects.
|
|
29
|
+
return fetch(path, { ...init, headers });
|
|
26
30
|
};
|
|
27
31
|
}
|
|
28
|
-
function
|
|
32
|
+
function assertOwnOriginPath(path) {
|
|
29
33
|
if (typeof path !== "string" || path === "") {
|
|
30
34
|
throw new Error("fetchWithAuth() requires a path, such as '/api/orders'.");
|
|
31
35
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
// Check what a URL parser would see, not the raw string: it drops every ASCII
|
|
37
|
+
// tab/newline anywhere in the input and trims leading C0/space, so
|
|
38
|
+
// "/<tab>/evil.example" would pass a naive prefix check and then resolve to
|
|
39
|
+
// another host.
|
|
40
|
+
const asParsed = path.replace(/[\t\n\r]/g, "").replace(/^[\x00-\x20]+/, "");
|
|
41
|
+
// One leading slash is the whole rule: it cannot carry a scheme, and it rules
|
|
42
|
+
// out the two forms that reach another origin — "//host" and, since URL
|
|
43
|
+
// parsing treats a backslash as a slash, "/\host".
|
|
44
|
+
if (!asParsed.startsWith("/") ||
|
|
45
|
+
asParsed.startsWith("//") ||
|
|
46
|
+
asParsed.startsWith("/\\")) {
|
|
47
|
+
throw new Error(`fetchWithAuth() only sends requests to your app's own origin, so the access token never reaches a third party. "${path}" is not a path on it — pass a relative path such as '/api/orders'. Use base44.functions.fetch() to call a Base44 backend function, or plain fetch() for another origin.`);
|
|
35
48
|
}
|
|
36
|
-
let pageUrl;
|
|
37
|
-
let resolved;
|
|
38
|
-
try {
|
|
39
|
-
pageUrl = new URL(location.href);
|
|
40
|
-
resolved = new URL(path, pageUrl);
|
|
41
|
-
}
|
|
42
|
-
catch (_a) {
|
|
43
|
-
throw new Error(`fetchWithAuth() received an invalid path: "${path}".`);
|
|
44
|
-
}
|
|
45
|
-
// Resolving before comparing is what makes this safe: a protocol-relative
|
|
46
|
-
// path ("//evil.example"), a backslash ("/\\evil.example") and an absolute URL
|
|
47
|
-
// all land on another origin here, and are rejected the same way.
|
|
48
|
-
if (resolved.origin !== pageUrl.origin) {
|
|
49
|
-
throw new Error(`fetchWithAuth() only sends requests to your app's own origin, so the access token never reaches a third party. "${path}" resolves to ${resolved.origin}. Use base44.functions.fetch() to call a Base44 backend function, or plain fetch() for another origin.`);
|
|
50
|
-
}
|
|
51
|
-
return resolved.toString();
|
|
52
49
|
}
|