@blazeo.com/calendar-client 1.0.27 → 1.0.29
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 +18 -23
- package/dist/index.d.ts +22 -21
- package/dist/index.js +47 -127
- package/dist/index.mjs +45 -121
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,23 +24,19 @@ Configure once at app startup, then use models and their methods:
|
|
|
24
24
|
```js
|
|
25
25
|
import {
|
|
26
26
|
configure,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
setApiCredentials,
|
|
30
|
-
fetchAccessToken,
|
|
27
|
+
setAccessToken,
|
|
28
|
+
setGetAccessToken,
|
|
31
29
|
CalendarModel,
|
|
32
30
|
createRootStore,
|
|
33
31
|
} from '@blazeo.com/calendar-client';
|
|
34
32
|
|
|
35
|
-
configure({
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// Or set a token you obtained elsewhere:
|
|
43
|
-
// setAccessToken('eyJ...', '2026-05-19T12:00:00Z');
|
|
33
|
+
configure({
|
|
34
|
+
baseUrl: 'https://your-appointment-api.example.com',
|
|
35
|
+
consumer: 'my-app', // optional Consumer header
|
|
36
|
+
accessToken: yourIdpAccessToken, // JWT from your identity provider
|
|
37
|
+
expiresAtUtc: '2026-05-21T12:00:00Z', // optional — used before auto-refresh
|
|
38
|
+
getAccessToken: async () => yourIdp.getAccessToken(), // optional refresh callback
|
|
39
|
+
});
|
|
44
40
|
|
|
45
41
|
// Calendar static methods (no store needed)
|
|
46
42
|
const timezones = await CalendarModel.getTimeZones();
|
|
@@ -52,23 +48,22 @@ const cal = store.addCalendar({ calendarId: 'my-cal', name: 'My Calendar' });
|
|
|
52
48
|
await cal.create(); // POST to backend
|
|
53
49
|
```
|
|
54
50
|
|
|
55
|
-
###
|
|
51
|
+
### JWT authentication
|
|
56
52
|
|
|
57
|
-
The
|
|
53
|
+
The Appointment API validates Bearer JWTs from your identity provider (`Authentication:Jwt:Authority` on the server). This package does **not** exchange api keys for tokens — pass the JWT your app already has.
|
|
58
54
|
|
|
59
55
|
| Function | Purpose |
|
|
60
56
|
|----------|---------|
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `clearAuth()` | Clear token and credentials |
|
|
57
|
+
| `setAccessToken(token, expiresAtUtc?)` | Store JWT from your IdP |
|
|
58
|
+
| `setGetAccessToken(fn)` | Async callback to refresh when missing/near expiry |
|
|
59
|
+
| `ensureValidAccessToken()` | Resolve token before a request (uses callback if needed) |
|
|
60
|
+
| `clearAuth()` | Clear token and refresh callback |
|
|
66
61
|
|
|
67
|
-
All `reqGet` / `reqPost` calls
|
|
62
|
+
All `reqGet` / `reqPost` calls attach `Authorization: Bearer …` when a token is configured.
|
|
68
63
|
|
|
69
64
|
```js
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
setAccessToken(session.accessToken, session.expiresAt);
|
|
66
|
+
setGetAccessToken(() => authService.acquireTokenSilent());
|
|
72
67
|
await LeadModel.requestExport('company-key');
|
|
73
68
|
```
|
|
74
69
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,19 +1,8 @@
|
|
|
1
1
|
/** @blazeo.com/calendar-client - type declarations */
|
|
2
2
|
|
|
3
|
-
export type AccessTokenResult = {
|
|
4
|
-
status: string;
|
|
5
|
-
message?: string;
|
|
6
|
-
accessToken?: string;
|
|
7
|
-
expiresAtUtc?: string | null;
|
|
8
|
-
tokenType?: string;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
3
|
export type AuthState = {
|
|
12
4
|
accessToken?: string;
|
|
13
5
|
tokenExpiresAt?: string;
|
|
14
|
-
apiKey?: string;
|
|
15
|
-
apiSecret?: string;
|
|
16
|
-
hasApiCredentials: boolean;
|
|
17
6
|
};
|
|
18
7
|
|
|
19
8
|
export function configure(env: {
|
|
@@ -25,10 +14,8 @@ export function configure(env: {
|
|
|
25
14
|
expiresAtUtc?: string;
|
|
26
15
|
tokenExpiresAt?: string;
|
|
27
16
|
expires_at_utc?: string;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
apiSecret?: string;
|
|
31
|
-
api_secret?: string;
|
|
17
|
+
/** Called when the stored JWT is missing or near expiry; return a fresh token from your IdP. */
|
|
18
|
+
getAccessToken?: () => Promise<string | undefined>;
|
|
32
19
|
}): void;
|
|
33
20
|
|
|
34
21
|
export function getConfig(): {
|
|
@@ -38,23 +25,18 @@ export function getConfig(): {
|
|
|
38
25
|
getDefaultOffset?: () => number;
|
|
39
26
|
accessToken?: string;
|
|
40
27
|
tokenExpiresAt?: string;
|
|
41
|
-
hasApiCredentials?: boolean;
|
|
42
28
|
} | null;
|
|
43
29
|
|
|
44
30
|
export function setBaseUrl(baseUrl: string): void;
|
|
45
31
|
export function setConsumer(consumer: string): void;
|
|
46
32
|
export function setAccessToken(accessToken: string, expiresAtUtc?: string): void;
|
|
47
33
|
export function clearAccessToken(): void;
|
|
48
|
-
export function
|
|
49
|
-
export function clearApiCredentials(): void;
|
|
34
|
+
export function setGetAccessToken(fn: () => Promise<string | undefined>): void;
|
|
50
35
|
export function clearAuth(): void;
|
|
51
36
|
export function getAuth(): AuthState;
|
|
52
|
-
export function fetchAccessToken(apiKey?: string, apiSecret?: string): Promise<AccessTokenResult>;
|
|
53
37
|
export function ensureValidAccessToken(): Promise<string | undefined>;
|
|
54
|
-
export function requestAccessToken(apiKey: string, apiSecret: string, opts?: { baseUrl?: string; fetch?: typeof fetch }): Promise<AccessTokenResult>;
|
|
55
38
|
export function isAccessTokenExpired(expiresAtUtc?: string | number | Date | null, skewMs?: number): boolean;
|
|
56
39
|
export function buildAuthHeaders(extra?: Record<string, string>): Record<string, string>;
|
|
57
|
-
export const TOKEN_PATH: '/Api/Auth/Token';
|
|
58
40
|
export const DEFAULT_TOKEN_REFRESH_SKEW_MS: number;
|
|
59
41
|
export function getConfigStore(): unknown;
|
|
60
42
|
|
|
@@ -336,6 +318,20 @@ export type ParticipantAuthorizationStatus = {
|
|
|
336
318
|
providers: CalendarProviderAuthorizationState[];
|
|
337
319
|
};
|
|
338
320
|
|
|
321
|
+
export type ParticipantData = {
|
|
322
|
+
id?: number | null;
|
|
323
|
+
participantId: string;
|
|
324
|
+
companyKey?: string | null;
|
|
325
|
+
alias?: string;
|
|
326
|
+
email?: string;
|
|
327
|
+
isApproved: boolean;
|
|
328
|
+
isAvailable: boolean;
|
|
329
|
+
provider: number;
|
|
330
|
+
createdOn?: string | null;
|
|
331
|
+
modifiedOn?: string | null;
|
|
332
|
+
isDeleted?: boolean;
|
|
333
|
+
};
|
|
334
|
+
|
|
339
335
|
export const CALENDAR_AUTH_MESSAGE_TYPE: 'calendar-authorization';
|
|
340
336
|
|
|
341
337
|
export const CalendarEmailProvider: {
|
|
@@ -366,6 +362,11 @@ export const AuthModel: {
|
|
|
366
362
|
data?: ParticipantAuthorizationStatus;
|
|
367
363
|
message?: string;
|
|
368
364
|
}>;
|
|
365
|
+
IsAuthorized(participantId: string): Promise<{
|
|
366
|
+
status: string;
|
|
367
|
+
data?: ParticipantData;
|
|
368
|
+
message?: string;
|
|
369
|
+
}>;
|
|
369
370
|
openOAuthPopup(
|
|
370
371
|
authorizationUrl: string,
|
|
371
372
|
opts?: { width?: number; height?: number }
|
package/dist/index.js
CHANGED
|
@@ -49,27 +49,23 @@ __export(index_exports, {
|
|
|
49
49
|
RecurringFrequency: () => RecurringFrequency,
|
|
50
50
|
RootStore: () => RootStore,
|
|
51
51
|
SettingModel: () => Setting_default,
|
|
52
|
-
TOKEN_PATH: () => TOKEN_PATH,
|
|
53
52
|
TimeFrameModel: () => TimeFrame_default,
|
|
54
53
|
TimeSlotModel: () => TimeSlot_default,
|
|
55
54
|
Unit: () => Unit,
|
|
56
55
|
buildAuthHeaders: () => buildAuthHeaders,
|
|
57
56
|
clearAccessToken: () => clearAccessToken,
|
|
58
|
-
clearApiCredentials: () => clearApiCredentials,
|
|
59
57
|
clearAuth: () => clearAuth,
|
|
60
58
|
configure: () => configure,
|
|
61
59
|
createRootStore: () => createRootStore,
|
|
62
60
|
ensureValidAccessToken: () => ensureValidAccessToken,
|
|
63
|
-
fetchAccessToken: () => fetchAccessToken2,
|
|
64
61
|
getAuth: () => getAuth,
|
|
65
62
|
getConfig: () => getConfig,
|
|
66
63
|
getConfigStore: () => getConfigStore,
|
|
67
64
|
isAccessTokenExpired: () => isAccessTokenExpired,
|
|
68
|
-
requestAccessToken: () => requestAccessToken,
|
|
69
65
|
setAccessToken: () => setAccessToken,
|
|
70
|
-
setApiCredentials: () => setApiCredentials,
|
|
71
66
|
setBaseUrl: () => setBaseUrl,
|
|
72
|
-
setConsumer: () => setConsumer
|
|
67
|
+
setConsumer: () => setConsumer,
|
|
68
|
+
setGetAccessToken: () => setGetAccessToken
|
|
73
69
|
});
|
|
74
70
|
module.exports = __toCommonJS(index_exports);
|
|
75
71
|
|
|
@@ -85,8 +81,8 @@ var ConfigModel = import_mobx_state_tree.types.model("Config", {
|
|
|
85
81
|
fetch: void 0,
|
|
86
82
|
accessToken: void 0,
|
|
87
83
|
tokenExpiresAt: void 0,
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
/** Host app supplies a fresh JWT (e.g. from your IdP) when the stored token is missing/expired. */
|
|
85
|
+
getAccessToken: void 0,
|
|
90
86
|
getDefaultOffset: () => -(/* @__PURE__ */ new Date()).getTimezoneOffset()
|
|
91
87
|
})).actions((self) => ({
|
|
92
88
|
setBaseUrl(url) {
|
|
@@ -101,6 +97,9 @@ var ConfigModel = import_mobx_state_tree.types.model("Config", {
|
|
|
101
97
|
setGetDefaultOffset(fn) {
|
|
102
98
|
self.getDefaultOffset = fn;
|
|
103
99
|
},
|
|
100
|
+
setGetAccessToken(fn) {
|
|
101
|
+
self.getAccessToken = fn;
|
|
102
|
+
},
|
|
104
103
|
setAccessToken(token, expiresAtUtc = void 0) {
|
|
105
104
|
self.accessToken = token ? String(token) : void 0;
|
|
106
105
|
self.tokenExpiresAt = expiresAtUtc != null && expiresAtUtc !== "" ? String(expiresAtUtc) : void 0;
|
|
@@ -109,40 +108,24 @@ var ConfigModel = import_mobx_state_tree.types.model("Config", {
|
|
|
109
108
|
self.accessToken = void 0;
|
|
110
109
|
self.tokenExpiresAt = void 0;
|
|
111
110
|
},
|
|
112
|
-
setApiCredentials(apiKey, apiSecret) {
|
|
113
|
-
self.apiKey = apiKey != null ? String(apiKey) : void 0;
|
|
114
|
-
self.apiSecret = apiSecret != null ? String(apiSecret) : void 0;
|
|
115
|
-
},
|
|
116
|
-
clearApiCredentials() {
|
|
117
|
-
self.apiKey = void 0;
|
|
118
|
-
self.apiSecret = void 0;
|
|
119
|
-
},
|
|
120
111
|
clearAuth() {
|
|
121
112
|
self.clearAccessToken();
|
|
122
|
-
self.
|
|
113
|
+
self.getAccessToken = void 0;
|
|
123
114
|
},
|
|
124
115
|
configure(env) {
|
|
125
116
|
if (env.baseUrl != null) self.baseUrl = env.baseUrl;
|
|
126
117
|
if (env.consumer != null) self.consumer = env.consumer;
|
|
127
118
|
if (env.fetch != null) self.fetch = env.fetch;
|
|
128
119
|
if (env.getDefaultOffset != null) self.getDefaultOffset = env.getDefaultOffset;
|
|
120
|
+
if (env.getAccessToken != null) self.getAccessToken = env.getAccessToken;
|
|
129
121
|
if (env.accessToken != null) {
|
|
130
122
|
self.setAccessToken(
|
|
131
123
|
env.accessToken,
|
|
132
124
|
env.expiresAtUtc ?? env.tokenExpiresAt ?? env.expires_at_utc
|
|
133
125
|
);
|
|
134
126
|
}
|
|
135
|
-
if (env.apiKey != null || env.api_key != null) {
|
|
136
|
-
self.apiKey = String(env.apiKey ?? env.api_key);
|
|
137
|
-
}
|
|
138
|
-
if (env.apiSecret != null || env.api_secret != null) {
|
|
139
|
-
self.apiSecret = String(env.apiSecret ?? env.api_secret);
|
|
140
|
-
}
|
|
141
127
|
}
|
|
142
128
|
})).views((self) => ({
|
|
143
|
-
get hasApiCredentials() {
|
|
144
|
-
return Boolean(self.apiKey && self.apiSecret);
|
|
145
|
-
},
|
|
146
129
|
getEnv() {
|
|
147
130
|
return {
|
|
148
131
|
baseUrl: self.baseUrl || void 0,
|
|
@@ -150,8 +133,7 @@ var ConfigModel = import_mobx_state_tree.types.model("Config", {
|
|
|
150
133
|
fetch: self.fetch,
|
|
151
134
|
getDefaultOffset: self.getDefaultOffset,
|
|
152
135
|
accessToken: self.accessToken,
|
|
153
|
-
tokenExpiresAt: self.tokenExpiresAt
|
|
154
|
-
hasApiCredentials: self.hasApiCredentials
|
|
136
|
+
tokenExpiresAt: self.tokenExpiresAt
|
|
155
137
|
};
|
|
156
138
|
}
|
|
157
139
|
}));
|
|
@@ -163,26 +145,7 @@ function getConfigStore() {
|
|
|
163
145
|
var ConfigModel_default = ConfigModel;
|
|
164
146
|
|
|
165
147
|
// src/apiAuth.js
|
|
166
|
-
var TOKEN_PATH = "/Api/Auth/Token";
|
|
167
148
|
var DEFAULT_TOKEN_REFRESH_SKEW_MS = 6e4;
|
|
168
|
-
function defaultFetch() {
|
|
169
|
-
if (typeof fetch === "undefined") {
|
|
170
|
-
throw new Error("fetch not available");
|
|
171
|
-
}
|
|
172
|
-
return fetch;
|
|
173
|
-
}
|
|
174
|
-
function pickTokenPayload(data) {
|
|
175
|
-
if (!data || typeof data !== "object") return null;
|
|
176
|
-
const nested = data.data && typeof data.data === "object" ? data.data : data;
|
|
177
|
-
const accessToken = nested.access_token ?? nested.accessToken ?? nested.AccessToken ?? null;
|
|
178
|
-
if (!accessToken) return null;
|
|
179
|
-
const expiresAtUtc = nested.expires_at_utc ?? nested.expiresAtUtc ?? nested.ExpiresAtUtc ?? null;
|
|
180
|
-
return {
|
|
181
|
-
accessToken: String(accessToken),
|
|
182
|
-
expiresAtUtc: expiresAtUtc != null ? String(expiresAtUtc) : null,
|
|
183
|
-
tokenType: nested.token_type ?? nested.tokenType ?? "Bearer"
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
149
|
function isAccessTokenExpired(expiresAtUtc, skewMs = DEFAULT_TOKEN_REFRESH_SKEW_MS) {
|
|
187
150
|
if (expiresAtUtc == null || expiresAtUtc === "") return false;
|
|
188
151
|
const expMs = new Date(expiresAtUtc).getTime();
|
|
@@ -193,77 +156,22 @@ function getAuthState() {
|
|
|
193
156
|
const store = getConfigStore();
|
|
194
157
|
return {
|
|
195
158
|
accessToken: store.accessToken ?? void 0,
|
|
196
|
-
tokenExpiresAt: store.tokenExpiresAt ?? void 0
|
|
197
|
-
apiKey: store.apiKey ?? void 0,
|
|
198
|
-
apiSecret: store.apiSecret ?? void 0,
|
|
199
|
-
hasApiCredentials: Boolean(store.apiKey && store.apiSecret)
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
async function requestAccessToken(apiKey, apiSecret, opts = {}) {
|
|
203
|
-
const store = getConfigStore();
|
|
204
|
-
const baseUrl = opts.baseUrl ?? store.baseUrl;
|
|
205
|
-
if (!baseUrl) {
|
|
206
|
-
return { status: "failure", message: "baseUrl required. Call configure({ baseUrl }) first." };
|
|
207
|
-
}
|
|
208
|
-
const key = apiKey != null ? String(apiKey).trim() : "";
|
|
209
|
-
const secret = apiSecret != null ? String(apiSecret).trim() : "";
|
|
210
|
-
if (!key || !secret) {
|
|
211
|
-
return { status: "failure", message: "api_key and api_secret are required" };
|
|
212
|
-
}
|
|
213
|
-
const fetchFn = opts.fetch ?? store.fetch ?? defaultFetch();
|
|
214
|
-
const url = `${String(baseUrl).replace(/\/+$/, "")}${TOKEN_PATH}`;
|
|
215
|
-
const httpRes = await fetchFn(url, {
|
|
216
|
-
method: "POST",
|
|
217
|
-
headers: { "Content-Type": "application/json" },
|
|
218
|
-
body: JSON.stringify({ api_key: key, api_secret: secret })
|
|
219
|
-
});
|
|
220
|
-
const text = await httpRes.text();
|
|
221
|
-
let body;
|
|
222
|
-
try {
|
|
223
|
-
body = JSON.parse(text);
|
|
224
|
-
} catch {
|
|
225
|
-
body = { status: "failure", message: text || httpRes.statusText };
|
|
226
|
-
}
|
|
227
|
-
if (!httpRes.ok && body.status !== "failure") {
|
|
228
|
-
body.status = "failure";
|
|
229
|
-
body.message = body.message ?? `HTTP ${httpRes.status}`;
|
|
230
|
-
}
|
|
231
|
-
if (body.status !== "success") {
|
|
232
|
-
return {
|
|
233
|
-
status: "failure",
|
|
234
|
-
message: body.message ?? "Failed to obtain access token"
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
const parsed = pickTokenPayload(body);
|
|
238
|
-
if (!parsed) {
|
|
239
|
-
return { status: "failure", message: "Token response missing access_token" };
|
|
240
|
-
}
|
|
241
|
-
return {
|
|
242
|
-
status: "success",
|
|
243
|
-
accessToken: parsed.accessToken,
|
|
244
|
-
expiresAtUtc: parsed.expiresAtUtc,
|
|
245
|
-
tokenType: parsed.tokenType
|
|
159
|
+
tokenExpiresAt: store.tokenExpiresAt ?? void 0
|
|
246
160
|
};
|
|
247
161
|
}
|
|
248
|
-
async function fetchAccessToken(apiKey, apiSecret) {
|
|
249
|
-
const store = getConfigStore();
|
|
250
|
-
const key = apiKey ?? store.apiKey;
|
|
251
|
-
const secret = apiSecret ?? store.apiSecret;
|
|
252
|
-
const result = await requestAccessToken(key, secret);
|
|
253
|
-
if (result.status === "success") {
|
|
254
|
-
store.setAccessToken(result.accessToken, result.expiresAtUtc);
|
|
255
|
-
}
|
|
256
|
-
return result;
|
|
257
|
-
}
|
|
258
162
|
async function ensureAccessToken() {
|
|
259
163
|
const store = getConfigStore();
|
|
260
|
-
const { accessToken, tokenExpiresAt
|
|
164
|
+
const { accessToken, tokenExpiresAt } = getAuthState();
|
|
261
165
|
if (accessToken && !isAccessTokenExpired(tokenExpiresAt)) {
|
|
262
166
|
return accessToken;
|
|
263
167
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
168
|
+
const refresh = store.getAccessToken;
|
|
169
|
+
if (typeof refresh === "function") {
|
|
170
|
+
const next = await refresh();
|
|
171
|
+
if (next) {
|
|
172
|
+
store.setAccessToken(next);
|
|
173
|
+
return next;
|
|
174
|
+
}
|
|
267
175
|
}
|
|
268
176
|
return accessToken;
|
|
269
177
|
}
|
|
@@ -300,11 +208,8 @@ function setAccessToken(accessToken, expiresAtUtc) {
|
|
|
300
208
|
function clearAccessToken() {
|
|
301
209
|
getConfigStore().clearAccessToken();
|
|
302
210
|
}
|
|
303
|
-
function
|
|
304
|
-
getConfigStore().
|
|
305
|
-
}
|
|
306
|
-
function clearApiCredentials() {
|
|
307
|
-
getConfigStore().clearApiCredentials();
|
|
211
|
+
function setGetAccessToken(fn) {
|
|
212
|
+
getConfigStore().setGetAccessToken(fn);
|
|
308
213
|
}
|
|
309
214
|
function clearAuth() {
|
|
310
215
|
getConfigStore().clearAuth();
|
|
@@ -312,9 +217,6 @@ function clearAuth() {
|
|
|
312
217
|
function getAuth() {
|
|
313
218
|
return getAuthState();
|
|
314
219
|
}
|
|
315
|
-
function fetchAccessToken2(apiKey, apiSecret) {
|
|
316
|
-
return fetchAccessToken(apiKey, apiSecret);
|
|
317
|
-
}
|
|
318
220
|
function ensureValidAccessToken() {
|
|
319
221
|
return ensureAccessToken();
|
|
320
222
|
}
|
|
@@ -1587,23 +1489,27 @@ var ParticipantModel = import_mobx_state_tree11.types.model("Participant", {
|
|
|
1587
1489
|
}
|
|
1588
1490
|
};
|
|
1589
1491
|
});
|
|
1590
|
-
function
|
|
1492
|
+
function mapParticipantFromApi(d) {
|
|
1591
1493
|
if (!d) return d;
|
|
1592
1494
|
const pick2 = (...keys) => keys.reduce((v, k) => v ?? d[k], void 0);
|
|
1593
1495
|
const n = (v) => v != null && v !== "" ? Number(v) : void 0;
|
|
1594
1496
|
return {
|
|
1497
|
+
id: n(pick2("id", "Id")) ?? null,
|
|
1595
1498
|
participantId: String(pick2("participantId", "ParticipantId", "participant_id") ?? ""),
|
|
1596
1499
|
companyKey: pick2("companyKey", "CompanyKey", "company_key") ?? null,
|
|
1597
1500
|
alias: pick2("alias", "Alias") ?? "",
|
|
1598
1501
|
email: pick2("email", "Email") ?? "",
|
|
1599
1502
|
isApproved: Boolean(pick2("isApproved", "IsApproved", "is_approved")),
|
|
1600
1503
|
isAvailable: Boolean(pick2("isAvailable", "IsAvailable", "is_available")),
|
|
1601
|
-
provider: n(pick2("provider", "Provider")) ?? 0,
|
|
1504
|
+
provider: n(pick2("provider", "Provider", "emailProvider", "EmailProvider", "email_provider")) ?? 0,
|
|
1602
1505
|
createdOn: pick2("createdOn", "CreatedOn", "created_on") ?? null,
|
|
1603
1506
|
modifiedOn: pick2("modifiedOn", "ModifiedOn", "modified_on") ?? null,
|
|
1604
1507
|
isDeleted: Boolean(pick2("isDeleted", "IsDeleted", "is_deleted"))
|
|
1605
1508
|
};
|
|
1606
1509
|
}
|
|
1510
|
+
function mapFromApi2(d) {
|
|
1511
|
+
return mapParticipantFromApi(d);
|
|
1512
|
+
}
|
|
1607
1513
|
function toPayload(self) {
|
|
1608
1514
|
return {
|
|
1609
1515
|
participantId: self.participantId,
|
|
@@ -2944,6 +2850,22 @@ var AuthModel = {
|
|
|
2944
2850
|
}
|
|
2945
2851
|
return res;
|
|
2946
2852
|
},
|
|
2853
|
+
/**
|
|
2854
|
+
* GET Auth/IsAuthorized — validates OAuth with the provider; clears isApproved when not authorized.
|
|
2855
|
+
* Returns the updated participant record (same shape as participant/get).
|
|
2856
|
+
* @param {string} participantId
|
|
2857
|
+
* @returns {Promise<{ status: string, data?: object, message?: string }>}
|
|
2858
|
+
*/
|
|
2859
|
+
async IsAuthorized(participantId) {
|
|
2860
|
+
const id = participantId != null ? String(participantId).trim() : "";
|
|
2861
|
+
if (!id) return { status: "failure", message: "participantId required" };
|
|
2862
|
+
const { reqGet } = createRequestHelpersFromEnv(getConfig());
|
|
2863
|
+
const res = await reqGet("/Auth/IsAuthorized", { participant_id: id });
|
|
2864
|
+
if (res.status === "success" && res.data) {
|
|
2865
|
+
res.data = mapParticipantFromApi(res.data);
|
|
2866
|
+
}
|
|
2867
|
+
return res;
|
|
2868
|
+
},
|
|
2947
2869
|
/**
|
|
2948
2870
|
* Open Google/Microsoft OAuth in a centered popup.
|
|
2949
2871
|
* @param {string} authorizationUrl — from getAuthorizationUrl().data.authorizationUrl
|
|
@@ -2964,6 +2886,8 @@ var AuthModel = {
|
|
|
2964
2886
|
},
|
|
2965
2887
|
/**
|
|
2966
2888
|
* Subscribe to calendar-authorization postMessage from OAuth popup (Authorized.html / Error.html).
|
|
2889
|
+
* Do not use popup.closed to detect completion — Cross-Origin-Opener-Policy on the parent app
|
|
2890
|
+
* blocks that check after the popup navigates to Google/Microsoft.
|
|
2967
2891
|
* @param {(payload: { type: string, status: 'success' | 'error', message?: string }) => void} handler
|
|
2968
2892
|
* @returns {() => void} unsubscribe
|
|
2969
2893
|
*/
|
|
@@ -3035,25 +2959,21 @@ function createRootStore(initialState = {}) {
|
|
|
3035
2959
|
RecurringFrequency,
|
|
3036
2960
|
RootStore,
|
|
3037
2961
|
SettingModel,
|
|
3038
|
-
TOKEN_PATH,
|
|
3039
2962
|
TimeFrameModel,
|
|
3040
2963
|
TimeSlotModel,
|
|
3041
2964
|
Unit,
|
|
3042
2965
|
buildAuthHeaders,
|
|
3043
2966
|
clearAccessToken,
|
|
3044
|
-
clearApiCredentials,
|
|
3045
2967
|
clearAuth,
|
|
3046
2968
|
configure,
|
|
3047
2969
|
createRootStore,
|
|
3048
2970
|
ensureValidAccessToken,
|
|
3049
|
-
fetchAccessToken,
|
|
3050
2971
|
getAuth,
|
|
3051
2972
|
getConfig,
|
|
3052
2973
|
getConfigStore,
|
|
3053
2974
|
isAccessTokenExpired,
|
|
3054
|
-
requestAccessToken,
|
|
3055
2975
|
setAccessToken,
|
|
3056
|
-
setApiCredentials,
|
|
3057
2976
|
setBaseUrl,
|
|
3058
|
-
setConsumer
|
|
2977
|
+
setConsumer,
|
|
2978
|
+
setGetAccessToken
|
|
3059
2979
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -10,8 +10,8 @@ var ConfigModel = types.model("Config", {
|
|
|
10
10
|
fetch: void 0,
|
|
11
11
|
accessToken: void 0,
|
|
12
12
|
tokenExpiresAt: void 0,
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
/** Host app supplies a fresh JWT (e.g. from your IdP) when the stored token is missing/expired. */
|
|
14
|
+
getAccessToken: void 0,
|
|
15
15
|
getDefaultOffset: () => -(/* @__PURE__ */ new Date()).getTimezoneOffset()
|
|
16
16
|
})).actions((self) => ({
|
|
17
17
|
setBaseUrl(url) {
|
|
@@ -26,6 +26,9 @@ var ConfigModel = types.model("Config", {
|
|
|
26
26
|
setGetDefaultOffset(fn) {
|
|
27
27
|
self.getDefaultOffset = fn;
|
|
28
28
|
},
|
|
29
|
+
setGetAccessToken(fn) {
|
|
30
|
+
self.getAccessToken = fn;
|
|
31
|
+
},
|
|
29
32
|
setAccessToken(token, expiresAtUtc = void 0) {
|
|
30
33
|
self.accessToken = token ? String(token) : void 0;
|
|
31
34
|
self.tokenExpiresAt = expiresAtUtc != null && expiresAtUtc !== "" ? String(expiresAtUtc) : void 0;
|
|
@@ -34,40 +37,24 @@ var ConfigModel = types.model("Config", {
|
|
|
34
37
|
self.accessToken = void 0;
|
|
35
38
|
self.tokenExpiresAt = void 0;
|
|
36
39
|
},
|
|
37
|
-
setApiCredentials(apiKey, apiSecret) {
|
|
38
|
-
self.apiKey = apiKey != null ? String(apiKey) : void 0;
|
|
39
|
-
self.apiSecret = apiSecret != null ? String(apiSecret) : void 0;
|
|
40
|
-
},
|
|
41
|
-
clearApiCredentials() {
|
|
42
|
-
self.apiKey = void 0;
|
|
43
|
-
self.apiSecret = void 0;
|
|
44
|
-
},
|
|
45
40
|
clearAuth() {
|
|
46
41
|
self.clearAccessToken();
|
|
47
|
-
self.
|
|
42
|
+
self.getAccessToken = void 0;
|
|
48
43
|
},
|
|
49
44
|
configure(env) {
|
|
50
45
|
if (env.baseUrl != null) self.baseUrl = env.baseUrl;
|
|
51
46
|
if (env.consumer != null) self.consumer = env.consumer;
|
|
52
47
|
if (env.fetch != null) self.fetch = env.fetch;
|
|
53
48
|
if (env.getDefaultOffset != null) self.getDefaultOffset = env.getDefaultOffset;
|
|
49
|
+
if (env.getAccessToken != null) self.getAccessToken = env.getAccessToken;
|
|
54
50
|
if (env.accessToken != null) {
|
|
55
51
|
self.setAccessToken(
|
|
56
52
|
env.accessToken,
|
|
57
53
|
env.expiresAtUtc ?? env.tokenExpiresAt ?? env.expires_at_utc
|
|
58
54
|
);
|
|
59
55
|
}
|
|
60
|
-
if (env.apiKey != null || env.api_key != null) {
|
|
61
|
-
self.apiKey = String(env.apiKey ?? env.api_key);
|
|
62
|
-
}
|
|
63
|
-
if (env.apiSecret != null || env.api_secret != null) {
|
|
64
|
-
self.apiSecret = String(env.apiSecret ?? env.api_secret);
|
|
65
|
-
}
|
|
66
56
|
}
|
|
67
57
|
})).views((self) => ({
|
|
68
|
-
get hasApiCredentials() {
|
|
69
|
-
return Boolean(self.apiKey && self.apiSecret);
|
|
70
|
-
},
|
|
71
58
|
getEnv() {
|
|
72
59
|
return {
|
|
73
60
|
baseUrl: self.baseUrl || void 0,
|
|
@@ -75,8 +62,7 @@ var ConfigModel = types.model("Config", {
|
|
|
75
62
|
fetch: self.fetch,
|
|
76
63
|
getDefaultOffset: self.getDefaultOffset,
|
|
77
64
|
accessToken: self.accessToken,
|
|
78
|
-
tokenExpiresAt: self.tokenExpiresAt
|
|
79
|
-
hasApiCredentials: self.hasApiCredentials
|
|
65
|
+
tokenExpiresAt: self.tokenExpiresAt
|
|
80
66
|
};
|
|
81
67
|
}
|
|
82
68
|
}));
|
|
@@ -88,26 +74,7 @@ function getConfigStore() {
|
|
|
88
74
|
var ConfigModel_default = ConfigModel;
|
|
89
75
|
|
|
90
76
|
// src/apiAuth.js
|
|
91
|
-
var TOKEN_PATH = "/Api/Auth/Token";
|
|
92
77
|
var DEFAULT_TOKEN_REFRESH_SKEW_MS = 6e4;
|
|
93
|
-
function defaultFetch() {
|
|
94
|
-
if (typeof fetch === "undefined") {
|
|
95
|
-
throw new Error("fetch not available");
|
|
96
|
-
}
|
|
97
|
-
return fetch;
|
|
98
|
-
}
|
|
99
|
-
function pickTokenPayload(data) {
|
|
100
|
-
if (!data || typeof data !== "object") return null;
|
|
101
|
-
const nested = data.data && typeof data.data === "object" ? data.data : data;
|
|
102
|
-
const accessToken = nested.access_token ?? nested.accessToken ?? nested.AccessToken ?? null;
|
|
103
|
-
if (!accessToken) return null;
|
|
104
|
-
const expiresAtUtc = nested.expires_at_utc ?? nested.expiresAtUtc ?? nested.ExpiresAtUtc ?? null;
|
|
105
|
-
return {
|
|
106
|
-
accessToken: String(accessToken),
|
|
107
|
-
expiresAtUtc: expiresAtUtc != null ? String(expiresAtUtc) : null,
|
|
108
|
-
tokenType: nested.token_type ?? nested.tokenType ?? "Bearer"
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
78
|
function isAccessTokenExpired(expiresAtUtc, skewMs = DEFAULT_TOKEN_REFRESH_SKEW_MS) {
|
|
112
79
|
if (expiresAtUtc == null || expiresAtUtc === "") return false;
|
|
113
80
|
const expMs = new Date(expiresAtUtc).getTime();
|
|
@@ -118,77 +85,22 @@ function getAuthState() {
|
|
|
118
85
|
const store = getConfigStore();
|
|
119
86
|
return {
|
|
120
87
|
accessToken: store.accessToken ?? void 0,
|
|
121
|
-
tokenExpiresAt: store.tokenExpiresAt ?? void 0
|
|
122
|
-
apiKey: store.apiKey ?? void 0,
|
|
123
|
-
apiSecret: store.apiSecret ?? void 0,
|
|
124
|
-
hasApiCredentials: Boolean(store.apiKey && store.apiSecret)
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
async function requestAccessToken(apiKey, apiSecret, opts = {}) {
|
|
128
|
-
const store = getConfigStore();
|
|
129
|
-
const baseUrl = opts.baseUrl ?? store.baseUrl;
|
|
130
|
-
if (!baseUrl) {
|
|
131
|
-
return { status: "failure", message: "baseUrl required. Call configure({ baseUrl }) first." };
|
|
132
|
-
}
|
|
133
|
-
const key = apiKey != null ? String(apiKey).trim() : "";
|
|
134
|
-
const secret = apiSecret != null ? String(apiSecret).trim() : "";
|
|
135
|
-
if (!key || !secret) {
|
|
136
|
-
return { status: "failure", message: "api_key and api_secret are required" };
|
|
137
|
-
}
|
|
138
|
-
const fetchFn = opts.fetch ?? store.fetch ?? defaultFetch();
|
|
139
|
-
const url = `${String(baseUrl).replace(/\/+$/, "")}${TOKEN_PATH}`;
|
|
140
|
-
const httpRes = await fetchFn(url, {
|
|
141
|
-
method: "POST",
|
|
142
|
-
headers: { "Content-Type": "application/json" },
|
|
143
|
-
body: JSON.stringify({ api_key: key, api_secret: secret })
|
|
144
|
-
});
|
|
145
|
-
const text = await httpRes.text();
|
|
146
|
-
let body;
|
|
147
|
-
try {
|
|
148
|
-
body = JSON.parse(text);
|
|
149
|
-
} catch {
|
|
150
|
-
body = { status: "failure", message: text || httpRes.statusText };
|
|
151
|
-
}
|
|
152
|
-
if (!httpRes.ok && body.status !== "failure") {
|
|
153
|
-
body.status = "failure";
|
|
154
|
-
body.message = body.message ?? `HTTP ${httpRes.status}`;
|
|
155
|
-
}
|
|
156
|
-
if (body.status !== "success") {
|
|
157
|
-
return {
|
|
158
|
-
status: "failure",
|
|
159
|
-
message: body.message ?? "Failed to obtain access token"
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
const parsed = pickTokenPayload(body);
|
|
163
|
-
if (!parsed) {
|
|
164
|
-
return { status: "failure", message: "Token response missing access_token" };
|
|
165
|
-
}
|
|
166
|
-
return {
|
|
167
|
-
status: "success",
|
|
168
|
-
accessToken: parsed.accessToken,
|
|
169
|
-
expiresAtUtc: parsed.expiresAtUtc,
|
|
170
|
-
tokenType: parsed.tokenType
|
|
88
|
+
tokenExpiresAt: store.tokenExpiresAt ?? void 0
|
|
171
89
|
};
|
|
172
90
|
}
|
|
173
|
-
async function fetchAccessToken(apiKey, apiSecret) {
|
|
174
|
-
const store = getConfigStore();
|
|
175
|
-
const key = apiKey ?? store.apiKey;
|
|
176
|
-
const secret = apiSecret ?? store.apiSecret;
|
|
177
|
-
const result = await requestAccessToken(key, secret);
|
|
178
|
-
if (result.status === "success") {
|
|
179
|
-
store.setAccessToken(result.accessToken, result.expiresAtUtc);
|
|
180
|
-
}
|
|
181
|
-
return result;
|
|
182
|
-
}
|
|
183
91
|
async function ensureAccessToken() {
|
|
184
92
|
const store = getConfigStore();
|
|
185
|
-
const { accessToken, tokenExpiresAt
|
|
93
|
+
const { accessToken, tokenExpiresAt } = getAuthState();
|
|
186
94
|
if (accessToken && !isAccessTokenExpired(tokenExpiresAt)) {
|
|
187
95
|
return accessToken;
|
|
188
96
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
97
|
+
const refresh = store.getAccessToken;
|
|
98
|
+
if (typeof refresh === "function") {
|
|
99
|
+
const next = await refresh();
|
|
100
|
+
if (next) {
|
|
101
|
+
store.setAccessToken(next);
|
|
102
|
+
return next;
|
|
103
|
+
}
|
|
192
104
|
}
|
|
193
105
|
return accessToken;
|
|
194
106
|
}
|
|
@@ -225,11 +137,8 @@ function setAccessToken(accessToken, expiresAtUtc) {
|
|
|
225
137
|
function clearAccessToken() {
|
|
226
138
|
getConfigStore().clearAccessToken();
|
|
227
139
|
}
|
|
228
|
-
function
|
|
229
|
-
getConfigStore().
|
|
230
|
-
}
|
|
231
|
-
function clearApiCredentials() {
|
|
232
|
-
getConfigStore().clearApiCredentials();
|
|
140
|
+
function setGetAccessToken(fn) {
|
|
141
|
+
getConfigStore().setGetAccessToken(fn);
|
|
233
142
|
}
|
|
234
143
|
function clearAuth() {
|
|
235
144
|
getConfigStore().clearAuth();
|
|
@@ -237,9 +146,6 @@ function clearAuth() {
|
|
|
237
146
|
function getAuth() {
|
|
238
147
|
return getAuthState();
|
|
239
148
|
}
|
|
240
|
-
function fetchAccessToken2(apiKey, apiSecret) {
|
|
241
|
-
return fetchAccessToken(apiKey, apiSecret);
|
|
242
|
-
}
|
|
243
149
|
function ensureValidAccessToken() {
|
|
244
150
|
return ensureAccessToken();
|
|
245
151
|
}
|
|
@@ -1512,23 +1418,27 @@ var ParticipantModel = types11.model("Participant", {
|
|
|
1512
1418
|
}
|
|
1513
1419
|
};
|
|
1514
1420
|
});
|
|
1515
|
-
function
|
|
1421
|
+
function mapParticipantFromApi(d) {
|
|
1516
1422
|
if (!d) return d;
|
|
1517
1423
|
const pick2 = (...keys) => keys.reduce((v, k) => v ?? d[k], void 0);
|
|
1518
1424
|
const n = (v) => v != null && v !== "" ? Number(v) : void 0;
|
|
1519
1425
|
return {
|
|
1426
|
+
id: n(pick2("id", "Id")) ?? null,
|
|
1520
1427
|
participantId: String(pick2("participantId", "ParticipantId", "participant_id") ?? ""),
|
|
1521
1428
|
companyKey: pick2("companyKey", "CompanyKey", "company_key") ?? null,
|
|
1522
1429
|
alias: pick2("alias", "Alias") ?? "",
|
|
1523
1430
|
email: pick2("email", "Email") ?? "",
|
|
1524
1431
|
isApproved: Boolean(pick2("isApproved", "IsApproved", "is_approved")),
|
|
1525
1432
|
isAvailable: Boolean(pick2("isAvailable", "IsAvailable", "is_available")),
|
|
1526
|
-
provider: n(pick2("provider", "Provider")) ?? 0,
|
|
1433
|
+
provider: n(pick2("provider", "Provider", "emailProvider", "EmailProvider", "email_provider")) ?? 0,
|
|
1527
1434
|
createdOn: pick2("createdOn", "CreatedOn", "created_on") ?? null,
|
|
1528
1435
|
modifiedOn: pick2("modifiedOn", "ModifiedOn", "modified_on") ?? null,
|
|
1529
1436
|
isDeleted: Boolean(pick2("isDeleted", "IsDeleted", "is_deleted"))
|
|
1530
1437
|
};
|
|
1531
1438
|
}
|
|
1439
|
+
function mapFromApi2(d) {
|
|
1440
|
+
return mapParticipantFromApi(d);
|
|
1441
|
+
}
|
|
1532
1442
|
function toPayload(self) {
|
|
1533
1443
|
return {
|
|
1534
1444
|
participantId: self.participantId,
|
|
@@ -2869,6 +2779,22 @@ var AuthModel = {
|
|
|
2869
2779
|
}
|
|
2870
2780
|
return res;
|
|
2871
2781
|
},
|
|
2782
|
+
/**
|
|
2783
|
+
* GET Auth/IsAuthorized — validates OAuth with the provider; clears isApproved when not authorized.
|
|
2784
|
+
* Returns the updated participant record (same shape as participant/get).
|
|
2785
|
+
* @param {string} participantId
|
|
2786
|
+
* @returns {Promise<{ status: string, data?: object, message?: string }>}
|
|
2787
|
+
*/
|
|
2788
|
+
async IsAuthorized(participantId) {
|
|
2789
|
+
const id = participantId != null ? String(participantId).trim() : "";
|
|
2790
|
+
if (!id) return { status: "failure", message: "participantId required" };
|
|
2791
|
+
const { reqGet } = createRequestHelpersFromEnv(getConfig());
|
|
2792
|
+
const res = await reqGet("/Auth/IsAuthorized", { participant_id: id });
|
|
2793
|
+
if (res.status === "success" && res.data) {
|
|
2794
|
+
res.data = mapParticipantFromApi(res.data);
|
|
2795
|
+
}
|
|
2796
|
+
return res;
|
|
2797
|
+
},
|
|
2872
2798
|
/**
|
|
2873
2799
|
* Open Google/Microsoft OAuth in a centered popup.
|
|
2874
2800
|
* @param {string} authorizationUrl — from getAuthorizationUrl().data.authorizationUrl
|
|
@@ -2889,6 +2815,8 @@ var AuthModel = {
|
|
|
2889
2815
|
},
|
|
2890
2816
|
/**
|
|
2891
2817
|
* Subscribe to calendar-authorization postMessage from OAuth popup (Authorized.html / Error.html).
|
|
2818
|
+
* Do not use popup.closed to detect completion — Cross-Origin-Opener-Policy on the parent app
|
|
2819
|
+
* blocks that check after the popup navigates to Google/Microsoft.
|
|
2892
2820
|
* @param {(payload: { type: string, status: 'success' | 'error', message?: string }) => void} handler
|
|
2893
2821
|
* @returns {() => void} unsubscribe
|
|
2894
2822
|
*/
|
|
@@ -2959,25 +2887,21 @@ export {
|
|
|
2959
2887
|
RecurringFrequency,
|
|
2960
2888
|
RootStore,
|
|
2961
2889
|
Setting_default as SettingModel,
|
|
2962
|
-
TOKEN_PATH,
|
|
2963
2890
|
TimeFrame_default as TimeFrameModel,
|
|
2964
2891
|
TimeSlot_default as TimeSlotModel,
|
|
2965
2892
|
Unit,
|
|
2966
2893
|
buildAuthHeaders,
|
|
2967
2894
|
clearAccessToken,
|
|
2968
|
-
clearApiCredentials,
|
|
2969
2895
|
clearAuth,
|
|
2970
2896
|
configure,
|
|
2971
2897
|
createRootStore,
|
|
2972
2898
|
ensureValidAccessToken,
|
|
2973
|
-
fetchAccessToken2 as fetchAccessToken,
|
|
2974
2899
|
getAuth,
|
|
2975
2900
|
getConfig,
|
|
2976
2901
|
getConfigStore,
|
|
2977
2902
|
isAccessTokenExpired,
|
|
2978
|
-
requestAccessToken,
|
|
2979
2903
|
setAccessToken,
|
|
2980
|
-
setApiCredentials,
|
|
2981
2904
|
setBaseUrl,
|
|
2982
|
-
setConsumer
|
|
2905
|
+
setConsumer,
|
|
2906
|
+
setGetAccessToken
|
|
2983
2907
|
};
|