@baliola/auth-sdk 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +76 -0
- package/LICENSE +37 -0
- package/README.md +305 -0
- package/dist/authError-C5g5jP5l.d.ts +102 -0
- package/dist/authError-DbEZJnC4.js +214 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +2 -0
- package/dist/client-BcyoQ5Cj.js +504 -0
- package/dist/errors/index.d.ts +2 -0
- package/dist/errors/index.js +2 -0
- package/dist/index-G-gqveGn.d.ts +142 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +4 -0
- package/dist/requests-eTORIjY5.d.ts +42 -0
- package/dist/session-Cs_P7ojF.d.ts +49 -0
- package/dist/sessionStore-BDdEpbL8.d.ts +35 -0
- package/dist/sessionStore-DD6lON9W.js +50 -0
- package/dist/store/index.d.ts +2 -0
- package/dist/store/index.js +2 -0
- package/dist/tokens-BXrPLi5B.d.ts +29 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.js +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
import { _ as authErrorFromResponse, g as authErrorFromFetchFailure, n as AuthError } from "./authError-DbEZJnC4.js";
|
|
2
|
+
import { n as memoryStore } from "./sessionStore-DD6lON9W.js";
|
|
3
|
+
//#region src/client/methods.ts
|
|
4
|
+
function toAuthSession(data) {
|
|
5
|
+
const issuedAt = Date.now();
|
|
6
|
+
return {
|
|
7
|
+
accessToken: data.accessToken,
|
|
8
|
+
refreshToken: data.refreshToken,
|
|
9
|
+
expiresIn: data.expiresIn,
|
|
10
|
+
issuedAt,
|
|
11
|
+
expiresAt: issuedAt + data.expiresIn * 1e3,
|
|
12
|
+
account: data.account,
|
|
13
|
+
...data.project !== void 0 && { project: data.project },
|
|
14
|
+
roles: data.roles,
|
|
15
|
+
permissions: data.permissions
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function createMethods(ctx) {
|
|
19
|
+
const withClientId = (body) => {
|
|
20
|
+
if (ctx.clientId === void 0) return body;
|
|
21
|
+
return {
|
|
22
|
+
...body,
|
|
23
|
+
clientId: ctx.clientId
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
const stripUndefined = (body) => {
|
|
27
|
+
const out = {};
|
|
28
|
+
for (const [k, v] of Object.entries(body)) if (v !== void 0) out[k] = v;
|
|
29
|
+
return out;
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
async sendLoginCode(input) {
|
|
33
|
+
return ctx.transport.request({
|
|
34
|
+
path: "/auth/email-otp/send-login-code",
|
|
35
|
+
method: "POST",
|
|
36
|
+
authMode: "none",
|
|
37
|
+
body: stripUndefined(withClientId({
|
|
38
|
+
email: input.email,
|
|
39
|
+
captchaToken: input.captchaToken
|
|
40
|
+
}))
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
async verifyLoginCode(input) {
|
|
44
|
+
return toAuthSession(await ctx.transport.request({
|
|
45
|
+
path: "/auth/email-otp/verify-login-code",
|
|
46
|
+
method: "POST",
|
|
47
|
+
authMode: "none",
|
|
48
|
+
body: withClientId({
|
|
49
|
+
email: input.email,
|
|
50
|
+
otp: input.code
|
|
51
|
+
})
|
|
52
|
+
}));
|
|
53
|
+
},
|
|
54
|
+
async resendLoginCode(input) {
|
|
55
|
+
return ctx.transport.request({
|
|
56
|
+
path: "/auth/email-otp/resend-login-code",
|
|
57
|
+
method: "POST",
|
|
58
|
+
authMode: "none",
|
|
59
|
+
body: stripUndefined(withClientId({
|
|
60
|
+
email: input.email,
|
|
61
|
+
captchaToken: input.captchaToken
|
|
62
|
+
}))
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
async register(input) {
|
|
66
|
+
return ctx.transport.request({
|
|
67
|
+
path: "/auth/email-password/register",
|
|
68
|
+
method: "POST",
|
|
69
|
+
authMode: "none",
|
|
70
|
+
body: stripUndefined(withClientId({
|
|
71
|
+
email: input.email,
|
|
72
|
+
password: input.password,
|
|
73
|
+
captchaToken: input.captchaToken
|
|
74
|
+
}))
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
async verifyRegistrationCode(input) {
|
|
78
|
+
return toAuthSession(await ctx.transport.request({
|
|
79
|
+
path: "/auth/email-password/verify-registration-code",
|
|
80
|
+
method: "POST",
|
|
81
|
+
authMode: "none",
|
|
82
|
+
body: withClientId({
|
|
83
|
+
email: input.email,
|
|
84
|
+
otp: input.code
|
|
85
|
+
})
|
|
86
|
+
}));
|
|
87
|
+
},
|
|
88
|
+
async resendRegistrationCode(input) {
|
|
89
|
+
return ctx.transport.request({
|
|
90
|
+
path: "/auth/email-password/resend-registration-code",
|
|
91
|
+
method: "POST",
|
|
92
|
+
authMode: "none",
|
|
93
|
+
body: stripUndefined(withClientId({
|
|
94
|
+
email: input.email,
|
|
95
|
+
captchaToken: input.captchaToken
|
|
96
|
+
}))
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
async loginWithPassword(input) {
|
|
100
|
+
return toAuthSession(await ctx.transport.request({
|
|
101
|
+
path: "/auth/email-password/login",
|
|
102
|
+
method: "POST",
|
|
103
|
+
authMode: "none",
|
|
104
|
+
body: stripUndefined(withClientId({
|
|
105
|
+
email: input.email,
|
|
106
|
+
password: input.password,
|
|
107
|
+
captchaToken: input.captchaToken
|
|
108
|
+
}))
|
|
109
|
+
}));
|
|
110
|
+
},
|
|
111
|
+
async setPassword(input) {
|
|
112
|
+
await ctx.transport.request({
|
|
113
|
+
path: "/auth/email-password/set-password",
|
|
114
|
+
method: "POST",
|
|
115
|
+
authMode: "bearer+session",
|
|
116
|
+
body: { password: input.password }
|
|
117
|
+
});
|
|
118
|
+
},
|
|
119
|
+
async changePassword(input) {
|
|
120
|
+
await ctx.transport.request({
|
|
121
|
+
path: "/auth/email-password/change-password",
|
|
122
|
+
method: "POST",
|
|
123
|
+
authMode: "bearer+session",
|
|
124
|
+
body: {
|
|
125
|
+
currentPassword: input.currentPassword,
|
|
126
|
+
newPassword: input.newPassword
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
},
|
|
130
|
+
async loginWithGoogle(input) {
|
|
131
|
+
return toAuthSession(await ctx.transport.request({
|
|
132
|
+
path: "/auth/google/login",
|
|
133
|
+
method: "POST",
|
|
134
|
+
authMode: "none",
|
|
135
|
+
body: withClientId({ idToken: input.idToken })
|
|
136
|
+
}));
|
|
137
|
+
},
|
|
138
|
+
async logoutRequest() {
|
|
139
|
+
await ctx.transport.request({
|
|
140
|
+
path: "/auth/logout",
|
|
141
|
+
method: "POST",
|
|
142
|
+
authMode: "bearer+session"
|
|
143
|
+
});
|
|
144
|
+
},
|
|
145
|
+
/**
|
|
146
|
+
* Type-only helper: declares the refresh response shape so consumers
|
|
147
|
+
* pulling the inferred Methods type don't break. Refresh itself is
|
|
148
|
+
* handled by transport.refreshSession() to keep stampede protection.
|
|
149
|
+
*/
|
|
150
|
+
refreshResponseShape() {
|
|
151
|
+
throw new Error("refreshResponseShape is a type-only helper");
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region src/client/transport.ts
|
|
157
|
+
/**
|
|
158
|
+
* Minimal fetch wrapper for Baliola Auth: header injection, timeout via
|
|
159
|
+
* AbortController, error mapping to AuthError, and the 401 → /auth/refresh
|
|
160
|
+
* → retry-once stampede-safe flow.
|
|
161
|
+
*
|
|
162
|
+
* Public surface: `request<T>()` for SDK-internal endpoints (returns parsed
|
|
163
|
+
* data of type T or throws AuthError) and `passthroughFetch()` for the
|
|
164
|
+
* consumer escape hatch (returns Response, never throws on consumer-URL
|
|
165
|
+
* non-2xx, throws only when the SDK's own refresh sub-call fails).
|
|
166
|
+
*/
|
|
167
|
+
function createTransport(options) {
|
|
168
|
+
const { baseUrl, fetch: fetchImpl, timeoutMs, onRefreshed, onSessionLost, getCurrentSession, shouldProactivelyRefresh } = options;
|
|
169
|
+
let refreshPromise = null;
|
|
170
|
+
function buildAuthHeaders(mode, session) {
|
|
171
|
+
const headers = new Headers();
|
|
172
|
+
headers.set("Content-Type", "application/json");
|
|
173
|
+
headers.set("Accept", "application/json");
|
|
174
|
+
if (!session) return headers;
|
|
175
|
+
switch (mode) {
|
|
176
|
+
case "bearer+session":
|
|
177
|
+
headers.set("Authorization", `Bearer ${session.accessToken}`);
|
|
178
|
+
headers.set("X-Session-ID", session.refreshToken);
|
|
179
|
+
break;
|
|
180
|
+
case "session-only":
|
|
181
|
+
headers.set("X-Session-ID", session.refreshToken);
|
|
182
|
+
break;
|
|
183
|
+
case "bearer-only-passthrough":
|
|
184
|
+
headers.set("Authorization", `Bearer ${session.accessToken}`);
|
|
185
|
+
break;
|
|
186
|
+
case "none": break;
|
|
187
|
+
}
|
|
188
|
+
return headers;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Compose two AbortSignals into one. Uses native AbortSignal.any when
|
|
192
|
+
* available (Node 20.3+, modern browsers, Bun); falls back to a small
|
|
193
|
+
* polyfill otherwise.
|
|
194
|
+
*/
|
|
195
|
+
function composeSignals(signals) {
|
|
196
|
+
const real = signals.filter((s) => s !== void 0);
|
|
197
|
+
if (real.length === 0) return new AbortController().signal;
|
|
198
|
+
if (real.length === 1) return real[0];
|
|
199
|
+
if (typeof AbortSignal.any === "function") return AbortSignal.any(real);
|
|
200
|
+
const ctrl = new AbortController();
|
|
201
|
+
for (const s of real) {
|
|
202
|
+
if (s.aborted) {
|
|
203
|
+
ctrl.abort(s.reason);
|
|
204
|
+
return ctrl.signal;
|
|
205
|
+
}
|
|
206
|
+
s.addEventListener("abort", () => ctrl.abort(s.reason), { once: true });
|
|
207
|
+
}
|
|
208
|
+
return ctrl.signal;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Run a single fetch with timeout + consumer signal composition.
|
|
212
|
+
* Returns the raw Response. Network/abort failures become AuthError.
|
|
213
|
+
*/
|
|
214
|
+
async function rawFetch(url, init) {
|
|
215
|
+
const timeoutCtrl = new AbortController();
|
|
216
|
+
const timer = setTimeout(() => timeoutCtrl.abort(/* @__PURE__ */ new Error("timeout")), timeoutMs);
|
|
217
|
+
let didTimeout = false;
|
|
218
|
+
timeoutCtrl.signal.addEventListener("abort", () => {
|
|
219
|
+
didTimeout = true;
|
|
220
|
+
}, { once: true });
|
|
221
|
+
try {
|
|
222
|
+
const signal = composeSignals([timeoutCtrl.signal, init.signal]);
|
|
223
|
+
return await fetchImpl(url, {
|
|
224
|
+
...init,
|
|
225
|
+
signal
|
|
226
|
+
});
|
|
227
|
+
} catch (err) {
|
|
228
|
+
throw authErrorFromFetchFailure(err, didTimeout);
|
|
229
|
+
} finally {
|
|
230
|
+
clearTimeout(timer);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Call POST /auth/refresh. Used by both proactive and reactive paths.
|
|
235
|
+
* Stampede-protected via shared `refreshPromise`.
|
|
236
|
+
*/
|
|
237
|
+
function refreshSession() {
|
|
238
|
+
if (refreshPromise) return refreshPromise;
|
|
239
|
+
const current = getCurrentSession();
|
|
240
|
+
if (!current) {
|
|
241
|
+
const err = new AuthError({
|
|
242
|
+
status: 401,
|
|
243
|
+
serverMessage: "No session to refresh"
|
|
244
|
+
});
|
|
245
|
+
onSessionLost();
|
|
246
|
+
return Promise.reject(err);
|
|
247
|
+
}
|
|
248
|
+
refreshPromise = (async () => {
|
|
249
|
+
try {
|
|
250
|
+
const headers = buildAuthHeaders("session-only", current);
|
|
251
|
+
const response = await rawFetch(`${baseUrl}/auth/refresh`, {
|
|
252
|
+
method: "POST",
|
|
253
|
+
headers
|
|
254
|
+
});
|
|
255
|
+
if (!response.ok) {
|
|
256
|
+
const err = await authErrorFromResponse(response);
|
|
257
|
+
await onSessionLost();
|
|
258
|
+
throw err;
|
|
259
|
+
}
|
|
260
|
+
const body = await response.json();
|
|
261
|
+
const issuedAt = Date.now();
|
|
262
|
+
const merged = {
|
|
263
|
+
...current,
|
|
264
|
+
accessToken: body.data.accessToken,
|
|
265
|
+
refreshToken: body.data.refreshToken,
|
|
266
|
+
issuedAt,
|
|
267
|
+
expiresAt: issuedAt + current.expiresIn * 1e3
|
|
268
|
+
};
|
|
269
|
+
await onRefreshed(merged);
|
|
270
|
+
return merged;
|
|
271
|
+
} catch (err) {
|
|
272
|
+
if (err instanceof AuthError) {
|
|
273
|
+
if (err.status === 0) await onSessionLost();
|
|
274
|
+
throw err;
|
|
275
|
+
}
|
|
276
|
+
await onSessionLost();
|
|
277
|
+
throw authErrorFromFetchFailure(err);
|
|
278
|
+
} finally {
|
|
279
|
+
refreshPromise = null;
|
|
280
|
+
}
|
|
281
|
+
})();
|
|
282
|
+
return refreshPromise;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Internal SDK request helper. Returns parsed `data` of type T from
|
|
286
|
+
* the standard `{ message, data }` envelope. Throws AuthError on any
|
|
287
|
+
* non-2xx after the refresh-retry flow.
|
|
288
|
+
*/
|
|
289
|
+
async function request(opts) {
|
|
290
|
+
if (opts.authMode === "bearer+session" && shouldProactivelyRefresh() && !opts.skipAutoRefresh) await refreshSession();
|
|
291
|
+
const url = `${baseUrl}${opts.path}`;
|
|
292
|
+
let response = await rawFetch(url, {
|
|
293
|
+
method: opts.method,
|
|
294
|
+
headers: buildAuthHeaders(opts.authMode, getCurrentSession()),
|
|
295
|
+
...opts.body !== void 0 && { body: JSON.stringify(opts.body) },
|
|
296
|
+
...opts.signal !== void 0 && { signal: opts.signal }
|
|
297
|
+
});
|
|
298
|
+
if (response.status === 401 && !opts.skipAutoRefresh && (opts.authMode === "bearer+session" || opts.authMode === "session-only") && getCurrentSession()) {
|
|
299
|
+
await refreshSession();
|
|
300
|
+
response = await rawFetch(url, {
|
|
301
|
+
method: opts.method,
|
|
302
|
+
headers: buildAuthHeaders(opts.authMode, getCurrentSession()),
|
|
303
|
+
...opts.body !== void 0 && { body: JSON.stringify(opts.body) },
|
|
304
|
+
...opts.signal !== void 0 && { signal: opts.signal }
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
if (!response.ok) throw await authErrorFromResponse(response);
|
|
308
|
+
const text = await response.text();
|
|
309
|
+
if (text.length === 0) return;
|
|
310
|
+
return JSON.parse(text).data;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Consumer-URL escape hatch. Returns the raw Response. Auto-refresh on
|
|
314
|
+
* 401 the same way as internal calls, but:
|
|
315
|
+
* - Does NOT throw on consumer-URL non-2xx; consumer handles via res.ok.
|
|
316
|
+
* - DOES throw AuthError when the SDK's own /auth/refresh sub-call fails.
|
|
317
|
+
* - Adds Authorization header ONLY if a session is loaded; otherwise
|
|
318
|
+
* passes through unauthenticated.
|
|
319
|
+
* - NEVER sends X-Session-ID to the consumer URL.
|
|
320
|
+
*/
|
|
321
|
+
async function passthroughFetch(input, init = {}) {
|
|
322
|
+
const { requireAuth, disableRefresh, ...fetchInit } = init;
|
|
323
|
+
if (requireAuth && !getCurrentSession()) throw new AuthError({
|
|
324
|
+
status: 401,
|
|
325
|
+
serverMessage: "No session loaded"
|
|
326
|
+
});
|
|
327
|
+
const session = getCurrentSession();
|
|
328
|
+
if (!disableRefresh && session && shouldProactivelyRefresh()) await refreshSession();
|
|
329
|
+
const composedInit = composeAuthInit(fetchInit, getCurrentSession());
|
|
330
|
+
let response = await rawFetch(toUrlString(input), composedInit);
|
|
331
|
+
if (!disableRefresh && response.status === 401 && getCurrentSession()) {
|
|
332
|
+
await refreshSession();
|
|
333
|
+
const retryInit = composeAuthInit(fetchInit, getCurrentSession());
|
|
334
|
+
response = await rawFetch(toUrlString(input), retryInit);
|
|
335
|
+
}
|
|
336
|
+
return response;
|
|
337
|
+
}
|
|
338
|
+
function composeAuthInit(init, session) {
|
|
339
|
+
const headers = new Headers(init.headers);
|
|
340
|
+
if (session) headers.set("Authorization", `Bearer ${session.accessToken}`);
|
|
341
|
+
const { signal: _signal, ...rest } = init;
|
|
342
|
+
const out = {
|
|
343
|
+
...rest,
|
|
344
|
+
headers
|
|
345
|
+
};
|
|
346
|
+
if (init.signal) out.signal = init.signal;
|
|
347
|
+
return out;
|
|
348
|
+
}
|
|
349
|
+
function toUrlString(input) {
|
|
350
|
+
if (typeof input === "string") return input;
|
|
351
|
+
if (input instanceof URL) return input.toString();
|
|
352
|
+
return input.url;
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
request,
|
|
356
|
+
passthroughFetch,
|
|
357
|
+
refreshSession
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
//#endregion
|
|
361
|
+
//#region src/client/index.ts
|
|
362
|
+
const DEFAULT_TIMEOUT_MS = 15e3;
|
|
363
|
+
const DEFAULT_PROACTIVE_LEAD_TIME_MS = 6e4;
|
|
364
|
+
/**
|
|
365
|
+
* Channel name used to propagate session changes between same-origin tabs
|
|
366
|
+
* via `BroadcastChannel`. The `v1` suffix lets us evolve the wire format
|
|
367
|
+
* without colliding with old tabs running an older SDK.
|
|
368
|
+
*/
|
|
369
|
+
const BROADCAST_CHANNEL_NAME = "baliola.auth.session.v1";
|
|
370
|
+
function createAuthClient(options) {
|
|
371
|
+
if (!options.baseUrl) throw new Error("createAuthClient: baseUrl is required");
|
|
372
|
+
const baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
373
|
+
const store = options.store ?? memoryStore();
|
|
374
|
+
const fetchImpl = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
375
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
376
|
+
const proactiveLeadTimeMs = options.proactiveRefresh === false ? null : options.proactiveRefresh?.leadTimeMs ?? DEFAULT_PROACTIVE_LEAD_TIME_MS;
|
|
377
|
+
const onError = options.onError ?? ((e, source) => console.error("[auth-sdk]", source, e));
|
|
378
|
+
let currentSession = null;
|
|
379
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
380
|
+
function notifySubscribers(session) {
|
|
381
|
+
for (const handler of subscribers) callHandler(handler, session, "session-change-handler");
|
|
382
|
+
}
|
|
383
|
+
function callHandler(handler, session, source) {
|
|
384
|
+
try {
|
|
385
|
+
handler(session);
|
|
386
|
+
} catch (e) {
|
|
387
|
+
try {
|
|
388
|
+
onError(e instanceof Error ? e : new Error(String(e)), source);
|
|
389
|
+
} catch {}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
const channel = typeof globalThis.BroadcastChannel !== "undefined" ? new globalThis.BroadcastChannel(BROADCAST_CHANNEL_NAME) : null;
|
|
393
|
+
async function applyRemoteSession(session) {
|
|
394
|
+
currentSession = session;
|
|
395
|
+
await store.set(session);
|
|
396
|
+
notifySubscribers(session);
|
|
397
|
+
}
|
|
398
|
+
async function setSession(session) {
|
|
399
|
+
await applyRemoteSession(session);
|
|
400
|
+
channel?.postMessage({ session });
|
|
401
|
+
}
|
|
402
|
+
channel?.addEventListener("message", (event) => {
|
|
403
|
+
applyRemoteSession(event.data.session).catch((err) => {
|
|
404
|
+
onError(err instanceof Error ? err : new Error(String(err)), "broadcast-channel-message");
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
const transport = createTransport({
|
|
408
|
+
baseUrl,
|
|
409
|
+
fetch: fetchImpl,
|
|
410
|
+
timeoutMs,
|
|
411
|
+
getCurrentSession: () => currentSession,
|
|
412
|
+
shouldProactivelyRefresh: () => {
|
|
413
|
+
if (proactiveLeadTimeMs === null) return false;
|
|
414
|
+
if (!currentSession) return false;
|
|
415
|
+
return currentSession.expiresAt - Date.now() < proactiveLeadTimeMs;
|
|
416
|
+
},
|
|
417
|
+
onRefreshed: async (session) => {
|
|
418
|
+
currentSession = session;
|
|
419
|
+
await store.set(session);
|
|
420
|
+
notifySubscribers(session);
|
|
421
|
+
},
|
|
422
|
+
onSessionLost: async () => {
|
|
423
|
+
currentSession = null;
|
|
424
|
+
await store.set(null);
|
|
425
|
+
notifySubscribers(null);
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
const methods = createMethods({
|
|
429
|
+
transport,
|
|
430
|
+
...options.clientId !== void 0 && { clientId: options.clientId }
|
|
431
|
+
});
|
|
432
|
+
return {
|
|
433
|
+
emailOtp: {
|
|
434
|
+
sendLoginCode: (input) => methods.sendLoginCode(input),
|
|
435
|
+
async verifyLoginCode(input) {
|
|
436
|
+
const session = await methods.verifyLoginCode(input);
|
|
437
|
+
await setSession(session);
|
|
438
|
+
return session;
|
|
439
|
+
},
|
|
440
|
+
resendLoginCode: (input) => methods.resendLoginCode(input)
|
|
441
|
+
},
|
|
442
|
+
emailPassword: {
|
|
443
|
+
register: (input) => methods.register(input),
|
|
444
|
+
async verifyRegistrationCode(input) {
|
|
445
|
+
const session = await methods.verifyRegistrationCode(input);
|
|
446
|
+
await setSession(session);
|
|
447
|
+
return session;
|
|
448
|
+
},
|
|
449
|
+
resendRegistrationCode: (input) => methods.resendRegistrationCode(input),
|
|
450
|
+
async login(input) {
|
|
451
|
+
const session = await methods.loginWithPassword(input);
|
|
452
|
+
await setSession(session);
|
|
453
|
+
return session;
|
|
454
|
+
},
|
|
455
|
+
setPassword: (input) => methods.setPassword(input),
|
|
456
|
+
changePassword: (input) => methods.changePassword(input)
|
|
457
|
+
},
|
|
458
|
+
google: { async login(input) {
|
|
459
|
+
const session = await methods.loginWithGoogle(input);
|
|
460
|
+
await setSession(session);
|
|
461
|
+
return session;
|
|
462
|
+
} },
|
|
463
|
+
async refresh() {
|
|
464
|
+
return transport.refreshSession();
|
|
465
|
+
},
|
|
466
|
+
async logout() {
|
|
467
|
+
let serverError;
|
|
468
|
+
try {
|
|
469
|
+
await methods.logoutRequest();
|
|
470
|
+
} catch (e) {
|
|
471
|
+
serverError = e;
|
|
472
|
+
}
|
|
473
|
+
await setSession(null);
|
|
474
|
+
if (serverError !== void 0) throw serverError;
|
|
475
|
+
},
|
|
476
|
+
getSession() {
|
|
477
|
+
return currentSession;
|
|
478
|
+
},
|
|
479
|
+
isAuthenticated() {
|
|
480
|
+
return currentSession !== null;
|
|
481
|
+
},
|
|
482
|
+
async loadSession() {
|
|
483
|
+
const persisted = await store.get();
|
|
484
|
+
currentSession = persisted;
|
|
485
|
+
return persisted;
|
|
486
|
+
},
|
|
487
|
+
onSessionChange(handler, opts) {
|
|
488
|
+
const immediate = opts?.immediate ?? true;
|
|
489
|
+
subscribers.add(handler);
|
|
490
|
+
if (immediate) callHandler(handler, currentSession, "session-change-handler");
|
|
491
|
+
return () => {
|
|
492
|
+
subscribers.delete(handler);
|
|
493
|
+
};
|
|
494
|
+
},
|
|
495
|
+
fetch(input, init) {
|
|
496
|
+
return transport.passthroughFetch(input, init);
|
|
497
|
+
},
|
|
498
|
+
close() {
|
|
499
|
+
channel?.close();
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
//#endregion
|
|
504
|
+
export { createAuthClient as t };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { _ as authErrorFromFetchFailure, a as EmailAlreadyHasPasswordError, c as InvalidPasswordError, d as NoPasswordSetError, f as NoPendingOtpError, g as ResendCooldownError, h as RateLimitedError, i as CaptchaFailedError, l as MaxAttemptsError, m as OtpInvalidError, n as AuthError, o as InvalidCredentialsError, p as OtpExpiredError, r as AuthErrorInit, s as InvalidEmailError, t as AccountSuspendedError, u as MaxResendsError, v as authErrorFromResponse } from "../authError-C5g5jP5l.js";
|
|
2
|
+
export { AccountSuspendedError, AuthError, type AuthErrorInit, CaptchaFailedError, EmailAlreadyHasPasswordError, InvalidCredentialsError, InvalidEmailError, InvalidPasswordError, MaxAttemptsError, MaxResendsError, NoPasswordSetError, NoPendingOtpError, OtpExpiredError, OtpInvalidError, RateLimitedError, ResendCooldownError, authErrorFromFetchFailure, authErrorFromResponse };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { _ as authErrorFromResponse, a as InvalidCredentialsError, c as MaxAttemptsError, d as NoPendingOtpError, f as OtpExpiredError, g as authErrorFromFetchFailure, h as ResendCooldownError, i as EmailAlreadyHasPasswordError, l as MaxResendsError, m as RateLimitedError, n as AuthError, o as InvalidEmailError, p as OtpInvalidError, r as CaptchaFailedError, s as InvalidPasswordError, t as AccountSuspendedError, u as NoPasswordSetError } from "../authError-DbEZJnC4.js";
|
|
2
|
+
export { AccountSuspendedError, AuthError, CaptchaFailedError, EmailAlreadyHasPasswordError, InvalidCredentialsError, InvalidEmailError, InvalidPasswordError, MaxAttemptsError, MaxResendsError, NoPasswordSetError, NoPendingOtpError, OtpExpiredError, OtpInvalidError, RateLimitedError, ResendCooldownError, authErrorFromFetchFailure, authErrorFromResponse };
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { c as ResendCodeResult, l as SendLoginCodeResult, n as AuthSession, r as ErrorHandler, s as RegisterResult, u as SessionChangeHandler } from "./session-Cs_P7ojF.js";
|
|
2
|
+
import { n as SessionStore } from "./sessionStore-BDdEpbL8.js";
|
|
3
|
+
import { a as RegisterInput, c as SendLoginCodeInput, d as VerifyRegistrationCodeInput, i as LoginWithPasswordInput, l as SetPasswordInput, n as ChangePasswordInput, o as ResendLoginCodeInput, r as LoginWithGoogleInput, s as ResendRegistrationCodeInput, u as VerifyLoginCodeInput } from "./requests-eTORIjY5.js";
|
|
4
|
+
|
|
5
|
+
//#region src/client/transport.d.ts
|
|
6
|
+
type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
7
|
+
/**
|
|
8
|
+
* Init shape accepted by `auth.fetch()`. Extends `RequestInit` with two
|
|
9
|
+
* SDK-specific opt-in flags: `requireAuth` and `disableRefresh`. Both are
|
|
10
|
+
* stripped before the underlying fetch is called.
|
|
11
|
+
*/
|
|
12
|
+
type AuthFetchInit = RequestInit & {
|
|
13
|
+
/**
|
|
14
|
+
* If `true`, throw `AuthError({ status: 401 })` before sending when no
|
|
15
|
+
* session is loaded. Saves a wasted round-trip when the consumer URL
|
|
16
|
+
* would have 401-ed anyway.
|
|
17
|
+
*/
|
|
18
|
+
requireAuth?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* If `true`, skip the SDK's automatic session-refresh machinery for
|
|
21
|
+
* this single call — both proactive (pre-flight) and reactive (post-401
|
|
22
|
+
* retry) paths are bypassed. The raw 401 `Response` is returned to the
|
|
23
|
+
* caller. Use for fire-and-forget telemetry or beacons where a stale
|
|
24
|
+
* token shouldn't trigger a refresh round-trip.
|
|
25
|
+
*/
|
|
26
|
+
disableRefresh?: boolean;
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/client/index.d.ts
|
|
30
|
+
type ProactiveRefreshConfig = false | {
|
|
31
|
+
/** Refresh proactively if `expiresAt - now < leadTimeMs`. Defaults to 60_000 (60s). */leadTimeMs?: number;
|
|
32
|
+
};
|
|
33
|
+
type CreateAuthClientOptions = {
|
|
34
|
+
/** Base URL of the Baliola Auth server (no trailing slash). */baseUrl: string; /** Optional clientId, applied to every auth request that accepts one. */
|
|
35
|
+
clientId?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Pluggable session store. Defaults to `memoryStore()`. Use
|
|
38
|
+
* `localStorageStore()` in browsers for persistence across reloads.
|
|
39
|
+
*/
|
|
40
|
+
store?: SessionStore; /** Override the fetch implementation (e.g. for tests). Defaults to globalThis.fetch. */
|
|
41
|
+
fetch?: FetchLike; /** Per-request timeout in ms. Defaults to 15_000. */
|
|
42
|
+
timeoutMs?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Proactively refresh the access token before it expires. Pass `false`
|
|
45
|
+
* to disable; defaults to `{ leadTimeMs: 60_000 }`.
|
|
46
|
+
*/
|
|
47
|
+
proactiveRefresh?: ProactiveRefreshConfig;
|
|
48
|
+
/**
|
|
49
|
+
* Receives errors thrown by user code (e.g. session-change handlers).
|
|
50
|
+
* Defaults to `console.error`. Pass a no-op to silence.
|
|
51
|
+
*/
|
|
52
|
+
onError?: ErrorHandler;
|
|
53
|
+
};
|
|
54
|
+
type SubscribeOptions = {
|
|
55
|
+
/**
|
|
56
|
+
* If `true` (default), the handler is called synchronously during
|
|
57
|
+
* subscribe with the current session value. Set to `false` for strict
|
|
58
|
+
* future-changes-only semantics.
|
|
59
|
+
*/
|
|
60
|
+
immediate?: boolean;
|
|
61
|
+
};
|
|
62
|
+
/** Passwordless flow ("email_otp" provider). */
|
|
63
|
+
type EmailOtpNamespace = {
|
|
64
|
+
/**
|
|
65
|
+
* Send a 6-digit one-time code to the email. Used for passwordless
|
|
66
|
+
* login (existing accounts) or signup (creates a new account on verify).
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* const r = await auth.emailOtp.sendLoginCode({ email, captchaToken })
|
|
70
|
+
* // r = { flow: 'login'|'signup', methods: ['password','passwordless'],
|
|
71
|
+
* // otp: { expiresInSeconds, expiresAt, canResendInSeconds, resendsRemaining } }
|
|
72
|
+
*
|
|
73
|
+
* @throws CaptchaFailedError, RateLimitedError, InvalidEmailError, AuthError.
|
|
74
|
+
*/
|
|
75
|
+
sendLoginCode(input: SendLoginCodeInput): Promise<SendLoginCodeResult>;
|
|
76
|
+
/**
|
|
77
|
+
* Verify a passwordless login code and return an authenticated session.
|
|
78
|
+
*
|
|
79
|
+
* @throws OtpInvalidError ({ attemptsRemaining, canResendInSeconds }),
|
|
80
|
+
* OtpExpiredError, MaxAttemptsError ({ retryAfterSeconds }),
|
|
81
|
+
* NoPendingOtpError, RateLimitedError, AuthError.
|
|
82
|
+
*/
|
|
83
|
+
verifyLoginCode(input: VerifyLoginCodeInput): Promise<AuthSession>;
|
|
84
|
+
/**
|
|
85
|
+
* Resend the verification code for a pending passwordless OTP.
|
|
86
|
+
* Subject to RESEND_COOLDOWN_SECONDS and MAX_RESENDS_PER_OTP.
|
|
87
|
+
*
|
|
88
|
+
* @throws ResendCooldownError, MaxResendsError, NoPendingOtpError,
|
|
89
|
+
* CaptchaFailedError, RateLimitedError, AuthError.
|
|
90
|
+
*/
|
|
91
|
+
resendLoginCode(input: ResendLoginCodeInput): Promise<ResendCodeResult>;
|
|
92
|
+
};
|
|
93
|
+
/** Password flow ("email_password" provider). */
|
|
94
|
+
type EmailPasswordNamespace = {
|
|
95
|
+
/**
|
|
96
|
+
* Begin email+password registration. The bcrypt hash is held server-side
|
|
97
|
+
* with the OTP record until the user verifies.
|
|
98
|
+
*
|
|
99
|
+
* @throws EmailAlreadyHasPasswordError, InvalidPasswordError,
|
|
100
|
+
* CaptchaFailedError, RateLimitedError, AuthError.
|
|
101
|
+
*/
|
|
102
|
+
register(input: RegisterInput): Promise<RegisterResult>;
|
|
103
|
+
/**
|
|
104
|
+
* Verify the password-registration code, finalize signup, and return a session.
|
|
105
|
+
*/
|
|
106
|
+
verifyRegistrationCode(input: VerifyRegistrationCodeInput): Promise<AuthSession>; /** Resend the verification code for a pending password registration. */
|
|
107
|
+
resendRegistrationCode(input: ResendRegistrationCodeInput): Promise<ResendCodeResult>;
|
|
108
|
+
/**
|
|
109
|
+
* Authenticate with email + password.
|
|
110
|
+
*
|
|
111
|
+
* @throws InvalidCredentialsError, NoPasswordSetError,
|
|
112
|
+
* AccountSuspendedError, CaptchaFailedError, AuthError.
|
|
113
|
+
*/
|
|
114
|
+
login(input: LoginWithPasswordInput): Promise<AuthSession>; /** Set a password on an existing account (auth required). */
|
|
115
|
+
setPassword(input: SetPasswordInput): Promise<void>; /** Rotate password (auth required); revokes other sessions on success. */
|
|
116
|
+
changePassword(input: ChangePasswordInput): Promise<void>;
|
|
117
|
+
};
|
|
118
|
+
/** Google flow. */
|
|
119
|
+
type GoogleNamespace = {
|
|
120
|
+
login(input: LoginWithGoogleInput): Promise<AuthSession>;
|
|
121
|
+
};
|
|
122
|
+
type AuthClient = {
|
|
123
|
+
emailOtp: EmailOtpNamespace;
|
|
124
|
+
emailPassword: EmailPasswordNamespace;
|
|
125
|
+
google: GoogleNamespace;
|
|
126
|
+
refresh(): Promise<AuthSession>;
|
|
127
|
+
logout(): Promise<void>;
|
|
128
|
+
getSession(): AuthSession | null;
|
|
129
|
+
isAuthenticated(): boolean;
|
|
130
|
+
loadSession(): Promise<AuthSession | null>;
|
|
131
|
+
onSessionChange(handler: SessionChangeHandler, opts?: SubscribeOptions): () => void;
|
|
132
|
+
/**
|
|
133
|
+
* Fetch a consumer URL with `Authorization: Bearer <accessToken>`
|
|
134
|
+
* automatically attached. See AuthFetchInit for `requireAuth` and
|
|
135
|
+
* `disableRefresh` semantics.
|
|
136
|
+
*/
|
|
137
|
+
fetch(input: RequestInfo | URL, init?: AuthFetchInit): Promise<Response>; /** Close cross-tab BroadcastChannel listener. Optional. */
|
|
138
|
+
close(): void;
|
|
139
|
+
};
|
|
140
|
+
declare function createAuthClient(options: CreateAuthClientOptions): AuthClient;
|
|
141
|
+
//#endregion
|
|
142
|
+
export { GoogleNamespace as a, createAuthClient as c, EmailPasswordNamespace as i, AuthFetchInit as l, CreateAuthClientOptions as n, ProactiveRefreshConfig as o, EmailOtpNamespace as r, SubscribeOptions as s, AuthClient as t };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { a as OtpInfo, c as ResendCodeResult, i as ErrorSource, l as SendLoginCodeResult, n as AuthSession, o as Project, r as ErrorHandler, s as RegisterResult, t as Account, u as SessionChangeHandler } from "./session-Cs_P7ojF.js";
|
|
2
|
+
import { a as GoogleNamespace, c as createAuthClient, i as EmailPasswordNamespace, l as AuthFetchInit, n as CreateAuthClientOptions, o as ProactiveRefreshConfig, r as EmailOtpNamespace, s as SubscribeOptions, t as AuthClient } from "./index-G-gqveGn.js";
|
|
3
|
+
import { i as memoryStore, n as SessionStore, r as localStorageStore, t as LocalStorageStoreOptions } from "./sessionStore-BDdEpbL8.js";
|
|
4
|
+
import { a as RegisterInput, c as SendLoginCodeInput, d as VerifyRegistrationCodeInput, i as LoginWithPasswordInput, l as SetPasswordInput, n as ChangePasswordInput, o as ResendLoginCodeInput, r as LoginWithGoogleInput, s as ResendRegistrationCodeInput, t as CaptchaArgs, u as VerifyLoginCodeInput } from "./requests-eTORIjY5.js";
|
|
5
|
+
import { _ as authErrorFromFetchFailure, a as EmailAlreadyHasPasswordError, c as InvalidPasswordError, d as NoPasswordSetError, f as NoPendingOtpError, g as ResendCooldownError, h as RateLimitedError, i as CaptchaFailedError, l as MaxAttemptsError, m as OtpInvalidError, n as AuthError, o as InvalidCredentialsError, p as OtpExpiredError, r as AuthErrorInit, s as InvalidEmailError, t as AccountSuspendedError, u as MaxResendsError, v as authErrorFromResponse } from "./authError-C5g5jP5l.js";
|
|
6
|
+
import { n as AccessTokenPayloadProject, t as AccessTokenPayload } from "./tokens-BXrPLi5B.js";
|
|
7
|
+
export { type AccessTokenPayload, type AccessTokenPayloadProject, type Account, AccountSuspendedError, type AuthClient, AuthError, type AuthErrorInit, type AuthFetchInit, type AuthSession, type CaptchaArgs, CaptchaFailedError, type ChangePasswordInput, type CreateAuthClientOptions, EmailAlreadyHasPasswordError, type EmailOtpNamespace, type EmailPasswordNamespace, type ErrorHandler, type ErrorSource, type GoogleNamespace, InvalidCredentialsError, InvalidEmailError, InvalidPasswordError, type LocalStorageStoreOptions, type LoginWithGoogleInput, type LoginWithPasswordInput, MaxAttemptsError, MaxResendsError, NoPasswordSetError, NoPendingOtpError, OtpExpiredError, type OtpInfo, OtpInvalidError, type ProactiveRefreshConfig, type Project, RateLimitedError, type RegisterInput, type RegisterResult, type ResendCodeResult, ResendCooldownError, type ResendLoginCodeInput, type ResendRegistrationCodeInput, type SendLoginCodeInput, type SendLoginCodeResult, type SessionChangeHandler, type SessionStore, type SetPasswordInput, type SubscribeOptions, type VerifyLoginCodeInput, type VerifyRegistrationCodeInput, authErrorFromFetchFailure, authErrorFromResponse, createAuthClient, localStorageStore, memoryStore };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { t as createAuthClient } from "./client-BcyoQ5Cj.js";
|
|
2
|
+
import { _ as authErrorFromResponse, a as InvalidCredentialsError, c as MaxAttemptsError, d as NoPendingOtpError, f as OtpExpiredError, g as authErrorFromFetchFailure, h as ResendCooldownError, i as EmailAlreadyHasPasswordError, l as MaxResendsError, m as RateLimitedError, n as AuthError, o as InvalidEmailError, p as OtpInvalidError, r as CaptchaFailedError, s as InvalidPasswordError, t as AccountSuspendedError, u as NoPasswordSetError } from "./authError-DbEZJnC4.js";
|
|
3
|
+
import { n as memoryStore, t as localStorageStore } from "./sessionStore-DD6lON9W.js";
|
|
4
|
+
export { AccountSuspendedError, AuthError, CaptchaFailedError, EmailAlreadyHasPasswordError, InvalidCredentialsError, InvalidEmailError, InvalidPasswordError, MaxAttemptsError, MaxResendsError, NoPasswordSetError, NoPendingOtpError, OtpExpiredError, OtpInvalidError, RateLimitedError, ResendCooldownError, authErrorFromFetchFailure, authErrorFromResponse, createAuthClient, localStorageStore, memoryStore };
|