@base44-preview/sdk 0.8.48-pr.282.2b05017 → 0.8.48-pr.282.3c9bac2
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.js +25 -48
- package/dist/modules/auth.js +7 -4
- package/dist/modules/auth.types.d.ts +5 -0
- package/dist/utils/auth-utils.d.ts +2 -0
- package/dist/utils/auth-utils.js +11 -0
- package/dist/utils/embed-session.d.ts +2 -0
- package/dist/utils/embed-session.js +4 -3
- package/dist/utils/socket-utils.d.ts +3 -4
- package/dist/utils/socket-utils.js +5 -10
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -55,37 +55,16 @@ import { createActorsModule, resolveActorsHost, } from "./modules/actors.js";
|
|
|
55
55
|
*/
|
|
56
56
|
export function createClient(config) {
|
|
57
57
|
var _a, _b, _c, _d;
|
|
58
|
-
const { serverUrl = "https://base44.app", appId, analytics,
|
|
58
|
+
const { serverUrl = "https://base44.app", appId, analytics, serviceToken, requiresAuth = false, appBaseUrl, options, functionsVersion, headers: optionalHeaders, } = config;
|
|
59
59
|
// Normalize appBaseUrl to always be a string (empty if not provided or invalid)
|
|
60
60
|
const normalizedAppBaseUrl = typeof appBaseUrl === "string" ? appBaseUrl : "";
|
|
61
|
-
const
|
|
62
|
-
// A host platform embeds the app with a one-time token on the iframe URL.
|
|
63
|
-
// It comes off the URL here, before any other code can read it; the
|
|
64
|
-
// exchange that turns it into a session runs once the modules exist.
|
|
65
|
-
const embedOtt = inBrowser ? takeEmbedTokenFromUrl() : null;
|
|
66
|
-
const embedded = embedOtt !== null || (inBrowser && isEmbeddedTab());
|
|
61
|
+
const embedOtt = takeEmbedTokenFromUrl();
|
|
67
62
|
if (embedOtt) {
|
|
68
63
|
markEmbeddedTab();
|
|
69
64
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
mountPath: "/ws-user-apps/socket.io/",
|
|
74
|
-
transports: ["websocket"],
|
|
75
|
-
appId,
|
|
76
|
-
// null (not undefined): an embedded frame's only identity is the one the
|
|
77
|
-
// platform minted, so the socket must not fall back to a stored token.
|
|
78
|
-
token: embedded ? null : token,
|
|
79
|
-
};
|
|
80
|
-
let socket = null;
|
|
81
|
-
const getSocket = () => {
|
|
82
|
-
if (!socket) {
|
|
83
|
-
socket = RoomsSocket({
|
|
84
|
-
config: socketConfig,
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
return socket;
|
|
88
|
-
};
|
|
65
|
+
const embedded = Boolean(embedOtt) || isEmbeddedTab();
|
|
66
|
+
// In a frame the passed token is the OTT itself or a stale stored one, not this session.
|
|
67
|
+
const token = embedded ? undefined : config.token;
|
|
89
68
|
const headers = {
|
|
90
69
|
...optionalHeaders,
|
|
91
70
|
"X-App-Id": String(appId),
|
|
@@ -144,35 +123,34 @@ export function createClient(config) {
|
|
|
144
123
|
// requests during construction (notably analytics, which fires an init
|
|
145
124
|
// event whose flush calls auth.me()). Without this, the first User/me
|
|
146
125
|
// request is built before setToken runs and goes out unauthenticated.
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
if (inBrowser && !embedded) {
|
|
126
|
+
// Skipped in a frame: the session comes from the exchange below.
|
|
127
|
+
if (typeof window !== "undefined" && !embedded) {
|
|
150
128
|
const accessToken = token || getAccessToken();
|
|
151
129
|
if (accessToken) {
|
|
152
|
-
currentToken = accessToken;
|
|
153
130
|
userAuthModule.setToken(accessToken);
|
|
154
131
|
}
|
|
155
132
|
}
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
133
|
+
// Live token for every module; outside a frame it still falls back to storage.
|
|
134
|
+
const getToken = () => { var _a; return (_a = userAuthModule.getToken()) !== null && _a !== void 0 ? _a : (embedded ? null : getAccessToken()); };
|
|
135
|
+
const socketConfig = {
|
|
136
|
+
serverUrl,
|
|
137
|
+
mountPath: "/ws-user-apps/socket.io/",
|
|
138
|
+
transports: ["websocket"],
|
|
139
|
+
appId,
|
|
140
|
+
getToken,
|
|
141
|
+
};
|
|
142
|
+
let socket = null;
|
|
143
|
+
const getSocket = () => {
|
|
144
|
+
if (!socket) {
|
|
145
|
+
socket = RoomsSocket({ config: socketConfig });
|
|
162
146
|
}
|
|
163
|
-
return
|
|
147
|
+
return socket;
|
|
164
148
|
};
|
|
165
149
|
const applyToken = (newToken, saveToStorage) => {
|
|
166
150
|
userAuthModule.setToken(newToken, saveToStorage);
|
|
167
|
-
|
|
168
|
-
socketConfig.token = newToken;
|
|
169
|
-
if (socket) {
|
|
170
|
-
socket.updateConfig({ token: newToken });
|
|
171
|
-
}
|
|
151
|
+
socket === null || socket === void 0 ? void 0 : socket.reconnect();
|
|
172
152
|
};
|
|
173
|
-
//
|
|
174
|
-
// immediately for every other client. The exchanged token is applied without
|
|
175
|
-
// persisting it: the session must not outlive the frame it was minted for.
|
|
153
|
+
// Settles once the exchanged session is applied (memory only); at once otherwise.
|
|
176
154
|
const authReady = embedOtt
|
|
177
155
|
? exchangeEmbedToken({ serverUrl, appId, ott: embedOtt }).then((sessionToken) => {
|
|
178
156
|
if (sessionToken) {
|
|
@@ -181,9 +159,8 @@ export function createClient(config) {
|
|
|
181
159
|
})
|
|
182
160
|
: Promise.resolve();
|
|
183
161
|
if (embedOtt) {
|
|
184
|
-
// Requests issued
|
|
185
|
-
//
|
|
186
|
-
// so it runs first and the anonymous-visitor header sees the Authorization.
|
|
162
|
+
// Requests issued during the exchange wait for it. Registered after createAxiosClient's
|
|
163
|
+
// interceptors so it runs first and the anonymous-visitor header sees the Authorization.
|
|
187
164
|
for (const client of [axiosClient, functionsAxiosClient]) {
|
|
188
165
|
client.interceptors.request.use(async (requestConfig) => {
|
|
189
166
|
await authReady;
|
package/dist/modules/auth.js
CHANGED
|
@@ -82,10 +82,13 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
|
|
|
82
82
|
// Tracked here rather than read off `axios.defaults` so the answer stays tied
|
|
83
83
|
// to the identity transitions below (`setToken`, `logout`) instead of to the
|
|
84
84
|
// header a caller may have set on the instance directly.
|
|
85
|
-
let
|
|
85
|
+
let accessToken = options.token || null;
|
|
86
86
|
return {
|
|
87
87
|
hasToken() {
|
|
88
|
-
return
|
|
88
|
+
return accessToken !== null;
|
|
89
|
+
},
|
|
90
|
+
getToken() {
|
|
91
|
+
return accessToken;
|
|
89
92
|
},
|
|
90
93
|
isEmbedded() {
|
|
91
94
|
return Boolean(options.embedded);
|
|
@@ -161,7 +164,7 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
|
|
|
161
164
|
// flight would otherwise resolve into callers that run after the logout.
|
|
162
165
|
clearPendingMe();
|
|
163
166
|
resetAnalyticsSessionContext();
|
|
164
|
-
|
|
167
|
+
accessToken = null;
|
|
165
168
|
// Only do the rest if in a browser environment
|
|
166
169
|
if (typeof window !== "undefined") {
|
|
167
170
|
// Remove token from localStorage
|
|
@@ -190,7 +193,7 @@ export function createAuthModule(axios, functionsAxiosClient, appId, options) {
|
|
|
190
193
|
// resolved for the previous one must not be handed to later callers.
|
|
191
194
|
clearPendingMe();
|
|
192
195
|
resetAnalyticsSessionContext();
|
|
193
|
-
|
|
196
|
+
accessToken = token;
|
|
194
197
|
// handle token change for axios clients
|
|
195
198
|
axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
|
|
196
199
|
functionsAxiosClient.defaults.headers.common["Authorization"] = `Bearer ${token}`;
|
|
@@ -560,4 +560,9 @@ export interface InternalAuthModule extends AuthModule {
|
|
|
560
560
|
* could not succeed without a session, not to decide that one is valid.
|
|
561
561
|
*/
|
|
562
562
|
hasToken(): boolean;
|
|
563
|
+
/**
|
|
564
|
+
* The access token currently set on the client, or `null` when there is none.
|
|
565
|
+
* Follows {@linkcode AuthModule.setToken | setToken} and {@linkcode AuthModule.logout | logout}.
|
|
566
|
+
*/
|
|
567
|
+
getToken(): string | null;
|
|
563
568
|
}
|
|
@@ -5,6 +5,8 @@ import { GetAccessTokenOptions, SaveAccessTokenOptions, RemoveAccessTokenOptions
|
|
|
5
5
|
* Low-level utility for manually retrieving tokens. In most cases, the Base44 client handles
|
|
6
6
|
* token management automatically. This function is useful for custom authentication flows or when you need direct access to stored tokens. Requires a browser environment and can't be used in the backend.
|
|
7
7
|
*
|
|
8
|
+
* When a host platform has embedded the app with a one-time token (`?ott=`), that token is returned as it stands: it is what {@linkcode createClient} trades for the session, so a page that gates on "is there a token?" as it loads sees one. It is neither stored nor removed from the URL here.
|
|
9
|
+
*
|
|
8
10
|
* @internal
|
|
9
11
|
*
|
|
10
12
|
* @param options - Configuration options for token retrieval.
|
package/dist/utils/auth-utils.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { EMBED_TOKEN_PARAM } from "./embed-session.js";
|
|
1
2
|
/**
|
|
2
3
|
* Retrieves an access token from URL parameters or local storage.
|
|
3
4
|
*
|
|
4
5
|
* Low-level utility for manually retrieving tokens. In most cases, the Base44 client handles
|
|
5
6
|
* token management automatically. This function is useful for custom authentication flows or when you need direct access to stored tokens. Requires a browser environment and can't be used in the backend.
|
|
6
7
|
*
|
|
8
|
+
* When a host platform has embedded the app with a one-time token (`?ott=`), that token is returned as it stands: it is what {@linkcode createClient} trades for the session, so a page that gates on "is there a token?" as it loads sees one. It is neither stored nor removed from the URL here.
|
|
9
|
+
*
|
|
7
10
|
* @internal
|
|
8
11
|
*
|
|
9
12
|
* @param options - Configuration options for token retrieval.
|
|
@@ -56,6 +59,14 @@ export function getAccessToken(options = {}) {
|
|
|
56
59
|
}
|
|
57
60
|
return token;
|
|
58
61
|
}
|
|
62
|
+
// A platform-embedded load carries a one-time token instead. It is not a
|
|
63
|
+
// session yet — createClient takes it off the URL and exchanges it — but
|
|
64
|
+
// it is the identity this load arrives with, and callers that read this
|
|
65
|
+
// once as the page loads must not conclude there is none.
|
|
66
|
+
const embedToken = urlParams.get(EMBED_TOKEN_PARAM);
|
|
67
|
+
if (embedToken) {
|
|
68
|
+
return embedToken;
|
|
69
|
+
}
|
|
59
70
|
}
|
|
60
71
|
catch (e) {
|
|
61
72
|
console.error("Error retrieving token from URL:", e);
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @internal
|
|
11
11
|
*/
|
|
12
|
+
/** The query parameter a host platform puts the one-time token on. @internal */
|
|
13
|
+
export declare const EMBED_TOKEN_PARAM = "ott";
|
|
12
14
|
/**
|
|
13
15
|
* Reads the one-time token off the current URL and strips it, so it is never
|
|
14
16
|
* left in the address bar, in history, or in a shared link.
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @internal
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
/** The query parameter a host platform puts the one-time token on. @internal */
|
|
13
|
+
export const EMBED_TOKEN_PARAM = "ott";
|
|
13
14
|
const EMBED_TAB_KEY = "base44_embed_session";
|
|
14
15
|
const GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange";
|
|
15
16
|
const SUBJECT_TOKEN_TYPE = "urn:base44:params:oauth:token-type:embed-ott";
|
|
@@ -28,11 +29,11 @@ export function takeEmbedTokenFromUrl() {
|
|
|
28
29
|
}
|
|
29
30
|
try {
|
|
30
31
|
const url = new URL(window.location.href);
|
|
31
|
-
const ott = url.searchParams.get(
|
|
32
|
+
const ott = url.searchParams.get(EMBED_TOKEN_PARAM);
|
|
32
33
|
if (!ott) {
|
|
33
34
|
return null;
|
|
34
35
|
}
|
|
35
|
-
url.searchParams.delete(
|
|
36
|
+
url.searchParams.delete(EMBED_TOKEN_PARAM);
|
|
36
37
|
window.history.replaceState(window.history.state, "", url.toString());
|
|
37
38
|
return ott;
|
|
38
39
|
}
|
|
@@ -4,9 +4,8 @@ export interface RoomsSocketConfig {
|
|
|
4
4
|
mountPath: string;
|
|
5
5
|
transports: string[];
|
|
6
6
|
appId: string;
|
|
7
|
-
/**
|
|
8
|
-
|
|
9
|
-
token?: string | null;
|
|
7
|
+
/** Asked on every connect, so the socket always carries the current session. */
|
|
8
|
+
getToken: () => string | null;
|
|
10
9
|
}
|
|
11
10
|
export type TSocketRoom = string;
|
|
12
11
|
export type TJsonStr = string;
|
|
@@ -42,7 +41,7 @@ export declare function RoomsSocket({ config }: {
|
|
|
42
41
|
leave: (room: string) => void;
|
|
43
42
|
}>;
|
|
44
43
|
subscribeToRoom: (room: TSocketRoom, handlers: Partial<{ [k in TEvent]: THandler<k>; }>) => () => void;
|
|
45
|
-
|
|
44
|
+
reconnect: () => void;
|
|
46
45
|
updateModel: (room: string, data: any) => Promise<void>;
|
|
47
46
|
disconnect: () => void;
|
|
48
47
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { io } from "socket.io-client";
|
|
2
|
-
import { getAccessToken } from "./auth-utils.js";
|
|
3
2
|
import { getAnalyticsSessionId } from "../modules/analytics.js";
|
|
4
3
|
const ROOM_LEAVE_GRACE_MS = 250;
|
|
5
4
|
function initializeSocket(config, handlers) {
|
|
@@ -7,7 +6,7 @@ function initializeSocket(config, handlers) {
|
|
|
7
6
|
// handshake so the backend can verify room access for anonymous agent
|
|
8
7
|
// conversations (mirrors the X-Base44-Anonymous-Id HTTP header). Authenticated
|
|
9
8
|
// clients are identified by their token instead.
|
|
10
|
-
const resolvedToken = config.
|
|
9
|
+
const resolvedToken = config.getToken();
|
|
11
10
|
const query = {
|
|
12
11
|
app_id: config.appId,
|
|
13
12
|
token: resolvedToken,
|
|
@@ -41,7 +40,6 @@ function initializeSocket(config, handlers) {
|
|
|
41
40
|
return socket;
|
|
42
41
|
}
|
|
43
42
|
export function RoomsSocket({ config }) {
|
|
44
|
-
let currentConfig = { ...config };
|
|
45
43
|
const roomsToListeners = {};
|
|
46
44
|
const pendingRoomLeaves = {};
|
|
47
45
|
const handlers = {
|
|
@@ -83,13 +81,10 @@ export function RoomsSocket({ config }) {
|
|
|
83
81
|
socket.disconnect();
|
|
84
82
|
}
|
|
85
83
|
}
|
|
86
|
-
|
|
84
|
+
/** Drops the connection and opens a new one with the current token. */
|
|
85
|
+
function reconnect() {
|
|
87
86
|
cleanup();
|
|
88
|
-
|
|
89
|
-
...currentConfig,
|
|
90
|
-
...config,
|
|
91
|
-
};
|
|
92
|
-
socket = initializeSocket(currentConfig, handlers);
|
|
87
|
+
socket = initializeSocket(config, handlers);
|
|
93
88
|
}
|
|
94
89
|
function joinRoom(room) {
|
|
95
90
|
socket.emit("join", room);
|
|
@@ -162,7 +157,7 @@ export function RoomsSocket({ config }) {
|
|
|
162
157
|
return {
|
|
163
158
|
socket,
|
|
164
159
|
subscribeToRoom,
|
|
165
|
-
|
|
160
|
+
reconnect,
|
|
166
161
|
updateModel,
|
|
167
162
|
disconnect,
|
|
168
163
|
};
|