@mitway/sdk 0.4.0 → 0.5.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/dist/index.cjs CHANGED
@@ -199,6 +199,7 @@ var Logger = class {
199
199
 
200
200
  // src/lib/token-manager.ts
201
201
  var CSRF_TOKEN_COOKIE = "mitway_baas_csrf_token";
202
+ var DEFAULT_STORAGE_KEY = "mitway_baas_session";
202
203
  function getCsrfToken() {
203
204
  if (typeof document === "undefined") return null;
204
205
  const match = document.cookie.split(";").find((c) => c.trim().startsWith(`${CSRF_TOKEN_COOKIE}=`));
@@ -218,13 +219,24 @@ function clearCsrfToken() {
218
219
  }
219
220
  var TokenManager = class {
220
221
  accessToken = null;
222
+ refreshToken = null;
221
223
  user = null;
224
+ persistSession;
225
+ storageKey;
222
226
  /** Fired when the access token changes (used by long-lived consumers). */
223
227
  onTokenChange = null;
228
+ constructor(opts) {
229
+ this.persistSession = opts?.persistSession ?? true;
230
+ this.storageKey = opts?.storageKey ?? DEFAULT_STORAGE_KEY;
231
+ }
224
232
  saveSession(session) {
225
233
  const tokenChanged = session.accessToken !== this.accessToken;
226
234
  this.accessToken = session.accessToken;
227
235
  this.user = session.user;
236
+ if (session.refreshToken !== void 0) {
237
+ this.refreshToken = session.refreshToken ?? null;
238
+ }
239
+ this.persist();
228
240
  if (tokenChanged && this.onTokenChange) {
229
241
  this.onTokenChange();
230
242
  }
@@ -233,6 +245,7 @@ var TokenManager = class {
233
245
  if (!this.accessToken || !this.user) return null;
234
246
  return {
235
247
  accessToken: this.accessToken,
248
+ refreshToken: this.refreshToken ?? void 0,
236
249
  user: this.user
237
250
  };
238
251
  }
@@ -242,24 +255,76 @@ var TokenManager = class {
242
255
  setAccessToken(token) {
243
256
  const tokenChanged = token !== this.accessToken;
244
257
  this.accessToken = token;
258
+ this.persist();
245
259
  if (tokenChanged && this.onTokenChange) {
246
260
  this.onTokenChange();
247
261
  }
248
262
  }
263
+ getRefreshToken() {
264
+ return this.refreshToken;
265
+ }
266
+ setRefreshToken(token) {
267
+ this.refreshToken = token;
268
+ this.persist();
269
+ }
249
270
  getUser() {
250
271
  return this.user;
251
272
  }
252
273
  setUser(user) {
253
274
  this.user = user;
275
+ this.persist();
254
276
  }
255
277
  clearSession() {
256
278
  const hadToken = this.accessToken !== null;
257
279
  this.accessToken = null;
280
+ this.refreshToken = null;
258
281
  this.user = null;
282
+ this.removePersisted();
259
283
  if (hadToken && this.onTokenChange) {
260
284
  this.onTokenChange();
261
285
  }
262
286
  }
287
+ /**
288
+ * Restore the session from localStorage. Returns true if a persisted
289
+ * session was found and loaded into memory.
290
+ */
291
+ restoreSession() {
292
+ if (!this.persistSession || typeof localStorage === "undefined") return false;
293
+ try {
294
+ const raw = localStorage.getItem(this.storageKey);
295
+ if (!raw) return false;
296
+ const stored = JSON.parse(raw);
297
+ if (!stored.accessToken || !stored.user) return false;
298
+ this.accessToken = stored.accessToken;
299
+ this.refreshToken = stored.refreshToken ?? null;
300
+ this.user = stored.user;
301
+ return true;
302
+ } catch {
303
+ return false;
304
+ }
305
+ }
306
+ persist() {
307
+ if (!this.persistSession || typeof localStorage === "undefined") return;
308
+ if (!this.accessToken || !this.user) return;
309
+ try {
310
+ const data = {
311
+ accessToken: this.accessToken,
312
+ user: this.user
313
+ };
314
+ if (this.refreshToken) {
315
+ data.refreshToken = this.refreshToken;
316
+ }
317
+ localStorage.setItem(this.storageKey, JSON.stringify(data));
318
+ } catch {
319
+ }
320
+ }
321
+ removePersisted() {
322
+ if (!this.persistSession || typeof localStorage === "undefined") return;
323
+ try {
324
+ localStorage.removeItem(this.storageKey);
325
+ } catch {
326
+ }
327
+ }
263
328
  };
264
329
 
265
330
  // src/lib/auth-envelope.ts
@@ -679,6 +744,7 @@ var Auth = class {
679
744
  saveSessionFromResponse(response) {
680
745
  const session = {
681
746
  accessToken: response.accessToken,
747
+ refreshToken: response.refreshToken,
682
748
  user: response.user
683
749
  };
684
750
  if (response.csrfToken) {
@@ -772,6 +838,70 @@ var Auth = class {
772
838
  return wrapError(error, "Session refresh failed");
773
839
  }
774
840
  }
841
+ /**
842
+ * Restore the session from localStorage and validate it with the backend.
843
+ * Call this once on app startup (e.g. in a React AuthProvider useEffect).
844
+ *
845
+ * Flow:
846
+ * 1. Read persisted session from localStorage.
847
+ * 2. Populate in-memory state (TokenManager + HttpClient).
848
+ * 3. Validate with `GET /api/auth/sessions/current`.
849
+ * - If the access token expired, the HttpClient auto-refresh kicks in
850
+ * using the persisted refresh token (sent in the POST body, not
851
+ * cookies — works cross-site).
852
+ * 4. Return the validated user or an error.
853
+ *
854
+ * If no persisted session exists, returns `{ data: null, error }` — the
855
+ * app should show the login page.
856
+ */
857
+ async initialize() {
858
+ const restored = this.tokenManager.restoreSession();
859
+ if (!restored) {
860
+ return {
861
+ data: null,
862
+ error: new MitwayBaasError("No persisted session", 0, "NO_SESSION")
863
+ };
864
+ }
865
+ const session = this.tokenManager.getSession();
866
+ if (!session) {
867
+ return {
868
+ data: null,
869
+ error: new MitwayBaasError("No persisted session", 0, "NO_SESSION")
870
+ };
871
+ }
872
+ this.http.setAuthToken(session.accessToken);
873
+ const refreshToken = this.tokenManager.getRefreshToken();
874
+ if (refreshToken) {
875
+ this.http.setRefreshToken(refreshToken);
876
+ }
877
+ try {
878
+ const response = await this.http.get(
879
+ "/api/auth/sessions/current"
880
+ );
881
+ if (response?.user) {
882
+ this.tokenManager.setUser(response.user);
883
+ return {
884
+ data: {
885
+ user: response.user,
886
+ accessToken: session.accessToken
887
+ },
888
+ error: null
889
+ };
890
+ }
891
+ this.tokenManager.clearSession();
892
+ this.http.setAuthToken(null);
893
+ this.http.setRefreshToken(null);
894
+ return {
895
+ data: null,
896
+ error: new MitwayBaasError("Invalid session", 401, "INVALID_SESSION")
897
+ };
898
+ } catch (error) {
899
+ this.tokenManager.clearSession();
900
+ this.http.setAuthToken(null);
901
+ this.http.setRefreshToken(null);
902
+ return wrapError(error, "Session restore failed");
903
+ }
904
+ }
775
905
  /**
776
906
  * Get the current in-memory session, or null if the user is not signed in.
777
907
  * Synchronous — does not hit the network.
@@ -1915,7 +2045,10 @@ var MitwayBaasClient = class {
1915
2045
  storage;
1916
2046
  constructor(config = {}) {
1917
2047
  const logger = new Logger(config.debug);
1918
- this.tokenManager = new TokenManager();
2048
+ this.tokenManager = new TokenManager({
2049
+ persistSession: config.persistSession,
2050
+ storageKey: config.storageKey
2051
+ });
1919
2052
  this.http = new HttpClient(config, this.tokenManager, logger);
1920
2053
  this.auth = new Auth(this.http, this.tokenManager);
1921
2054
  this.database = new Database(this.http, this.tokenManager, config.anonKey);