Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

@toruslabs/session-manager

Package Overview
Dependencies
Maintainers
5
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@toruslabs/session-manager - npm Package Compare versions

Comparing version
5.0.0
to
5.1.0
.github/CODEOWNERS

Sorry, the diff of this file is not supported yet

+49
'use strict';
var _defineProperty = require('@babel/runtime/helpers/defineProperty');
class LocalStorageHandler {
constructor(storage, baseStorageKey) {
_defineProperty(this, "storage", void 0);
_defineProperty(this, "baseStorageKey", void 0);
this.storage = storage;
this.baseStorageKey = baseStorageKey;
}
async storeData(key, data) {
const storageKey = this.getStorageKey(key);
this.storage.setItem(storageKey, JSON.stringify(data));
}
async retrieveData(key) {
const storageKey = this.getStorageKey(key);
const localData = this.storage.getItem(storageKey);
if (!localData) return undefined;
try {
return JSON.parse(localData);
} catch {
// Corrupted cache should not block retrieval.
this.storage.removeItem(storageKey);
return undefined;
}
}
clearStorage(key) {
this.storage.removeItem(this.getStorageKey(key));
}
clearOrphanedData(baseKey = "") {
const keyPrefix = `${this.baseStorageKey}${baseKey}`;
const keysToRemove = [];
for (let index = 0; index < this.storage.length; index += 1) {
const storageKey = this.storage.key(index);
if (storageKey && storageKey.startsWith(keyPrefix)) {
keysToRemove.push(storageKey);
}
}
keysToRemove.forEach(key => {
this.storage.removeItem(key);
});
}
getStorageKey(key) {
return `${this.baseStorageKey}${key}`;
}
}
exports.LocalStorageHandler = LocalStorageHandler;
'use strict';
var _defineProperty = require('@babel/runtime/helpers/defineProperty');
var eccrypto = require('@toruslabs/eccrypto');
var metadataHelpers = require('@toruslabs/metadata-helpers');
class ServerHandler {
constructor(sessionServerBaseUrl, request) {
_defineProperty(this, "sessionServerBaseUrl", void 0);
_defineProperty(this, "request", void 0);
this.sessionServerBaseUrl = sessionServerBaseUrl;
this.request = request;
}
async storeData(key, data, options = {}) {
var _options$operation;
const privKey = metadataHelpers.hexToBytes(key);
const pubKey = metadataHelpers.bytesToHex(eccrypto.getPublic(privKey));
const encData = await metadataHelpers.encryptData(key, data);
const signature = metadataHelpers.bytesToHex(await eccrypto.sign(privKey, metadataHelpers.keccak256(metadataHelpers.utf8ToBytes(encData))));
const body = {
key: pubKey,
data: encData,
signature,
namespace: options.namespace,
allowedOrigin: options.allowedOrigin
};
const operation = (_options$operation = options.operation) !== null && _options$operation !== void 0 ? _options$operation : "create";
if (operation === "update") {
await this.request({
method: "PUT",
url: `${this.sessionServerBaseUrl}/v2/store/update`,
data: body,
headers: options.headers
});
return;
}
if (operation === "create") {
body.timeout = options.timeout;
} else if (operation === "invalidate") {
body.timeout = 1;
}
await this.request({
method: "POST",
url: `${this.sessionServerBaseUrl}/v2/store/set`,
data: body,
headers: options.headers
});
}
async retrieveData(key, options = {}) {
const pubKey = metadataHelpers.bytesToHex(eccrypto.getPublic(metadataHelpers.hexToBytes(key)));
const body = {
key: pubKey,
namespace: options.namespace
};
const result = await this.request({
method: "POST",
url: `${this.sessionServerBaseUrl}/v2/store/get`,
data: body,
headers: options.headers
});
if (!result.message) {
throw new Error("Session Expired or Invalid public key");
}
const response = await metadataHelpers.decryptData(key, result.message);
if (response.error) {
throw new Error("There was an error decrypting data.");
}
return response;
}
clearStorage(_key) {
// No-op. Server entries expire based on timeout.
}
clearOrphanedData(_baseKey = "") {
// No-op. Server entries expire based on timeout.
}
}
exports.ServerHandler = ServerHandler;
export * from "./localStorageHandler";
export * from "./serverHandler";
import type { StorageHandler } from "../interfaces";
export declare class LocalStorageHandler<T> implements StorageHandler<T> {
private readonly storage;
private readonly baseStorageKey;
constructor(storage: Storage, baseStorageKey: string);
storeData(key: string, data: Partial<T> | T | Record<string, never>): Promise<void>;
retrieveData(key: string): Promise<T | undefined>;
clearStorage(key: string): void;
clearOrphanedData(baseKey?: string): void;
private getStorageKey;
}
import type { ApiRequestParams, StorageHandler, StorageHandlerRetrieveOptions, StorageHandlerStoreOptions } from "../interfaces";
type RequestFn = <T>(params: ApiRequestParams) => Promise<T>;
export declare class ServerHandler<T> implements StorageHandler<T> {
private readonly sessionServerBaseUrl;
private readonly request;
constructor(sessionServerBaseUrl: string, request: RequestFn);
storeData(key: string, data: Partial<T> | T | Record<string, never>, options?: StorageHandlerStoreOptions): Promise<void>;
retrieveData(key: string, options?: StorageHandlerRetrieveOptions): Promise<T | undefined>;
clearStorage(_key: string): void;
clearOrphanedData(_baseKey?: string): void;
}
export {};
import _defineProperty from '@babel/runtime/helpers/defineProperty';
class LocalStorageHandler {
constructor(storage, baseStorageKey) {
_defineProperty(this, "storage", void 0);
_defineProperty(this, "baseStorageKey", void 0);
this.storage = storage;
this.baseStorageKey = baseStorageKey;
}
async storeData(key, data) {
const storageKey = this.getStorageKey(key);
this.storage.setItem(storageKey, JSON.stringify(data));
}
async retrieveData(key) {
const storageKey = this.getStorageKey(key);
const localData = this.storage.getItem(storageKey);
if (!localData) return undefined;
try {
return JSON.parse(localData);
} catch {
// Corrupted cache should not block retrieval.
this.storage.removeItem(storageKey);
return undefined;
}
}
clearStorage(key) {
this.storage.removeItem(this.getStorageKey(key));
}
clearOrphanedData(baseKey = "") {
const keyPrefix = `${this.baseStorageKey}${baseKey}`;
const keysToRemove = [];
for (let index = 0; index < this.storage.length; index += 1) {
const storageKey = this.storage.key(index);
if (storageKey && storageKey.startsWith(keyPrefix)) {
keysToRemove.push(storageKey);
}
}
keysToRemove.forEach(key => {
this.storage.removeItem(key);
});
}
getStorageKey(key) {
return `${this.baseStorageKey}${key}`;
}
}
export { LocalStorageHandler };
import _defineProperty from '@babel/runtime/helpers/defineProperty';
import { getPublic, sign } from '@toruslabs/eccrypto';
import { hexToBytes, bytesToHex, encryptData, keccak256, utf8ToBytes, decryptData } from '@toruslabs/metadata-helpers';
class ServerHandler {
constructor(sessionServerBaseUrl, request) {
_defineProperty(this, "sessionServerBaseUrl", void 0);
_defineProperty(this, "request", void 0);
this.sessionServerBaseUrl = sessionServerBaseUrl;
this.request = request;
}
async storeData(key, data, options = {}) {
var _options$operation;
const privKey = hexToBytes(key);
const pubKey = bytesToHex(getPublic(privKey));
const encData = await encryptData(key, data);
const signature = bytesToHex(await sign(privKey, keccak256(utf8ToBytes(encData))));
const body = {
key: pubKey,
data: encData,
signature,
namespace: options.namespace,
allowedOrigin: options.allowedOrigin
};
const operation = (_options$operation = options.operation) !== null && _options$operation !== void 0 ? _options$operation : "create";
if (operation === "update") {
await this.request({
method: "PUT",
url: `${this.sessionServerBaseUrl}/v2/store/update`,
data: body,
headers: options.headers
});
return;
}
if (operation === "create") {
body.timeout = options.timeout;
} else if (operation === "invalidate") {
body.timeout = 1;
}
await this.request({
method: "POST",
url: `${this.sessionServerBaseUrl}/v2/store/set`,
data: body,
headers: options.headers
});
}
async retrieveData(key, options = {}) {
const pubKey = bytesToHex(getPublic(hexToBytes(key)));
const body = {
key: pubKey,
namespace: options.namespace
};
const result = await this.request({
method: "POST",
url: `${this.sessionServerBaseUrl}/v2/store/get`,
data: body,
headers: options.headers
});
if (!result.message) {
throw new Error("Session Expired or Invalid public key");
}
const response = await decryptData(key, result.message);
if (response.error) {
throw new Error("There was an error decrypting data.");
}
return response;
}
clearStorage(_key) {
// No-op. Server entries expire based on timeout.
}
clearOrphanedData(_baseKey = "") {
// No-op. Server entries expire based on timeout.
}
}
export { ServerHandler };
+8
-0
'use strict';
var base = require('./base.js');
var localStorageHandler = require('./handlers/localStorageHandler.js');
var serverHandler = require('./handlers/serverHandler.js');
var sessionManager = require('./sessionManager.js');
var util = require('./util.js');

