@finueva/auth 0.13.0 → 0.14.1

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/server.d.ts CHANGED
@@ -1,4 +1,22 @@
1
1
  import { A as PersonalWorkspaceSelection, C as OrganizationPage, D as OrganizationTeamMemberPage, E as OrganizationTeamDisplay, G as SocialIdTokenProvider, K as UpdateOrganizationDisplayInput, O as OrganizationTeamPage, Q as AuthErrorStatus, R as ReceivedOrganizationInvitation, S as OrganizationMemberPage, T as OrganizationTeam, V as ResentOrganizationInvitation, W as SocialIdTokenInput, X as AuthError, Y as WorkspaceSelection, Z as AuthErrorCode, _ as OrganizationDisplay, b as OrganizationMember, c as AuthUser, d as CreatedOrganizationMemberInvitation, f as CreatedOrganizationTeam, g as InvitationAcceptance, k as OrganizationWorkspaceSelection, l as CanceledOrganizationInvitation, m as DriveCredentialInput, o as AuthSession, p as DriveCredential, q as UpdateOrganizationTeamDisplayInput, r as AuthOrganization, t as AcceptInvitationInput, u as CreateOrganizationMemberInvitationInput, v as OrganizationInvitation, w as OrganizationPageInput, x as OrganizationMemberAuthority, y as OrganizationInvitationPage, z as ReceivedOrganizationInvitationPage } from "./types-Dsu3nEaN.js";
2
+ //#region src/provisioning.d.ts
3
+ interface DriveProvisioningAuthOptions {
4
+ authOrigin: string;
5
+ fetch?: typeof globalThis.fetch;
6
+ now?: () => number;
7
+ timeoutMs?: number;
8
+ }
9
+ interface DriveProvisioningCredential {
10
+ tokenType: "Bearer";
11
+ token: string;
12
+ expiresAt: Date;
13
+ }
14
+ interface FinuevaDriveProvisioningAuth {
15
+ getDriveProvisioningCredential(clientAssertion: string): Promise<DriveProvisioningCredential>;
16
+ }
17
+ /** Service authority only. Never pass an incoming User request or session to this client. */
18
+ declare function createDriveProvisioningAuth(options: DriveProvisioningAuthOptions): FinuevaDriveProvisioningAuth;
19
+ //#endregion
2
20
  //#region src/server.d.ts
3
21
  interface ServerAuthOptions {
4
22
  authOrigin: string;
@@ -42,4 +60,4 @@ interface FinuevaServerAuth {
42
60
  }
43
61
  declare function createServerAuth(options: ServerAuthOptions): FinuevaServerAuth;
44
62
  //#endregion
45
- export { type AcceptInvitationInput, AuthError, type AuthErrorCode, type AuthErrorStatus, type AuthOrganization, type AuthSession, type AuthUser, type CanceledOrganizationInvitation, type CreateOrganizationMemberInvitationInput, type CreatedOrganizationMemberInvitation, type CreatedOrganizationTeam, type DriveCredential, type DriveCredentialInput, FinuevaServerAuth, type InvitationAcceptance, type OrganizationDisplay, type OrganizationInvitation, type OrganizationInvitationPage, type OrganizationMember, type OrganizationMemberAuthority, type OrganizationMemberPage, type OrganizationPage, type OrganizationPageInput, type OrganizationTeam, type OrganizationTeamDisplay, type OrganizationTeamMemberPage, type OrganizationTeamPage, type OrganizationWorkspaceSelection, type PersonalWorkspaceSelection, type ReceivedOrganizationInvitation, type ReceivedOrganizationInvitationPage, type ResentOrganizationInvitation, ServerAuthOptions, ServerInvitationAcceptance, ServerSignOutResult, ServerSocialIdTokenSession, type SocialIdTokenInput, type SocialIdTokenProvider, type UpdateOrganizationDisplayInput, type UpdateOrganizationTeamDisplayInput, type WorkspaceSelection, createServerAuth };
63
+ export { type AcceptInvitationInput, AuthError, type AuthErrorCode, type AuthErrorStatus, type AuthOrganization, type AuthSession, type AuthUser, type CanceledOrganizationInvitation, type CreateOrganizationMemberInvitationInput, type CreatedOrganizationMemberInvitation, type CreatedOrganizationTeam, type DriveCredential, type DriveCredentialInput, type DriveProvisioningAuthOptions, type DriveProvisioningCredential, type FinuevaDriveProvisioningAuth, FinuevaServerAuth, type InvitationAcceptance, type OrganizationDisplay, type OrganizationInvitation, type OrganizationInvitationPage, type OrganizationMember, type OrganizationMemberAuthority, type OrganizationMemberPage, type OrganizationPage, type OrganizationPageInput, type OrganizationTeam, type OrganizationTeamDisplay, type OrganizationTeamMemberPage, type OrganizationTeamPage, type OrganizationWorkspaceSelection, type PersonalWorkspaceSelection, type ReceivedOrganizationInvitation, type ReceivedOrganizationInvitationPage, type ResentOrganizationInvitation, ServerAuthOptions, ServerInvitationAcceptance, ServerSignOutResult, ServerSocialIdTokenSession, type SocialIdTokenInput, type SocialIdTokenProvider, type UpdateOrganizationDisplayInput, type UpdateOrganizationTeamDisplayInput, type WorkspaceSelection, createDriveProvisioningAuth, createServerAuth };
package/dist/server.js CHANGED
@@ -211,269 +211,6 @@ var AuthError = class extends Error {
211
211
  }
212
212
  };