@@ -9,2 +12,7 @@

exports.BaseSessionManager = base.BaseSessionManager;
exports.LocalStorageHandler = localStorageHandler.LocalStorageHandler;
exports.ServerHandler = serverHandler.ServerHandler;
exports.SessionManager = sessionManager.SessionManager;
exports.STORAGE_METHOD = util.STORAGE_METHOD;
exports.padHexString = util.padHexString;
exports.storageAvailable = util.storageAvailable;
+67
-57

@@ -8,2 +8,4 @@ 'use strict';

var base = require('./base.js');
var localStorageHandler = require('./handlers/localStorageHandler.js');
var serverHandler = require('./handlers/serverHandler.js');
var util = require('./util.js');

@@ -18,3 +20,4 @@

sessionId,
allowedOrigin
allowedOrigin,
useLocalStorage
} = {}) {

@@ -27,2 +30,5 @@ super();

_defineProperty(this, "sessionId", "");
_defineProperty(this, "serverHandler", void 0);
_defineProperty(this, "useLocalStorage", void 0);
_defineProperty(this, "localStorageHandler", void 0);
if (sessionServerBaseUrl) {

@@ -39,26 +45,25 @@ this.sessionServerBaseUrl = sessionServerBaseUrl;

}
this.useLocalStorage = Boolean(useLocalStorage);
this.serverHandler = new serverHandler.ServerHandler(this.sessionServerBaseUrl, params => super.request(params));
}
get baseLocalStorageKey() {
var _this$sessionNamespac;
return `${(_this$sessionNamespac = this.sessionNamespace) !== null && _this$sessionNamespac !== void 0 ? _this$sessionNamespac : "w3a_session_manager_default"}:`;
}
static generateRandomSessionKey() {
return util.padHexString(metadataHelpers.bytesToHex(eccrypto.generatePrivate()));
}
setSessionId(sessionId) {
this.sessionId = util.padHexString(sessionId);
}
async createSession(data, headers = {}) {
super.checkSessionParams();
const privKey = metadataHelpers.hexToBytes(this.sessionId);
const pubKey = metadataHelpers.bytesToHex(eccrypto.getPublic(privKey));
const encData = await metadataHelpers.encryptData(this.sessionId, data);
const signature = metadataHelpers.bytesToHex(await eccrypto.sign(privKey, metadataHelpers.keccak256(metadataHelpers.utf8ToBytes(encData))));
const body = {
key: pubKey,
data: encData,
signature,
await this.serverHandler.storeData(this.sessionId, data, {
operation: "create",
namespace: this.sessionNamespace,
timeout: this.sessionTime,
allowedOrigin: this.allowedOrigin
};
await super.request({
method: "POST",
url: `${this.sessionServerBaseUrl}/v2/store/set`,
data: body,
allowedOrigin: this.allowedOrigin,
headers
});
await this.safeLocalStorageOp(h => h.storeData(this.sessionId, data));
return this.sessionId;

@@ -72,20 +77,14 @@ }

super.checkSessionParams();
const pubkey = metadataHelpers.bytesToHex(eccrypto.getPublic(metadataHelpers.hexToBytes(this.sessionId)));
const body = {
key: pubkey,
namespace: this.sessionNamespace
};
const result = await super.request({
method: "POST",
url: `${this.sessionServerBaseUrl}/v2/store/get`,
data: body,
const localData = await this.safeLocalStorageOp(h => h.retrieveData(this.sessionId));
if (localData !== undefined) {
return localData;
}
const response = await this.serverHandler.retrieveData(this.sessionId, {
namespace: this.sessionNamespace,
headers
});
if (!result.message) {
if (response === undefined) {
throw new Error("Session Expired or Invalid public key");
}
const response = await metadataHelpers.decryptData(this.sessionId, result.message);
if (response.error) {
throw new Error("There was an error decrypting data.");
}
await this.safeLocalStorageOp(h => h.storeData(this.sessionId, response));
return response;

@@ -95,44 +94,55 @@ }

super.checkSessionParams();
const privKey = metadataHelpers.hexToBytes(this.sessionId);
const pubKey = metadataHelpers.bytesToHex(eccrypto.getPublic(privKey));
const encData = await metadataHelpers.encryptData(this.sessionId, data);
const signature = metadataHelpers.bytesToHex(await eccrypto.sign(privKey, metadataHelpers.keccak256(metadataHelpers.utf8ToBytes(encData))));
const body = {
key: pubKey,
data: encData,
signature,
await this.serverHandler.storeData(this.sessionId, data, {
operation: "update",
namespace: this.sessionNamespace,
allowedOrigin: this.allowedOrigin
};
await super.request({
method: "PUT",
url: `${this.sessionServerBaseUrl}/v2/store/update`,
data: body,
allowedOrigin: this.allowedOrigin,
headers
});
await this.safeLocalStorageOp(h => h.storeData(this.sessionId, data));
}
async invalidateSession(headers = {}) {
super.checkSessionParams();
const privKey = metadataHelpers.hexToBytes(this.sessionId);
const pubKey = metadataHelpers.bytesToHex(eccrypto.getPublic(privKey));
const encData = await metadataHelpers.encryptData(this.sessionId, {});
const signature = metadataHelpers.bytesToHex(await eccrypto.sign(privKey, metadataHelpers.keccak256(metadataHelpers.utf8ToBytes(encData))));
const data = {
key: pubKey,
data: encData,
signature,
await this.serverHandler.storeData(this.sessionId, {}, {
operation: "invalidate",
namespace: this.sessionNamespace,
timeout: 1
};
await super.request({
method: "POST",
url: `${this.sessionServerBaseUrl}/v2/store/set`,
data,
headers
});
this.clearStorage();
this.sessionId = "";
return true;
}
clearStorage() {
super.checkSessionParams();
const localStorageHandler = this.getLocalStorageHandler();
if (!localStorageHandler) return;
localStorageHandler.clearStorage(this.sessionId);
}
clearOrphanedData(baseKey) {
const localStorageHandler = this.getLocalStorageHandler();
if (!localStorageHandler) return;
localStorageHandler.clearOrphanedData(baseKey);
}
/**
* Runs a local-storage operation, swallowing any errors so that failures
* like QuotaExceededError never break the primary server-backed flow.
*/
async safeLocalStorageOp(fn) {
try {
const handler = this.getLocalStorageHandler();
if (!handler) return undefined;
return await fn(handler);
} catch {
return undefined;
}
}
getLocalStorageHandler() {
if (!this.useLocalStorage) return undefined;
if (typeof window === "undefined") return undefined;
if (this.localStorageHandler) return this.localStorageHandler;
if (!util.storageAvailable(util.STORAGE_METHOD.LOCAL_STORAGE)) return undefined;
this.localStorageHandler = new localStorageHandler.LocalStorageHandler(window[util.STORAGE_METHOD.LOCAL_STORAGE], this.baseLocalStorageKey);
return this.localStorageHandler;
}
}
exports.SessionManager = SessionManager;
export * from "./base";
export * from "./handlers";
export * from "./interfaces";
export * from "./sessionManager";
export * from "./util";

@@ -18,2 +18,20 @@ export type IRequestBody = Record<string, unknown>;

};
export type StorageOperation = "create" | "update" | "invalidate";
export interface StorageHandlerStoreOptions {
headers?: RequestInit["headers"];
namespace?: string;
timeout?: number;
allowedOrigin?: string | boolean;
operation?: StorageOperation;
}
export interface StorageHandlerRetrieveOptions {
headers?: RequestInit["headers"];
namespace?: string;
}
export interface StorageHandler<T> {
storeData(key: string, data: Partial<T> | T | Record<string, never>, options?: StorageHandlerStoreOptions): Promise<void>;
retrieveData(key: string, options?: StorageHandlerRetrieveOptions): Promise<T | undefined>;
clearStorage(key: string): void | Promise<void>;
clearOrphanedData(baseKey?: string): void | Promise<void>;
}
export interface SessionManagerOptions {

@@ -25,2 +43,3 @@ sessionServerBaseUrl?: string;

allowedOrigin?: string | boolean;
useLocalStorage?: boolean;
}

@@ -27,0 +46,0 @@ export interface SessionRequestBody extends IRequestBody {

@@ -9,4 +9,9 @@ import { BaseSessionManager } from "./base";

sessionId: string;
constructor({ sessionServerBaseUrl, sessionNamespace, sessionTime, sessionId, allowedOrigin }?: SessionManagerOptions);
private serverHandler;
private readonly useLocalStorage;
private localStorageHandler?;
constructor({ sessionServerBaseUrl, sessionNamespace, sessionTime, sessionId, allowedOrigin, useLocalStorage }?: SessionManagerOptions);
private get baseLocalStorageKey();
static generateRandomSessionKey(): string;
setSessionId(sessionId: string): void;
createSession(data: T, headers?: RequestInit["headers"]): Promise<string>;

@@ -18,2 +23,10 @@ authorizeSession({ headers }?: {

invalidateSession(headers?: RequestInit["headers"]): Promise<boolean>;
clearStorage(): void;
clearOrphanedData(baseKey?: string): void;
/**
* Runs a local-storage operation, swallowing any errors so that failures
* like QuotaExceededError never break the primary server-backed flow.
*/
private safeLocalStorageOp;
private getLocalStorageHandler;
}
export declare const padHexString: (hexString: string) => string;
export declare const STORAGE_METHOD: {
readonly LOCAL_STORAGE: "localStorage";
};
export type StorageMethod = (typeof STORAGE_METHOD)[keyof typeof STORAGE_METHOD];
export declare function storageAvailable(type: StorageMethod): boolean;

@@ -6,3 +6,32 @@ 'use strict';

};
const STORAGE_METHOD = {
LOCAL_STORAGE: "localStorage"
};
function storageAvailable(type) {
let storage;
try {
storage = window[type];
const x = "__storage_test__";
storage.setItem(x, x);
storage.removeItem(x);
return true;
} catch (error) {
const e = error;
return e && (
// everything except Firefox
e.code === 22 ||
// Firefox
e.code === 1014 ||
// test name field too, because code might not be present
// everything except Firefox
e.name === "QuotaExceededError" ||
// Firefox
e.name === "NS_ERROR_DOM_QUOTA_REACHED") &&
// acknowledge QuotaExceededError only if there's something already stored
storage && storage.length !== 0;
}
}
exports.STORAGE_METHOD = STORAGE_METHOD;
exports.padHexString = padHexString;
exports.storageAvailable = storageAvailable;
export { BaseSessionManager } from './base.js';
export { SessionManager } from './sessionManager.js';
export { STORAGE_METHOD, padHexString, storageAvailable } from './util.js';
export { LocalStorageHandler } from './handlers/localStorageHandler.js';
export { ServerHandler } from './handlers/serverHandler.js';
import _defineProperty from '@babel/runtime/helpers/defineProperty';
import { SESSION_SERVER_API_URL } from '@toruslabs/constants';
import { generatePrivate, getPublic, sign } from '@toruslabs/eccrypto';
import { bytesToHex, hexToBytes, encryptData, keccak256, utf8ToBytes, decryptData } from '@toruslabs/metadata-helpers';
import { generatePrivate } from '@toruslabs/eccrypto';
import { bytesToHex } from '@toruslabs/metadata-helpers';
import { BaseSessionManager } from './base.js';
import { padHexString } from './util.js';
import { LocalStorageHandler } from './handlers/localStorageHandler.js';
import { ServerHandler } from './handlers/serverHandler.js';
import { padHexString, storageAvailable, STORAGE_METHOD } from './util.js';

@@ -15,3 +17,4 @@ const DEFAULT_SESSION_TIMEOUT = 86400;

sessionId,
allowedOrigin
allowedOrigin,
useLocalStorage
} = {}) {

@@ -24,2 +27,5 @@ super();

_defineProperty(this, "sessionId", "");
_defineProperty(this, "serverHandler", void 0);
_defineProperty(this, "useLocalStorage", void 0);
_defineProperty(this, "localStorageHandler", void 0);
if (sessionServerBaseUrl) {

@@ -36,26 +42,25 @@ this.sessionServerBaseUrl = sessionServerBaseUrl;

}
this.useLocalStorage = Boolean(useLocalStorage);
this.serverHandler = new ServerHandler(this.sessionServerBaseUrl, params => super.request(params));
}
get baseLocalStorageKey() {
var _this$sessionNamespac;
return `${(_this$sessionNamespac = this.sessionNamespace) !== null && _this$sessionNamespac !== void 0 ? _this$sessionNamespac : "w3a_session_manager_default"}:`;
}
static generateRandomSessionKey() {
return padHexString(bytesToHex(generatePrivate()));
}
setSessionId(sessionId) {
this.sessionId = padHexString(sessionId);
}
async createSession(data, headers = {}) {
super.checkSessionParams();
const privKey = hexToBytes(this.sessionId);
const pubKey = bytesToHex(getPublic(privKey));
const encData = await encryptData(this.sessionId, data);
const signature = bytesToHex(await sign(privKey, keccak256(utf8ToBytes(encData))));
const body = {
key: pubKey,
data: encData,
signature,
await this.serverHandler.storeData(this.sessionId, data, {
operation: "create",
namespace: this.sessionNamespace,
timeout: this.sessionTime,
allowedOrigin: this.allowedOrigin
};
await super.request({
method: "POST",
url: `${this.sessionServerBaseUrl}/v2/store/set`,
data: body,
allowedOrigin: this.allowedOrigin,
headers
});
await this.safeLocalStorageOp(h => h.storeData(this.sessionId, data));
return this.sessionId;

@@ -69,20 +74,14 @@ }

super.checkSessionParams();
const pubkey = bytesToHex(getPublic(hexToBytes(this.sessionId)));
const body = {
key: pubkey,
namespace: this.sessionNamespace
};
const result = await super.request({
method: "POST",
url: `${this.sessionServerBaseUrl}/v2/store/get`,
data: body,
const localData = await this.safeLocalStorageOp(h => h.retrieveData(this.sessionId));
if (localData !== undefined) {
return localData;
}
const response = await this.serverHandler.retrieveData(this.sessionId, {
namespace: this.sessionNamespace,
headers
});
if (!result.message) {
if (response === undefined) {
throw new Error("Session Expired or Invalid public key");
}
const response = await decryptData(this.sessionId, result.message);
if (response.error) {
throw new Error("There was an error decrypting data.");
}
await this.safeLocalStorageOp(h => h.storeData(this.sessionId, response));
return response;

@@ -92,44 +91,56 @@ }

super.checkSessionParams();
const privKey = hexToBytes(this.sessionId);
const pubKey = bytesToHex(getPublic(privKey));
const encData = await encryptData(this.sessionId, data);
const signature = bytesToHex(await sign(privKey, keccak256(utf8ToBytes(encData))));
const body = {
key: pubKey,
data: encData,
signature,
await this.serverHandler.storeData(this.sessionId, data, {
operation: "update",
namespace: this.sessionNamespace,
allowedOrigin: this.allowedOrigin
};
await super.request({
method: "PUT",
url: `${this.sessionServerBaseUrl}/v2/store/update`,
data: body,
allowedOrigin: this.allowedOrigin,
headers
});
await this.safeLocalStorageOp(h => h.storeData(this.sessionId, data));
}
async invalidateSession(headers = {}) {
super.checkSessionParams();
const privKey = hexToBytes(this.sessionId);
const pubKey = bytesToHex(getPublic(privKey));
const encData = await encryptData(this.sessionId, {});
const signature = bytesToHex(await sign(privKey, keccak256(utf8ToBytes(encData))));
const data = {
key: pubKey,
data: encData,
signature,
await this.serverHandler.storeData(this.sessionId, {}, {
operation: "invalidate",
namespace: this.sessionNamespace,
timeout: 1
};
await super.request({
method: "POST",
url: `${this.sessionServerBaseUrl}/v2/store/set`,
data,
headers
});
this.clearStorage();
this.sessionId = "";
return true;
}
clearStorage() {
super.checkSessionParams();
const localStorageHandler = this.getLocalStorageHandler();
if (!localStorageHandler) return;
localStorageHandler.clearStorage(this.sessionId);
}
clearOrphanedData(baseKey) {
const localStorageHandler = this.getLocalStorageHandler();
if (!localStorageHandler) return;
localStorageHandler.clearOrphanedData(baseKey);
}
/**
* Runs a local-storage operation, swallowing any errors so that failures
* like QuotaExceededError never break the primary server-backed flow.
*/
async safeLocalStorageOp(fn) {
try {
const handler = this.getLocalStorageHandler();
if (!handler) return undefined;
return await fn(handler);
} catch {
return undefined;
}
}
getLocalStorageHandler() {
if (!this.useLocalStorage) return undefined;
if (typeof window === "undefined") return undefined;
if (this.localStorageHandler) return this.localStorageHandler;
if (!storageAvailable(STORAGE_METHOD.LOCAL_STORAGE)) return undefined;
this.localStorageHandler = new LocalStorageHandler(window[STORAGE_METHOD.LOCAL_STORAGE], this.baseLocalStorageKey);
return this.localStorageHandler;
}
}
export { SessionManager };
const padHexString = hexString => {
return hexString.padStart(64, "0").slice(0, 64);
};
const STORAGE_METHOD = {
LOCAL_STORAGE: "localStorage"
};
function storageAvailable(type) {
let storage;
try {
storage = window[type];
const x = "__storage_test__";
storage.setItem(x, x);
storage.removeItem(x);
return true;
} catch (error) {
const e = error;
return e && (
// everything except Firefox
e.code === 22 ||
// Firefox
e.code === 1014 ||
// test name field too, because code might not be present
// everything except Firefox
e.name === "QuotaExceededError" ||
// Firefox
e.name === "NS_ERROR_DOM_QUOTA_REACHED") &&
// acknowledge QuotaExceededError only if there's something already stored
storage && storage.length !== 0;
}
}
export { padHexString };
export { STORAGE_METHOD, padHexString, storageAvailable };
{
"name": "@toruslabs/session-manager",
"version": "5.0.0",
"version": "5.1.0",
"description": "session manager web",

@@ -5,0 +5,0 @@ "sideEffects": false,

import { SESSION_SERVER_API_URL } from "@toruslabs/constants";
import { beforeEach, describe, expect, test } from "vitest";
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import { BaseSessionManager } from "../src/base";
import { SessionManager } from "../src/sessionManager";

@@ -10,5 +11,3 @@

const sessionManager = new SessionManager({});
await expect(async () => {
await sessionManager.createSession({});
}).rejects.toThrow("Session id is required");
await expect(sessionManager.createSession({})).rejects.toThrow("Session id is required");
});

@@ -237,5 +236,3 @@ test("should use default base url if not set", () => {

await sessionManagerWithTrueOrigin.createSession({ testData: "test" }, { origin: "https://example.com" });
await expect(async () => {
await sessionManagerWithTrueOrigin.authorizeSession();
}).rejects.toThrow();
await expect(sessionManagerWithTrueOrigin.authorizeSession()).rejects.toThrow();
});

@@ -274,2 +271,159 @@ test("should return session data if allowedOrigin: FALSE, origin: empty", async () => {

});
describe("Local Storage Behavior", () => {
const originalWindow = globalThis.window;
class MemoryStorage implements Storage {
private store = new Map<string, string>();
get length() {
return this.store.size;
}
clear(): void {
this.store.forEach((_value, key) => {
delete (this as Record<string, unknown>)[key];
});
this.store.clear();
}
getItem(key: string): string | null {
return this.store.has(key) ? this.store.get(key)! : null;
}
key(index: number): string | null {
return Array.from(this.store.keys())[index] ?? null;
}
removeItem(key: string): void {
this.store.delete(key);
delete (this as Record<string, unknown>)[key];
}
setItem(key: string, value: string): void {
this.store.set(key, value);
Object.defineProperty(this, key, {
value,
configurable: true,
enumerable: true,
writable: true,
});
}
}
let storage: Storage;
let sessionId: string;
const sessionNamespace = "test-local";
beforeEach(() => {
storage = new MemoryStorage();
// Node test environment does not provide localStorage by default.
Object.defineProperty(globalThis, "window", {
value: { localStorage: storage },
configurable: true,
});
sessionId = SessionManager.generateRandomSessionKey();
});
afterEach(() => {
vi.restoreAllMocks();
Object.defineProperty(globalThis, "window", {
value: originalWindow,
configurable: true,
});
});
test("should write to local storage while creating session", async () => {
const sessionManager = new SessionManager<{ testData: string }>({
sessionId,
sessionNamespace,
useLocalStorage: true,
});
vi.spyOn(BaseSessionManager.prototype as unknown as { request: (...args: unknown[]) => Promise<unknown> }, "request").mockResolvedValue({});
await sessionManager.createSession({ testData: "test" });
expect(storage.getItem(`${sessionNamespace}:${sessionId}`)).toBe(JSON.stringify({ testData: "test" }));
});
test("should read from local storage before calling server", async () => {
const sessionManager = new SessionManager<{ testData: string }>({
sessionId,
sessionNamespace,
useLocalStorage: true,
});
const requestSpy = vi.spyOn(BaseSessionManager.prototype as unknown as { request: (...args: unknown[]) => Promise<unknown> }, "request");
storage.setItem(`${sessionNamespace}:${sessionId}`, JSON.stringify({ testData: "test" }));
const result = await sessionManager.authorizeSession();
expect(result).toEqual({ testData: "test" });
expect(requestSpy).not.toHaveBeenCalled();
});
test("should ignore corrupted local cache while updating session", async () => {
const sessionManager = new SessionManager<{ testData: string; count: number }>({
sessionId,
sessionNamespace,
useLocalStorage: true,
});
const requestSpy = vi
.spyOn(BaseSessionManager.prototype as unknown as { request: (...args: unknown[]) => Promise<unknown> }, "request")
.mockResolvedValueOnce({});
storage.setItem(`${sessionNamespace}:${sessionId}`, "not-json");
await expect(sessionManager.updateSession({ testData: "new" })).resolves.toBeUndefined();
expect(requestSpy).toHaveBeenCalledTimes(1);
expect(storage.getItem(`${sessionNamespace}:${sessionId}`)).toBe(JSON.stringify({ testData: "new" }));
});
test("should not write local storage when createSession request fails", async () => {
const sessionManager = new SessionManager<{ testData: string }>({
sessionId,
sessionNamespace,
useLocalStorage: true,
});
vi.spyOn(BaseSessionManager.prototype as unknown as { request: (...args: unknown[]) => Promise<unknown> }, "request").mockRejectedValueOnce(
new Error("request failed")
);
await expect(sessionManager.createSession({ testData: "test" })).rejects.toThrow("request failed");
expect(storage.getItem(`${sessionNamespace}:${sessionId}`)).toBeNull();
});
test("should not update local storage when updateSession request fails", async () => {
const sessionManager = new SessionManager<{ testData: string; count: number }>({
sessionId,
sessionNamespace,
useLocalStorage: true,
});
storage.setItem(`${sessionNamespace}:${sessionId}`, JSON.stringify({ testData: "existing", count: 1 }));
vi.spyOn(BaseSessionManager.prototype as unknown as { request: (...args: unknown[]) => Promise<unknown> }, "request").mockRejectedValueOnce(
new Error("request failed")
);
await expect(sessionManager.updateSession({ testData: "new" })).rejects.toThrow("request failed");
expect(storage.getItem(`${sessionNamespace}:${sessionId}`)).toBe(JSON.stringify({ testData: "existing", count: 1 }));
});
test("should clear current and orphaned local storage entries", () => {
const sessionManager = new SessionManager<{ testData: string }>({
sessionId,
sessionNamespace,
useLocalStorage: true,
});
storage.setItem(`${sessionNamespace}:${sessionId}`, JSON.stringify({ testData: "test" }));
storage.setItem(`${sessionNamespace}:orphaned`, JSON.stringify({ testData: "test2" }));
storage.setItem("another-namespace:keep", JSON.stringify({ testData: "keep" }));
sessionManager.clearStorage();
sessionManager.clearOrphanedData();
expect(storage.getItem(`${sessionNamespace}:${sessionId}`)).toBeNull();
expect(storage.getItem(`${sessionNamespace}:orphaned`)).toBeNull();
expect(storage.getItem("another-namespace:keep")).toBe(JSON.stringify({ testData: "keep" }));
});
});
});