213
213
  //#endregion
214
- //#region ../../node_modules/.pnpm/destr@2.0.5/node_modules/destr/dist/index.mjs
215
- const suspectProtoRx = /"(?:_|\\u0{2}5[Ff]){2}(?:p|\\u0{2}70)(?:r|\\u0{2}72)(?:o|\\u0{2}6[Ff])(?:t|\\u0{2}74)(?:o|\\u0{2}6[Ff])(?:_|\\u0{2}5[Ff]){2}"\s*:/;
216
- const suspectConstructorRx = /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/;
217
- const JsonSigRx = /^\s*["[{]|^\s*-?\d{1,16}(\.\d{1,17})?([Ee][+-]?\d+)?\s*$/;
218
- function jsonParseTransform(key, value) {
219
- if (key === "__proto__" || key === "constructor" && value && typeof value === "object" && "prototype" in value) {
220
- warnKeyDropped(key);
221
- return;
222
- }
223
- return value;
224
- }
225
- function warnKeyDropped(key) {
226
- console.warn(`[destr] Dropping "${key}" key to prevent prototype pollution.`);
227
- }
228
- function destr(value, options = {}) {
229
- if (typeof value !== "string") return value;
230
- if (value[0] === "\"" && value[value.length - 1] === "\"" && value.indexOf("\\") === -1) return value.slice(1, -1);
231
- const _value = value.trim();
232
- if (_value.length <= 9) switch (_value.toLowerCase()) {
233
- case "true": return true;
234
- case "false": return false;
235
- case "undefined": return;
236
- case "null": return null;
237
- case "nan": return NaN;
238
- case "infinity": return Number.POSITIVE_INFINITY;
239
- case "-infinity": return Number.NEGATIVE_INFINITY;
240
- }
241
- if (!JsonSigRx.test(value)) {
242
- if (options.strict) throw new SyntaxError("[destr] Invalid JSON");
243
- return value;
244
- }
245
- try {
246
- if (suspectProtoRx.test(value) || suspectConstructorRx.test(value)) {
247
- if (options.strict) throw new Error("[destr] Possible prototype pollution");
248
- return JSON.parse(value, jsonParseTransform);
249
- }
250
- return JSON.parse(value);
251
- } catch (error) {
252
- if (options.strict) throw error;
253
- return value;
254
- }
255
- }
256
- //#endregion
257
- //#region ../../node_modules/.pnpm/ofetch@1.5.1/node_modules/ofetch/dist/shared/ofetch.CWycOUEr.mjs
258
- var FetchError = class extends Error {
259
- constructor(message, opts) {
260
- super(message, opts);
261
- this.name = "FetchError";
262
- if (opts?.cause && !this.cause) this.cause = opts.cause;
263
- }
264
- };
265
- function createFetchError(ctx) {
266
- const errorMessage = ctx.error?.message || ctx.error?.toString() || "";
267
- const method = ctx.request?.method || ctx.options?.method || "GET";
268
- const url = ctx.request?.url || String(ctx.request) || "/";
269
- const fetchError = new FetchError(`${`[${method}] ${JSON.stringify(url)}`}: ${ctx.response ? `${ctx.response.status} ${ctx.response.statusText}` : "<no response>"}${errorMessage ? ` ${errorMessage}` : ""}`, ctx.error ? { cause: ctx.error } : void 0);
270
- for (const key of [
271
- "request",
272
- "options",
273
- "response"
274
- ]) Object.defineProperty(fetchError, key, { get() {
275
- return ctx[key];
276
- } });
277
- for (const [key, refKey] of [
278
- ["data", "_data"],
279
- ["status", "status"],
280
- ["statusCode", "status"],
281
- ["statusText", "statusText"],
282
- ["statusMessage", "statusText"]
283
- ]) Object.defineProperty(fetchError, key, { get() {
284
- return ctx.response && ctx.response[refKey];
285
- } });
286
- return fetchError;
287
- }
288
- const payloadMethods = new Set(Object.freeze([
289
- "PATCH",
290
- "POST",
291
- "PUT",
292
- "DELETE"
293
- ]));
294
- function isPayloadMethod(method = "GET") {
295
- return payloadMethods.has(method.toUpperCase());
296
- }
297
- function isJSONSerializable(value) {
298
- if (value === void 0) return false;
299
- const t = typeof value;
300
- if (t === "string" || t === "number" || t === "boolean" || t === null) return true;
301
- if (t !== "object") return false;
302
- if (Array.isArray(value)) return true;
303
- if (value.buffer) return false;
304
- if (value instanceof FormData || value instanceof URLSearchParams) return false;
305
- return value.constructor && value.constructor.name === "Object" || typeof value.toJSON === "function";
306
- }
307
- const textTypes = new Set([
308
- "image/svg",
309
- "application/xml",
310
- "application/xhtml",
311
- "application/html"
312
- ]);
313
- const JSON_RE = /^application\/(?:[\w!#$%&*.^`~-]*\+)?json(;.+)?$/i;
314
- function detectResponseType(_contentType = "") {
315
- if (!_contentType) return "json";
316
- const contentType = _contentType.split(";").shift() || "";
317
- if (JSON_RE.test(contentType)) return "json";
318
- if (contentType === "text/event-stream") return "stream";
319
- if (textTypes.has(contentType) || contentType.startsWith("text/")) return "text";
320
- return "blob";
321
- }
322
- function resolveFetchOptions(request, input, defaults, Headers) {
323
- const headers = mergeHeaders(input?.headers ?? request?.headers, defaults?.headers, Headers);
324
- let query;
325
- if (defaults?.query || defaults?.params || input?.params || input?.query) query = {
326
- ...defaults?.params,
327
- ...defaults?.query,
328
- ...input?.params,
329
- ...input?.query
330
- };
331
- return {
332
- ...defaults,
333
- ...input,
334
- query,
335
- params: query,
336
- headers
337
- };
338
- }
339
- function mergeHeaders(input, defaults, Headers) {
340
- if (!defaults) return new Headers(input);
341
- const headers = new Headers(defaults);
342
- if (input) for (const [key, value] of Symbol.iterator in input || Array.isArray(input) ? input : new Headers(input)) headers.set(key, value);
343
- return headers;
344
- }
345
- async function callHooks(context, hooks) {
346
- if (hooks) if (Array.isArray(hooks)) for (const hook of hooks) await hook(context);
347
- else await hooks(context);
348
- }
349
- const retryStatusCodes = new Set([
350
- 408,
351
- 409,
352
- 425,
353
- 429,
354
- 500,
355
- 502,
356
- 503,
357
- 504
358
- ]);
359
- const nullBodyResponses = new Set([
360
- 101,
361
- 204,
362
- 205,
363
- 304
364
- ]);
365
- function createFetch(globalOptions = {}) {
366
- const { fetch = globalThis.fetch, Headers = globalThis.Headers, AbortController = globalThis.AbortController } = globalOptions;
367
- async function onError(context) {
368
- const isAbort = context.error && context.error.name === "AbortError" && !context.options.timeout || false;
369
- if (context.options.retry !== false && !isAbort) {
370
- let retries;
371
- if (typeof context.options.retry === "number") retries = context.options.retry;
372
- else retries = isPayloadMethod(context.options.method) ? 0 : 1;
373
- const responseCode = context.response && context.response.status || 500;
374
- if (retries > 0 && (Array.isArray(context.options.retryStatusCodes) ? context.options.retryStatusCodes.includes(responseCode) : retryStatusCodes.has(responseCode))) {
375
- const retryDelay = typeof context.options.retryDelay === "function" ? context.options.retryDelay(context) : context.options.retryDelay || 0;
376
- if (retryDelay > 0) await new Promise((resolve) => setTimeout(resolve, retryDelay));
377
- return $fetchRaw(context.request, {
378
- ...context.options,
379
- retry: retries - 1
380
- });
381
- }
382
- }
383
- const error = createFetchError(context);
384
- if (Error.captureStackTrace) Error.captureStackTrace(error, $fetchRaw);
385
- throw error;
386
- }
387
- const $fetchRaw = async function $fetchRaw2(_request, _options = {}) {
388
- const context = {
389
- request: _request,
390
- options: resolveFetchOptions(_request, _options, globalOptions.defaults, Headers),
391
- response: void 0,
392
- error: void 0
393
- };
394
- if (context.options.method) context.options.method = context.options.method.toUpperCase();
395
- if (context.options.onRequest) {
396
- await callHooks(context, context.options.onRequest);
397
- if (!(context.options.headers instanceof Headers)) context.options.headers = new Headers(context.options.headers || {});
398
- }
399
- if (typeof context.request === "string") {
400
- if (context.options.baseURL) context.request = withBase(context.request, context.options.baseURL);
401
- if (context.options.query) {
402
- context.request = withQuery(context.request, context.options.query);
403
- delete context.options.query;
404
- }
405
- if ("query" in context.options) delete context.options.query;
406
- if ("params" in context.options) delete context.options.params;
407
- }
408
- if (context.options.body && isPayloadMethod(context.options.method)) {
409
- if (isJSONSerializable(context.options.body)) {
410
- const contentType = context.options.headers.get("content-type");
411
- if (typeof context.options.body !== "string") context.options.body = contentType === "application/x-www-form-urlencoded" ? new URLSearchParams(context.options.body).toString() : JSON.stringify(context.options.body);
412
- if (!contentType) context.options.headers.set("content-type", "application/json");
413
- if (!context.options.headers.has("accept")) context.options.headers.set("accept", "application/json");
414
- } else if ("pipeTo" in context.options.body && typeof context.options.body.pipeTo === "function" || typeof context.options.body.pipe === "function") {
415
- if (!("duplex" in context.options)) context.options.duplex = "half";
416
- }
417
- }
418
- let abortTimeout;
419
- if (!context.options.signal && context.options.timeout) {
420
- const controller = new AbortController();
421
- abortTimeout = setTimeout(() => {
422
- const error = new Error("[TimeoutError]: The operation was aborted due to timeout");
423
- error.name = "TimeoutError";
424
- error.code = 23;
425
- controller.abort(error);
426
- }, context.options.timeout);
427
- context.options.signal = controller.signal;
428
- }
429
- try {
430
- context.response = await fetch(context.request, context.options);
431
- } catch (error) {
432
- context.error = error;
433
- if (context.options.onRequestError) await callHooks(context, context.options.onRequestError);
434
- return await onError(context);
435
- } finally {
436
- if (abortTimeout) clearTimeout(abortTimeout);
437
- }
438
- if ((context.response.body || context.response._bodyInit) && !nullBodyResponses.has(context.response.status) && context.options.method !== "HEAD") {
439
- const responseType = (context.options.parseResponse ? "json" : context.options.responseType) || detectResponseType(context.response.headers.get("content-type") || "");
440
- switch (responseType) {
441
- case "json": {
442
- const data = await context.response.text();
443
- const parseFunction = context.options.parseResponse || destr;
444
- context.response._data = parseFunction(data);
445
- break;
446
- }
447
- case "stream":
448
- context.response._data = context.response.body || context.response._bodyInit;
449
- break;
450
- default: context.response._data = await context.response[responseType]();
451
- }
452
- }
453
- if (context.options.onResponse) await callHooks(context, context.options.onResponse);
454
- if (!context.options.ignoreResponseError && context.response.status >= 400 && context.response.status < 600) {
455
- if (context.options.onResponseError) await callHooks(context, context.options.onResponseError);
456
- return await onError(context);
457
- }
458
- return context.response;
459
- };
460
- const $fetch = async function $fetch2(request, options) {
461
- return (await $fetchRaw(request, options))._data;
462
- };
463
- $fetch.raw = $fetchRaw;
464
- $fetch.native = (...args) => fetch(...args);
465
- $fetch.create = (defaultOptions = {}, customGlobalOptions = {}) => createFetch({
466
- ...globalOptions,
467
- ...customGlobalOptions,
468
- defaults: {
469
- ...globalOptions.defaults,
470
- ...customGlobalOptions.defaults,
471
- ...defaultOptions
472
- }
473
- });
474
- return $fetch;
475
- }
476
- //#endregion
477
214
  //#region src/shared.ts
478
215
  const DRIVE_CREDENTIAL_LIFETIME_SECONDS = 840;
479
216
  const id = /^[A-Za-z0-9_-]{1,128}$/;
@@ -1024,45 +761,412 @@ function isId(value) {
1024
761
  function exact(value, keys) {
1025
762
  if (!isObject(value) || Object.keys(value).sort().join(",") !== [...keys].sort().join(",")) throw authorityUnavailable();
1026
763
  }
1027
- function boundedString(value, maximum) {
1028
- return typeof value === "string" && value.length > 0 && new TextEncoder().encode(value).byteLength <= maximum;
764
+ function boundedString(value, maximum) {
765
+ return typeof value === "string" && value.length > 0 && new TextEncoder().encode(value).byteLength <= maximum;
766
+ }
767
+ function role(value) {
768
+ return value === "owner" || value === "admin" || value === "member";
769
+ }
770
+ function nullableHttpsUrl(value) {
771
+ if (value === null) return null;
772
+ if (typeof value !== "string" || value.length > 2048) throw authorityUnavailable();
773
+ let url;
774
+ try {
775
+ url = new URL(value);
776
+ } catch {
777
+ throw authorityUnavailable();
778
+ }
779
+ if (url.protocol !== "https:" || url.username || url.password || url.toString() !== value) throw authorityUnavailable();
780
+ return value;
781
+ }
782
+ function responseDate(value) {
783
+ if (typeof value !== "string") throw authorityUnavailable();
784
+ const date = new Date(value);
785
+ if (!Number.isFinite(date.getTime()) || date.toISOString() !== value) throw authorityUnavailable();
786
+ return date;
787
+ }
788
+ function nullableCursor(value) {
789
+ if (value === null) return null;
790
+ if (typeof value !== "string" || value.length < 1 || value.length > 820 || !/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(value)) throw authorityUnavailable();
791
+ return value;
792
+ }
793
+ function normalizeResponseId(value) {
794
+ if (!isId(value)) throw authorityUnavailable();
795
+ return value;
796
+ }
797
+ function unique(items, key) {
798
+ if (new Set(items.map(key)).size !== items.length) throw authorityUnavailable();
799
+ return items;
800
+ }
801
+ function isObject(value) {
802
+ return typeof value === "object" && value !== null && !Array.isArray(value);
803
+ }
804
+ //#endregion
805
+ //#region src/provisioning.ts
806
+ const MAX_BYTES = 16 * 1024;
807
+ const compactJwt = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;
808
+ function isCompactCredential(value) {
809
+ return typeof value === "string" && value.length + 7 <= MAX_BYTES && compactJwt.test(value);
810
+ }
811
+ function createDriveProvisioningAuth(options) {
812
+ const url = joinURL(normalizeOrigin(options.authOrigin, "authOrigin"), "api/v1/auth/drive-provisioning-credential");
813
+ const fetchImpl = options.fetch ?? globalThis.fetch?.bind(globalThis);
814
+ if (typeof fetchImpl !== "function") throw new TypeError("fetch is required");
815
+ const timeoutMs = normalizeTimeout(options.timeoutMs);
816
+ const now = options.now ?? Date.now;
817
+ let inFlight = false;
818
+ return { async getDriveProvisioningCredential(clientAssertion) {
819
+ if (!isCompactCredential(clientAssertion)) throw new AuthError("invalid_request", 400);
820
+ if (inFlight) throw new AuthError("state_conflict", 409);
821
+ inFlight = true;
822
+ const controller = new AbortController();
823
+ let reader;
824
+ let timer;
825
+ let exchangeStarted = false;
826
+ let authorityError;
827
+ try {
828
+ const startedAt = now();
829
+ if (!Number.isSafeInteger(startedAt) || startedAt < 0 || startedAt > 86399999997e5) throw authorityUnavailable();
830
+ const deadline = new Promise((_, reject) => {
831
+ timer = setTimeout(() => {
832
+ reject(authorityUnavailable());
833
+ controller.abort();
834
+ reader?.cancel().catch(() => {});
835
+ }, timeoutMs);
836
+ });
837
+ const exchange = async () => {
838
+ const response = await fetchImpl(url, {
839
+ method: "POST",
840
+ headers: {
841
+ authorization: `Bearer ${clientAssertion}`,
842
+ accept: "application/json"
843
+ },
844
+ credentials: "omit",
845
+ cache: "no-store",
846
+ redirect: "manual",
847
+ signal: controller.signal
848
+ });
849
+ if (controller.signal.aborted) {
850
+ response.body?.cancel().catch(() => {});
851
+ throw authorityUnavailable();
852
+ }
853
+ if (!response.body) throw authorityUnavailable();
854
+ reader = response.body.getReader();
855
+ const contentType = response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
856
+ const length = response.headers.get("content-length");
857
+ if (contentType !== "application/json" || length !== null && (!/^\d+$/.test(length) || Number(length) > MAX_BYTES)) throw authorityUnavailable();
858
+ const bytes = new Uint8Array(MAX_BYTES);
859
+ let size = 0;
860
+ while (true) {
861
+ const chunk = await reader.read();
862
+ if (chunk.done) break;
863
+ if (controller.signal.aborted || size + chunk.value.byteLength > MAX_BYTES) throw authorityUnavailable();
864
+ bytes.set(chunk.value, size);
865
+ size += chunk.value.byteLength;
866
+ }
867
+ const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(0, size)));
868
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw authorityUnavailable();
869
+ const record = value;
870
+ if (response.status !== 200) {
871
+ if (Object.keys(record).length !== 2 || !("code" in record) || !("message" in record)) throw authorityUnavailable();
872
+ if (response.status === 401 && record.code === "UNAUTHORIZED" && record.message === "The ecosystem client assertion is invalid.") {
873
+ authorityError = new AuthError("authentication_required", 401);
874
+ throw authorityError;
875
+ }
876
+ const retryAfter = response.headers.get("retry-after");
877
+ if (response.status === 429 && record.code === "TOO_MANY_REQUESTS" && record.message === "Ecosystem credential exchanges are temporarily limited." && retryAfter !== null && /^(?:[1-9]|[1-5][0-9]|60)$/.test(retryAfter)) {
878
+ authorityError = new AuthError("auth_request_rate_limited", 429, Number(retryAfter));
879
+ throw authorityError;
880
+ }
881
+ throw authorityUnavailable();
882
+ }
883
+ if (Object.keys(record).length !== 3 || record.tokenType !== "Bearer" || record.expiresIn !== 300 || !isCompactCredential(record.token)) throw authorityUnavailable();
884
+ const expiresAt = new Date(Math.floor(startedAt / 1e3) * 1e3 + 3e5);
885
+ const completedAt = now();
886
+ if (!Number.isSafeInteger(completedAt) || completedAt < 0 || completedAt >= expiresAt.getTime()) throw authorityUnavailable();
887
+ return {
888
+ tokenType: "Bearer",
889
+ token: record.token,
890
+ expiresAt
891
+ };
892
+ };
893
+ exchangeStarted = true;
894
+ return await Promise.race([exchange().finally(() => {
895
+ inFlight = false;
896
+ }), deadline]);
897
+ } catch (error) {
898
+ if (authorityError && error === authorityError) throw authorityError;
899
+ throw authorityUnavailable();
900
+ } finally {
901
+ clearTimeout(timer);
902
+ controller.abort();
903
+ reader?.cancel().catch(() => {});
904
+ if (!exchangeStarted) inFlight = false;
905
+ }
906
+ } };
907
+ }
908
+ //#endregion
909
+ //#region ../../node_modules/.pnpm/destr@2.0.5/node_modules/destr/dist/index.mjs
910
+ const suspectProtoRx = /"(?:_|\\u0{2}5[Ff]){2}(?:p|\\u0{2}70)(?:r|\\u0{2}72)(?:o|\\u0{2}6[Ff])(?:t|\\u0{2}74)(?:o|\\u0{2}6[Ff])(?:_|\\u0{2}5[Ff]){2}"\s*:/;
911
+ const suspectConstructorRx = /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/;
912
+ const JsonSigRx = /^\s*["[{]|^\s*-?\d{1,16}(\.\d{1,17})?([Ee][+-]?\d+)?\s*$/;
913
+ function jsonParseTransform(key, value) {
914
+ if (key === "__proto__" || key === "constructor" && value && typeof value === "object" && "prototype" in value) {
915
+ warnKeyDropped(key);
916
+ return;
917
+ }
918
+ return value;
919
+ }
920
+ function warnKeyDropped(key) {
921
+ console.warn(`[destr] Dropping "${key}" key to prevent prototype pollution.`);
922
+ }
923
+ function destr(value, options = {}) {
924
+ if (typeof value !== "string") return value;
925
+ if (value[0] === "\"" && value[value.length - 1] === "\"" && value.indexOf("\\") === -1) return value.slice(1, -1);
926
+ const _value = value.trim();
927
+ if (_value.length <= 9) switch (_value.toLowerCase()) {
928
+ case "true": return true;
929
+ case "false": return false;
930
+ case "undefined": return;
931
+ case "null": return null;
932
+ case "nan": return NaN;
933
+ case "infinity": return Number.POSITIVE_INFINITY;
934
+ case "-infinity": return Number.NEGATIVE_INFINITY;
935
+ }
936
+ if (!JsonSigRx.test(value)) {
937
+ if (options.strict) throw new SyntaxError("[destr] Invalid JSON");
938
+ return value;
939
+ }
940
+ try {
941
+ if (suspectProtoRx.test(value) || suspectConstructorRx.test(value)) {
942
+ if (options.strict) throw new Error("[destr] Possible prototype pollution");
943
+ return JSON.parse(value, jsonParseTransform);
944
+ }
945
+ return JSON.parse(value);
946
+ } catch (error) {
947
+ if (options.strict) throw error;
948
+ return value;
949
+ }
950
+ }
951
+ //#endregion
952
+ //#region ../../node_modules/.pnpm/ofetch@1.5.1/node_modules/ofetch/dist/shared/ofetch.CWycOUEr.mjs
953
+ var FetchError = class extends Error {
954
+ constructor(message, opts) {
955
+ super(message, opts);
956
+ this.name = "FetchError";
957
+ if (opts?.cause && !this.cause) this.cause = opts.cause;
958
+ }
959
+ };
960
+ function createFetchError(ctx) {
961
+ const errorMessage = ctx.error?.message || ctx.error?.toString() || "";
962
+ const method = ctx.request?.method || ctx.options?.method || "GET";
963
+ const url = ctx.request?.url || String(ctx.request) || "/";
964
+ const fetchError = new FetchError(`${`[${method}] ${JSON.stringify(url)}`}: ${ctx.response ? `${ctx.response.status} ${ctx.response.statusText}` : "<no response>"}${errorMessage ? ` ${errorMessage}` : ""}`, ctx.error ? { cause: ctx.error } : void 0);
965
+ for (const key of [
966
+ "request",
967
+ "options",
968
+ "response"
969
+ ]) Object.defineProperty(fetchError, key, { get() {
970
+ return ctx[key];
971
+ } });
972
+ for (const [key, refKey] of [
973
+ ["data", "_data"],
974
+ ["status", "status"],
975
+ ["statusCode", "status"],
976
+ ["statusText", "statusText"],
977
+ ["statusMessage", "statusText"]
978
+ ]) Object.defineProperty(fetchError, key, { get() {
979
+ return ctx.response && ctx.response[refKey];
980
+ } });
981
+ return fetchError;
1029
982
  }
1030
- function role(value) {
1031
- return value === "owner" || value === "admin" || value === "member";
983
+ const payloadMethods = new Set(Object.freeze([
984
+ "PATCH",
985
+ "POST",
986
+ "PUT",
987
+ "DELETE"
988
+ ]));
989
+ function isPayloadMethod(method = "GET") {
990
+ return payloadMethods.has(method.toUpperCase());
1032
991
  }
1033
- function nullableHttpsUrl(value) {
1034
- if (value === null) return null;
1035
- if (typeof value !== "string" || value.length > 2048) throw authorityUnavailable();
1036
- let url;
1037
- try {
1038
- url = new URL(value);
1039
- } catch {
1040
- throw authorityUnavailable();
1041
- }
1042
- if (url.protocol !== "https:" || url.username || url.password || url.toString() !== value) throw authorityUnavailable();
1043
- return value;
992
+ function isJSONSerializable(value) {
993
+ if (value === void 0) return false;
994
+ const t = typeof value;
995
+ if (t === "string" || t === "number" || t === "boolean" || t === null) return true;
996
+ if (t !== "object") return false;
997
+ if (Array.isArray(value)) return true;
998
+ if (value.buffer) return false;
999
+ if (value instanceof FormData || value instanceof URLSearchParams) return false;
1000
+ return value.constructor && value.constructor.name === "Object" || typeof value.toJSON === "function";
1044
1001
  }
1045
- function responseDate(value) {
1046
- if (typeof value !== "string") throw authorityUnavailable();
1047
- const date = new Date(value);
1048
- if (!Number.isFinite(date.getTime()) || date.toISOString() !== value) throw authorityUnavailable();
1049
- return date;
1002
+ const textTypes = new Set([
1003
+ "image/svg",
1004
+ "application/xml",
1005
+ "application/xhtml",
1006
+ "application/html"
1007
+ ]);
1008
+ const JSON_RE = /^application\/(?:[\w!#$%&*.^`~-]*\+)?json(;.+)?$/i;
1009
+ function detectResponseType(_contentType = "") {
1010
+ if (!_contentType) return "json";
1011
+ const contentType = _contentType.split(";").shift() || "";
1012
+ if (JSON_RE.test(contentType)) return "json";
1013
+ if (contentType === "text/event-stream") return "stream";
1014
+ if (textTypes.has(contentType) || contentType.startsWith("text/")) return "text";
1015
+ return "blob";
1050
1016
  }
1051
- function nullableCursor(value) {
1052
- if (value === null) return null;
1053
- if (typeof value !== "string" || value.length < 1 || value.length > 820 || !/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(value)) throw authorityUnavailable();
1054
- return value;
1017
+ function resolveFetchOptions(request, input, defaults, Headers) {
1018
+ const headers = mergeHeaders(input?.headers ?? request?.headers, defaults?.headers, Headers);
1019
+ let query;
1020
+ if (defaults?.query || defaults?.params || input?.params || input?.query) query = {
1021
+ ...defaults?.params,
1022
+ ...defaults?.query,
1023
+ ...input?.params,
1024
+ ...input?.query
1025
+ };
1026
+ return {
1027
+ ...defaults,
1028
+ ...input,
1029
+ query,
1030
+ params: query,
1031
+ headers
1032
+ };
1055
1033
  }
1056
- function normalizeResponseId(value) {
1057
- if (!isId(value)) throw authorityUnavailable();
1058
- return value;
1034
+ function mergeHeaders(input, defaults, Headers) {
1035
+ if (!defaults) return new Headers(input);
1036
+ const headers = new Headers(defaults);
1037
+ if (input) for (const [key, value] of Symbol.iterator in input || Array.isArray(input) ? input : new Headers(input)) headers.set(key, value);
1038
+ return headers;
1059
1039
  }
1060
- function unique(items, key) {
1061
- if (new Set(items.map(key)).size !== items.length) throw authorityUnavailable();
1062
- return items;
1040
+ async function callHooks(context, hooks) {
1041
+ if (hooks) if (Array.isArray(hooks)) for (const hook of hooks) await hook(context);
1042
+ else await hooks(context);
1063
1043
  }
1064
- function isObject(value) {
1065
- return typeof value === "object" && value !== null && !Array.isArray(value);
1044
+ const retryStatusCodes = new Set([
1045
+ 408,
1046
+ 409,
1047
+ 425,
1048
+ 429,
1049
+ 500,
1050
+ 502,
1051
+ 503,
1052
+ 504
1053
+ ]);
1054
+ const nullBodyResponses = new Set([
1055
+ 101,
1056
+ 204,
1057
+ 205,
1058
+ 304
1059
+ ]);
1060
+ function createFetch(globalOptions = {}) {
1061
+ const { fetch = globalThis.fetch, Headers = globalThis.Headers, AbortController = globalThis.AbortController } = globalOptions;
1062
+ async function onError(context) {
1063
+ const isAbort = context.error && context.error.name === "AbortError" && !context.options.timeout || false;
1064
+ if (context.options.retry !== false && !isAbort) {
1065
+ let retries;
1066
+ if (typeof context.options.retry === "number") retries = context.options.retry;
1067
+ else retries = isPayloadMethod(context.options.method) ? 0 : 1;
1068
+ const responseCode = context.response && context.response.status || 500;
1069
+ if (retries > 0 && (Array.isArray(context.options.retryStatusCodes) ? context.options.retryStatusCodes.includes(responseCode) : retryStatusCodes.has(responseCode))) {
1070
+ const retryDelay = typeof context.options.retryDelay === "function" ? context.options.retryDelay(context) : context.options.retryDelay || 0;
1071
+ if (retryDelay > 0) await new Promise((resolve) => setTimeout(resolve, retryDelay));
1072
+ return $fetchRaw(context.request, {
1073
+ ...context.options,
1074
+ retry: retries - 1
1075
+ });
1076
+ }
1077
+ }
1078
+ const error = createFetchError(context);
1079
+ if (Error.captureStackTrace) Error.captureStackTrace(error, $fetchRaw);
1080
+ throw error;
1081
+ }
1082
+ const $fetchRaw = async function $fetchRaw2(_request, _options = {}) {
1083
+ const context = {
1084
+ request: _request,
1085
+ options: resolveFetchOptions(_request, _options, globalOptions.defaults, Headers),
1086
+ response: void 0,
1087
+ error: void 0
1088
+ };
1089
+ if (context.options.method) context.options.method = context.options.method.toUpperCase();
1090
+ if (context.options.onRequest) {
1091
+ await callHooks(context, context.options.onRequest);
1092
+ if (!(context.options.headers instanceof Headers)) context.options.headers = new Headers(context.options.headers || {});
1093
+ }
1094
+ if (typeof context.request === "string") {
1095
+ if (context.options.baseURL) context.request = withBase(context.request, context.options.baseURL);
1096
+ if (context.options.query) {
1097
+ context.request = withQuery(context.request, context.options.query);
1098
+ delete context.options.query;
1099
+ }
1100
+ if ("query" in context.options) delete context.options.query;
1101
+ if ("params" in context.options) delete context.options.params;
1102
+ }
1103
+ if (context.options.body && isPayloadMethod(context.options.method)) {
1104
+ if (isJSONSerializable(context.options.body)) {
1105
+ const contentType = context.options.headers.get("content-type");
1106
+ if (typeof context.options.body !== "string") context.options.body = contentType === "application/x-www-form-urlencoded" ? new URLSearchParams(context.options.body).toString() : JSON.stringify(context.options.body);
1107
+ if (!contentType) context.options.headers.set("content-type", "application/json");
1108
+ if (!context.options.headers.has("accept")) context.options.headers.set("accept", "application/json");
1109
+ } else if ("pipeTo" in context.options.body && typeof context.options.body.pipeTo === "function" || typeof context.options.body.pipe === "function") {
1110
+ if (!("duplex" in context.options)) context.options.duplex = "half";
1111
+ }
1112
+ }
1113
+ let abortTimeout;
1114
+ if (!context.options.signal && context.options.timeout) {
1115
+ const controller = new AbortController();
1116
+ abortTimeout = setTimeout(() => {
1117
+ const error = new Error("[TimeoutError]: The operation was aborted due to timeout");
1118
+ error.name = "TimeoutError";
1119
+ error.code = 23;
1120
+ controller.abort(error);
1121
+ }, context.options.timeout);
1122
+ context.options.signal = controller.signal;
1123
+ }
1124
+ try {
1125
+ context.response = await fetch(context.request, context.options);
1126
+ } catch (error) {
1127
+ context.error = error;
1128
+ if (context.options.onRequestError) await callHooks(context, context.options.onRequestError);
1129
+ return await onError(context);
1130
+ } finally {
1131
+ if (abortTimeout) clearTimeout(abortTimeout);
1132
+ }
1133
+ if ((context.response.body || context.response._bodyInit) && !nullBodyResponses.has(context.response.status) && context.options.method !== "HEAD") {
1134
+ const responseType = (context.options.parseResponse ? "json" : context.options.responseType) || detectResponseType(context.response.headers.get("content-type") || "");
1135
+ switch (responseType) {
1136
+ case "json": {
1137
+ const data = await context.response.text();
1138
+ const parseFunction = context.options.parseResponse || destr;
1139
+ context.response._data = parseFunction(data);
1140
+ break;
1141
+ }
1142
+ case "stream":
1143
+ context.response._data = context.response.body || context.response._bodyInit;
1144
+ break;
1145
+ default: context.response._data = await context.response[responseType]();
1146
+ }
1147
+ }
1148
+ if (context.options.onResponse) await callHooks(context, context.options.onResponse);
1149
+ if (!context.options.ignoreResponseError && context.response.status >= 400 && context.response.status < 600) {
1150
+ if (context.options.onResponseError) await callHooks(context, context.options.onResponseError);
1151
+ return await onError(context);
1152
+ }
1153
+ return context.response;
1154
+ };
1155
+ const $fetch = async function $fetch2(request, options) {
1156
+ return (await $fetchRaw(request, options))._data;
1157
+ };
1158
+ $fetch.raw = $fetchRaw;
1159
+ $fetch.native = (...args) => fetch(...args);
1160
+ $fetch.create = (defaultOptions = {}, customGlobalOptions = {}) => createFetch({
1161
+ ...globalOptions,
1162
+ ...customGlobalOptions,
1163
+ defaults: {
1164
+ ...globalOptions.defaults,
1165
+ ...customGlobalOptions.defaults,
1166
+ ...defaultOptions
1167
+ }
1168
+ });
1169
+ return $fetch;
1066
1170
  }
1067
1171
  //#endregion
1068
1172
  //#region src/http.ts
@@ -1390,7 +1494,11 @@ function createServerAuth(options) {
1390
1494
  response = await fetchImpl(joinURL(authOrigin, "api/v1/auth/sign-out"), {
1391
1495
  method: "POST",
1392
1496
  credentials,
1393
- headers: authHeaders(),
1497
+ headers: {
1498
+ ...authHeaders(),
1499
+ "content-type": "application/json; charset=utf-8"
1500
+ },
1501
+ body: "{}",
1394
1502
  redirect: "manual",
1395
1503
  signal: AbortSignal.timeout(timeoutMs)
1396
1504
  });
@@ -1431,4 +1539,4 @@ function authAuthorizationHeader(value) {
1431
1539
  return value;
1432
1540
  }
1433
1541
  //#endregion
1434
- export { AuthError, createServerAuth };
1542
+ export { AuthError, createDriveProvisioningAuth, createServerAuth };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finueva/auth",
3
- "version": "0.13.0",
3
+ "version": "0.14.1",
4
4
  "description": "Public browser and server SDK for Finueva Auth",
5
5
  "type": "module",
6
6
  "sideEffects": false,