@arex95/vue-core 1.1.27 → 1.1.30

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.
@@ -1,5 +1,4 @@
1
- import { AuthParams, AuthResponse, AuthTokenPaths } from "@/types";
2
- import { SessionPreference } from "@config/global/sessionConfig";
1
+ import { AuthParams, AuthResponse, AuthTokenPaths, SessionPreference } from "@/types";
3
2
  /**
4
3
  * @typedef {object} AuthHook
5
4
  * @property {function(): Promise<number | null>} getTokenExpiry - Retrieves the expiration time of the current token in milliseconds.
@@ -9,7 +8,7 @@ import { SessionPreference } from "@config/global/sessionConfig";
9
8
  * @property {(tokenPaths?: AuthTokenPaths) => Promise<AuthResponse>} refresh - Refreshes authentication tokens.
10
9
  * @property {function(): Promise<boolean>} verifyAuth - Verifies the validity and expiration of the current authentication token.
11
10
  * @property {(preference: SessionPreference) => void} setSessionPersistencePreference - Sets the user's preferred storage for authentication data.
12
- * @property {function(): SessionPreference} getSessionPersistencePreference - Retrieves the user's current preferred storage for authentication data.
11
+ * @property {function(): SessionPreference} getSessionPersistence - Retrieves the user's current preferred storage for authentication data.
13
12
  */
14
13
  /**
15
14
  * Custom hook for authentication logic, including login, logout, token management, and session preference.
@@ -1,3 +1,4 @@
1
1
  export * from './tokensConfig';
2
2
  export * from './endpointsConfig';
3
3
  export * from './sessionConfig';
4
+ export * from './keyConfig';
@@ -0,0 +1,20 @@
1
+ import { AppKeyConfig } from "../../types/AppKeyConfig";
2
+ /**
3
+ * Sets the main application encryption key.
4
+ * This key is expected to be used for encryption purposes within the application.
5
+ *
6
+ * @param {AppKeyConfig} config - An object containing the application encryption key.
7
+ * @param {string} config.key - The new application encryption key.
8
+ *
9
+ * @returns {void} Does not return anything, but updates the application key.
10
+ * @throws {Error} If the provided key is null, undefined, or an empty string.
11
+ */
12
+ export declare function configAppKey(config: AppKeyConfig): void;
13
+ /**
14
+ * Retrieves the current application encryption key.
15
+ * Throws an error if the application key has not been configured.
16
+ *
17
+ * @returns {string} The configured application encryption key.
18
+ * @throws {Error} If the application encryption key has not been set.
19
+ */
20
+ export declare function getAppKey(): string;
@@ -1,44 +1,34 @@
1
- export type SessionPreference = "local" | "session";
1
+ import { SessionPreference, SessionConfigObject, SessionConfig } from "@/types/SessionConfig";
2
2
  /**
3
- * Configuration object for the session.
4
- */
5
- interface SessionConfigObject {
6
- /** The unique session identifier. */
7
- sessionId?: string;
8
- /** The storage preference: 'local' for localStorage, 'session' for sessionStorage. */
9
- persistencePreference?: SessionPreference;
10
- }
11
- /**
12
- * Configures the session identifier and/or the data persistence preference for the active browser session.
13
- * Once configured, the session ID cannot be modified. The persistence preference can be updated.
3
+ * Configures the session identifier and/or data persistence preference
4
+ * for the active browser session.
14
5
  *
15
- * @param {SessionConfigObject} config - An object containing the unique session identifier and/or persistence preference.
6
+ * This function is asynchronous because it will always attempt to load the current configuration
7
+ * before applying changes and then saving them.
16
8
  *
17
- * @returns {void} Does not return anything, but freezes the session configuration (for ID) and updates preference.
9
+ * @param {SessionConfigObject} config - An object containing the unique session identifier and/or
10
+ * the persistence preference.
11
+ * @returns {Promise<void>} A promise that resolves when the session has been configured and saved.
18
12
  */
19
- export declare function configSession(config: SessionConfigObject): void;
13
+ export declare function configSession(config: SessionConfigObject): Promise<void>;
20
14
  /**
21
- * Retrieves the current session identifier configuration.
15
+ * Retrieves the current session identifier.
16
+ * Always attempts to load the configuration from storage. If it fails, it uses the internal state.
22
17
  *
23
- * @returns {string} The unique session identifier.
18
+ * @returns {Promise<string>} A promise that resolves with the unique session identifier.
24
19
  */
25
- export declare function getSessionConfig(): string;
20
+ export declare function getSessionId(): Promise<string>;
26
21
  /**
27
22
  * Retrieves the current data persistence preference.
23
+ * Always attempts to load the configuration from storage. If it fails, it uses the internal state.
28
24
  *
29
- * @returns {SessionPreference} The configured persistence preference ('local' or 'session').
25
+ * @returns {Promise<SessionPreference>} A promise that resolves with the configured persistence preference ('local' or 'session').
30
26
  */
31
- export declare function getSessionPersistencePreference(): SessionPreference;
27
+ export declare function getSessionPersistence(): Promise<SessionPreference>;
32
28
  /**
33
- * Generates a new UUID for the current session.
34
- *
35
- * @returns {void} Does not return anything, but updates the session identifier.
36
- */
37
- export declare function regenerateSessionId(): void;
38
- /**
39
- * Retrieves the current session identifier.
29
+ * Retrieves the complete session configuration.
30
+ * Always attempts to load the configuration from storage. If it fails, it uses the internal state.
40
31
  *
41
- * @returns {string} The unique session identifier.
32
+ * @returns {Promise<SessionConfig>} A promise that resolves with the session configuration object.
42
33
  */
43
- export declare function getSessionId(): string;
44
- export {};
34
+ export declare function getSessionConfig(): Promise<SessionConfig>;
@@ -23,18 +23,4 @@ export declare function configTokenKeys(config: TokenKeyConfig): void;
23
23
  * @returns {TokensConfig} The configuration of the access and refresh token keys.
24
24
  */
25
25
  export declare function getTokenConfig(): TokensConfig;
26
- /**
27
- * Sets the secret key for use in authentication.
28
- *
29
- * @param {string} key - The new secret key.
30
- *
31
- * @returns {void} Does not return anything, but updates the secret key.
32
- */
33
- export declare function setSecretKey(key: string): void;
34
- /**
35
- * Retrieves the current secret key.
36
- *
37
- * @returns {string} The configured secret key.
38
- */
39
- export declare function getSecretKey(): string;
40
26
  export {};
package/dist/index.mjs CHANGED
@@ -538,7 +538,6 @@ function inferErrorType(error) {
538
538
  return 'error';
539
539
  }
540
540
 
541
- let secretKey = "12345678901234567890123456789012";
542
541
  let tokensConfig = Object.freeze({
543
542
  ACCESS_TOKEN: "access_token",
544
543
  REFRESH_TOKEN: "refresh_token",
@@ -567,24 +566,6 @@ function configTokenKeys(config) {
567
566
  function getTokenConfig() {
568
567
  return tokensConfig;
569
568
  }
570
- /**
571
- * Sets the secret key for use in authentication.
572
- *
573
- * @param {string} key - The new secret key.
574
- *
575
- * @returns {void} Does not return anything, but updates the secret key.
576
- */
577
- function setSecretKey(key) {
578
- secretKey = key;
579
- }
580
- /**
581
- * Retrieves the current secret key.
582
- *
583
- * @returns {string} The configured secret key.
584
- */
585
- function getSecretKey() {
586
- return secretKey;
587
- }
588
569
 
589
570
  let endpointsConfig = {
590
571
  LOGIN: "/login",
@@ -784,62 +765,277 @@ const createCustomAxiosInstance = (config) => {
784
765
  return new AxiosService(config.baseURL, config.headers).getAxiosInstance();
785
766
  };
786
767
 
787
- let sessionId = v4();
788
- let sessionConfig = Object.freeze({
789
- SESSION_ID: sessionId,
790
- });
791
- let persistencePreference = "session";
792
768
  /**
793
- * Configures the session identifier and/or the data persistence preference for the active browser session.
794
- * Once configured, the session ID cannot be modified. The persistence preference can be updated.
769
+ * Converts an ArrayBuffer or Uint8Array to a hexadecimal string.
770
+ * @param buffer The ArrayBuffer or Uint8Array to convert.
771
+ * @returns The hexadecimal string.
772
+ */
773
+ function ab2hex(buffer) {
774
+ return Array.from(new Uint8Array(buffer))
775
+ .map((byte) => byte.toString(16).padStart(2, "0"))
776
+ .join("");
777
+ }
778
+ /**
779
+ * Converts a hexadecimal string to a Uint8Array.
780
+ * @param hex The hexadecimal string to convert.
781
+ * @returns The Uint8Array.
782
+ * @throws {TypeError} If the input is not a string.
783
+ * @throws {Error} If the hexadecimal string format is invalid or has an odd length.
784
+ */
785
+ function hex2ab(hex) {
786
+ if (typeof hex !== "string") {
787
+ throw new TypeError("Input must be a string.");
788
+ }
789
+ if (hex.length === 0) {
790
+ return new Uint8Array();
791
+ }
792
+ if (!/^[0-9a-fA-F]*$/.test(hex) || hex.length % 2 !== 0) {
793
+ throw new Error("Invalid hexadecimal string format or odd length.");
794
+ }
795
+ const array = new Uint8Array(hex.length / 2);
796
+ for (let i = 0; i < hex.length; i += 2) {
797
+ array[i / 2] = parseInt(hex.substring(i, i + 2), 16);
798
+ }
799
+ return array;
800
+ }
801
+ /**
802
+ * Derives an encryption key from a secret key.
803
+ * @param secretKey The secret key in plain text.
804
+ * @returns A promise that resolves with the derived CryptoKey.
805
+ * @throws {Error} If the secretKey is null or empty.
806
+ */
807
+ async function importKey(secretKey) {
808
+ if (!secretKey) {
809
+ throw new Error("Secret key cannot be null or empty.");
810
+ }
811
+ const keyMaterial = new TextEncoder().encode(secretKey);
812
+ const digest = await crypto.subtle.digest("SHA-256", keyMaterial);
813
+ return crypto.subtle.importKey("raw", digest, { name: "AES-CBC", length: 256 }, false, ["encrypt", "decrypt"]);
814
+ }
815
+ /**
816
+ * Encrypts a value with the provided secret key.
817
+ * @param value The value to encrypt.
818
+ * @param secretKey The secret key for encryption.
819
+ * @returns A promise that resolves with the IV (hex) + ciphertext (hex) string.
820
+ * @throws {Error} If the secretKey is null or empty (via importKey).
821
+ */
822
+ async function encrypt(value, secretKey) {
823
+ const key = await importKey(secretKey);
824
+ const iv = crypto.getRandomValues(new Uint8Array(16));
825
+ const encodedValue = new TextEncoder().encode(value);
826
+ const ciphertext = await crypto.subtle.encrypt({ name: "AES-CBC", iv: iv }, key, encodedValue);
827
+ return ab2hex(iv) + ab2hex(new Uint8Array(ciphertext));
828
+ }
829
+ /**
830
+ * Decrypts an encrypted value.
831
+ * @param encryptedValue The encrypted string (IV_hex + ciphertext_hex).
832
+ * @param secretKey The secret key for decryption.
833
+ * @returns A promise that resolves with the decrypted value.
834
+ * @throws {Error} If encryptedValue is null or empty, too short,
835
+ * or if the IV/ciphertext have incorrect lengths after conversion.
836
+ * @throws {Error} If the secretKey is null or empty (via importKey).
837
+ * @throws {TypeError} If hex2ab receives an invalid input type.
838
+ */
839
+ async function decrypt(encryptedValue, secretKey) {
840
+ if (!encryptedValue) {
841
+ throw new Error("Encrypted value cannot be null or empty.");
842
+ }
843
+ // For AES-CBC, the IV is ALWAYS 16 bytes.
844
+ // 16 bytes * 2 hex characters/byte = 32 hex characters for the IV.
845
+ if (encryptedValue.length < 32) {
846
+ throw new Error("Encrypted value is too short. Expected at least 32 hexadecimal characters for the IV.");
847
+ }
848
+ const key = await importKey(secretKey);
849
+ const ivHex = encryptedValue.substring(0, 32);
850
+ const ciphertextHex = encryptedValue.substring(32);
851
+ const iv = hex2ab(ivHex);
852
+ const ciphertext = hex2ab(ciphertextHex);
853
+ if (iv.byteLength !== 16) {
854
+ throw new Error(`Converted IV has incorrect length: ${iv.byteLength} bytes. Expected 16 bytes.`);
855
+ }
856
+ if (ciphertext.byteLength === 0) {
857
+ throw new Error("Ciphertext is empty. No data to decrypt.");
858
+ }
859
+ const decryptedBuffer = await crypto.subtle.decrypt({ name: "AES-CBC", iv: iv }, key, ciphertext);
860
+ return new TextDecoder().decode(decryptedBuffer);
861
+ }
862
+
863
+ /**
864
+ * Encrypts and stores an item in local or session storage.
865
+ * Assumes the `window` environment is available.
866
+ * @param key The key under which to store the value.
867
+ * @param value The value to encrypt and store.
868
+ * @param secretKey The secret key for encryption.
869
+ * @param persistent If true, uses localStorage; otherwise, uses sessionStorage.
870
+ * @returns A promise that resolves when the item is stored. Throws an error if it fails.
871
+ */
872
+ async function storeEncryptedItem(key, value, secretKey, persistent) {
873
+ if (typeof window === "undefined") {
874
+ throw new Error("Cannot access storage: window is not defined.");
875
+ }
876
+ const storage = persistent ? window.localStorage : window.sessionStorage;
877
+ const encryptedValue = await encrypt(value, secretKey);
878
+ storage.setItem(key, encryptedValue);
879
+ }
880
+ /**
881
+ * Retrieves and decrypts a value from local or session storage.
882
+ * Assumes the `window` environment is available.
883
+ * @param key The key of the item to retrieve.
884
+ * @param secretKey The secret key for decryption.
885
+ * @param persistent If true, searches localStorage; otherwise, sessionStorage.
886
+ * @returns A promise that resolves with the decrypted value or null if not found or decryption fails.
887
+ */
888
+ async function getDecryptedItem(key, secretKey, persistent) {
889
+ if (typeof window === "undefined") {
890
+ return null;
891
+ }
892
+ const storage = persistent ? window.localStorage : window.sessionStorage;
893
+ const encryptedData = storage.getItem(key);
894
+ if (!encryptedData) {
895
+ return null;
896
+ }
897
+ try {
898
+ return await decrypt(encryptedData, secretKey);
899
+ }
900
+ catch (error) {
901
+ return null;
902
+ }
903
+ }
904
+
905
+ let appKey = null;
906
+ /**
907
+ * Sets the main application encryption key.
908
+ * This key is expected to be used for encryption purposes within the application.
795
909
  *
796
- * @param {SessionConfigObject} config - An object containing the unique session identifier and/or persistence preference.
910
+ * @param {AppKeyConfig} config - An object containing the application encryption key.
911
+ * @param {string} config.key - The new application encryption key.
797
912
  *
798
- * @returns {void} Does not return anything, but freezes the session configuration (for ID) and updates preference.
913
+ * @returns {void} Does not return anything, but updates the application key.
914
+ * @throws {Error} If the provided key is null, undefined, or an empty string.
799
915
  */
800
- function configSession(config) {
801
- if (config.sessionId) {
802
- sessionConfig = Object.freeze({
803
- ...sessionConfig,
804
- SESSION_ID: config.sessionId,
805
- });
806
- }
807
- if (config.persistencePreference) {
808
- persistencePreference = config.persistencePreference;
916
+ function configAppKey(config) {
917
+ if (!config || !config.appKey || config.appKey.trim() === "") {
918
+ throw new Error("The application encryption key cannot be null or empty.");
809
919
  }
920
+ appKey = config.appKey;
810
921
  }
811
922
  /**
812
- * Retrieves the current session identifier configuration.
923
+ * Retrieves the current application encryption key.
924
+ * Throws an error if the application key has not been configured.
813
925
  *
814
- * @returns {string} The unique session identifier.
926
+ * @returns {string} The configured application encryption key.
927
+ * @throws {Error} If the application encryption key has not been set.
815
928
  */
816
- function getSessionConfig() {
817
- return sessionConfig.SESSION_ID;
929
+ function getAppKey() {
930
+ if (appKey === null) {
931
+ throw new Error("The application encryption key has not been configured. Please call 'configAppKey()' before attempting to access it.");
932
+ }
933
+ return appKey;
818
934
  }
935
+
936
+ const SESSION_KEY = "session_config_";
937
+ const internalSessionState = {
938
+ sessionId: v4(),
939
+ persistencePreference: "session",
940
+ };
941
+ let _sessionConfig = Object.freeze({
942
+ SESSION_ID: internalSessionState.sessionId,
943
+ PERSISTENCE: internalSessionState.persistencePreference,
944
+ });
819
945
  /**
820
- * Retrieves the current data persistence preference.
946
+ * Updates the immutable `_sessionConfig` object with the current state of `internalSessionState`
947
+ * and freezes it.
948
+ */
949
+ function updateSessionConfig() {
950
+ _sessionConfig = Object.freeze({
951
+ SESSION_ID: internalSessionState.sessionId,
952
+ PERSISTENCE: internalSessionState.persistencePreference,
953
+ });
954
+ }
955
+ /**
956
+ * Saves the current session configuration to local or session storage.
957
+ * @returns {Promise<void>} A promise that resolves when the session configuration has been saved.
958
+ */
959
+ async function saveSessionConfig() {
960
+ const isPersistent = internalSessionState.persistencePreference === "local";
961
+ try {
962
+ await storeEncryptedItem(SESSION_KEY, JSON.stringify(_sessionConfig), getAppKey(), isPersistent);
963
+ }
964
+ catch (error) {
965
+ console.error("Error saving session configuration to storage:", error);
966
+ }
967
+ }
968
+ /**
969
+ * Attempts to load the configuration from storage.
970
+ * If it fails or is not found, `internalSessionState` will retain its current values (initial or last configured).
971
+ * Then, it updates `_sessionConfig`.
821
972
  *
822
- * @returns {SessionPreference} The configured persistence preference ('local' or 'session').
973
+ * @returns {Promise<void>} A promise that resolves when the state has been loaded and updated.
823
974
  */
824
- function getSessionPersistencePreference() {
825
- return persistencePreference;
975
+ async function loadSessionConfig() {
976
+ try {
977
+ const storedConfig = await getDecryptedItem(SESSION_KEY, getAppKey(), true);
978
+ if (storedConfig) {
979
+ const parsedConfig = JSON.parse(storedConfig);
980
+ internalSessionState.sessionId = parsedConfig.SESSION_ID;
981
+ internalSessionState.persistencePreference = parsedConfig.PERSISTENCE;
982
+ updateSessionConfig();
983
+ }
984
+ }
985
+ catch (error) {
986
+ console.warn(`Error loading or parsing from storage`, error);
987
+ }
826
988
  }
827
989
  /**
828
- * Generates a new UUID for the current session.
990
+ * Configures the session identifier and/or data persistence preference
991
+ * for the active browser session.
992
+ *
993
+ * This function is asynchronous because it will always attempt to load the current configuration
994
+ * before applying changes and then saving them.
829
995
  *
830
- * @returns {void} Does not return anything, but updates the session identifier.
996
+ * @param {SessionConfigObject} config - An object containing the unique session identifier and/or
997
+ * the persistence preference.
998
+ * @returns {Promise<void>} A promise that resolves when the session has been configured and saved.
831
999
  */
832
- function regenerateSessionId() {
833
- sessionId = v4();
834
- configSession({ sessionId: sessionId });
1000
+ async function configSession(config) {
1001
+ if (config.sessionId) {
1002
+ internalSessionState.sessionId = config.sessionId;
1003
+ }
1004
+ if (config.persistencePreference) {
1005
+ internalSessionState.persistencePreference = config.persistencePreference;
1006
+ }
1007
+ updateSessionConfig();
1008
+ await saveSessionConfig();
835
1009
  }
836
1010
  /**
837
1011
  * Retrieves the current session identifier.
1012
+ * Always attempts to load the configuration from storage. If it fails, it uses the internal state.
1013
+ *
1014
+ * @returns {Promise<string>} A promise that resolves with the unique session identifier.
1015
+ */
1016
+ async function getSessionId() {
1017
+ await loadSessionConfig();
1018
+ return _sessionConfig.SESSION_ID;
1019
+ }
1020
+ /**
1021
+ * Retrieves the current data persistence preference.
1022
+ * Always attempts to load the configuration from storage. If it fails, it uses the internal state.
838
1023
  *
839
- * @returns {string} The unique session identifier.
1024
+ * @returns {Promise<SessionPreference>} A promise that resolves with the configured persistence preference ('local' or 'session').
840
1025
  */
841
- function getSessionId() {
842
- return sessionId;
1026
+ async function getSessionPersistence() {
1027
+ await loadSessionConfig();
1028
+ return _sessionConfig.PERSISTENCE;
1029
+ }
1030
+ /**
1031
+ * Retrieves the complete session configuration.
1032
+ * Always attempts to load the configuration from storage. If it fails, it uses the internal state.
1033
+ *
1034
+ * @returns {Promise<SessionConfig>} A promise that resolves with the session configuration object.
1035
+ */
1036
+ async function getSessionConfig() {
1037
+ await loadSessionConfig();
1038
+ return _sessionConfig;
843
1039
  }
844
1040
 
845
1041
  /**
@@ -2504,143 +2700,6 @@ function isValidHexNumber(hex) {
2504
2700
  // console.log(isValidHexNumber('1A3F')); // true
2505
2701
  // console.log(isValidHexNumber('GHIJ')); // false
2506
2702
 
2507
- /**
2508
- * Converts an ArrayBuffer or Uint8Array to a hexadecimal string.
2509
- * @param buffer The ArrayBuffer or Uint8Array to convert.
2510
- * @returns The hexadecimal string.
2511
- */
2512
- function ab2hex(buffer) {
2513
- return Array.from(new Uint8Array(buffer))
2514
- .map((byte) => byte.toString(16).padStart(2, "0"))
2515
- .join("");
2516
- }
2517
- /**
2518
- * Converts a hexadecimal string to a Uint8Array.
2519
- * @param hex The hexadecimal string to convert.
2520
- * @returns The Uint8Array.
2521
- * @throws {TypeError} If the input is not a string.
2522
- * @throws {Error} If the hexadecimal string format is invalid or has an odd length.
2523
- */
2524
- function hex2ab(hex) {
2525
- if (typeof hex !== "string") {
2526
- throw new TypeError("Input must be a string.");
2527
- }
2528
- if (hex.length === 0) {
2529
- return new Uint8Array();
2530
- }
2531
- if (!/^[0-9a-fA-F]*$/.test(hex) || hex.length % 2 !== 0) {
2532
- throw new Error("Invalid hexadecimal string format or odd length.");
2533
- }
2534
- const array = new Uint8Array(hex.length / 2);
2535
- for (let i = 0; i < hex.length; i += 2) {
2536
- array[i / 2] = parseInt(hex.substring(i, i + 2), 16);
2537
- }
2538
- return array;
2539
- }
2540
- /**
2541
- * Derives an encryption key from a secret key.
2542
- * @param secretKey The secret key in plain text.
2543
- * @returns A promise that resolves with the derived CryptoKey.
2544
- * @throws {Error} If the secretKey is null or empty.
2545
- */
2546
- async function importKey(secretKey) {
2547
- if (!secretKey) {
2548
- throw new Error("Secret key cannot be null or empty.");
2549
- }
2550
- const keyMaterial = new TextEncoder().encode(secretKey);
2551
- const digest = await crypto.subtle.digest("SHA-256", keyMaterial);
2552
- return crypto.subtle.importKey("raw", digest, { name: "AES-CBC", length: 256 }, false, ["encrypt", "decrypt"]);
2553
- }
2554
- /**
2555
- * Encrypts a value with the provided secret key.
2556
- * @param value The value to encrypt.
2557
- * @param secretKey The secret key for encryption.
2558
- * @returns A promise that resolves with the IV (hex) + ciphertext (hex) string.
2559
- * @throws {Error} If the secretKey is null or empty (via importKey).
2560
- */
2561
- async function encrypt(value, secretKey) {
2562
- const key = await importKey(secretKey);
2563
- const iv = crypto.getRandomValues(new Uint8Array(16));
2564
- const encodedValue = new TextEncoder().encode(value);
2565
- const ciphertext = await crypto.subtle.encrypt({ name: "AES-CBC", iv: iv }, key, encodedValue);
2566
- return ab2hex(iv) + ab2hex(new Uint8Array(ciphertext));
2567
- }
2568
- /**
2569
- * Decrypts an encrypted value.
2570
- * @param encryptedValue The encrypted string (IV_hex + ciphertext_hex).
2571
- * @param secretKey The secret key for decryption.
2572
- * @returns A promise that resolves with the decrypted value.
2573
- * @throws {Error} If encryptedValue is null or empty, too short,
2574
- * or if the IV/ciphertext have incorrect lengths after conversion.
2575
- * @throws {Error} If the secretKey is null or empty (via importKey).
2576
- * @throws {TypeError} If hex2ab receives an invalid input type.
2577
- */
2578
- async function decrypt(encryptedValue, secretKey) {
2579
- if (!encryptedValue) {
2580
- throw new Error("Encrypted value cannot be null or empty.");
2581
- }
2582
- // For AES-CBC, the IV is ALWAYS 16 bytes.
2583
- // 16 bytes * 2 hex characters/byte = 32 hex characters for the IV.
2584
- if (encryptedValue.length < 32) {
2585
- throw new Error("Encrypted value is too short. Expected at least 32 hexadecimal characters for the IV.");
2586
- }
2587
- const key = await importKey(secretKey);
2588
- const ivHex = encryptedValue.substring(0, 32);
2589
- const ciphertextHex = encryptedValue.substring(32);
2590
- const iv = hex2ab(ivHex);
2591
- const ciphertext = hex2ab(ciphertextHex);
2592
- if (iv.byteLength !== 16) {
2593
- throw new Error(`Converted IV has incorrect length: ${iv.byteLength} bytes. Expected 16 bytes.`);
2594
- }
2595
- if (ciphertext.byteLength === 0) {
2596
- throw new Error("Ciphertext is empty. No data to decrypt.");
2597
- }
2598
- const decryptedBuffer = await crypto.subtle.decrypt({ name: "AES-CBC", iv: iv }, key, ciphertext);
2599
- return new TextDecoder().decode(decryptedBuffer);
2600
- }
2601
-
2602
- /**
2603
- * Encrypts and stores an item in local or session storage.
2604
- * Assumes the `window` environment is available.
2605
- * @param key The key under which to store the value.
2606
- * @param value The value to encrypt and store.
2607
- * @param secretKey The secret key for encryption.
2608
- * @param isRememberMe If true, uses localStorage; otherwise, uses sessionStorage.
2609
- * @returns A promise that resolves when the item is stored. Throws an error if it fails.
2610
- */
2611
- async function storeEncryptedItem(key, value, secretKey, isRememberMe) {
2612
- if (typeof window === "undefined") {
2613
- throw new Error("Cannot access storage: window is not defined.");
2614
- }
2615
- const storage = isRememberMe ? window.localStorage : window.sessionStorage;
2616
- const encryptedValue = await encrypt(value, secretKey);
2617
- storage.setItem(key, encryptedValue);
2618
- }
2619
- /**
2620
- * Retrieves and decrypts a value from local or session storage.
2621
- * Assumes the `window` environment is available.
2622
- * @param key The key of the item to retrieve.
2623
- * @param secretKey The secret key for decryption.
2624
- * @param isRememberMe If true, searches localStorage; otherwise, sessionStorage.
2625
- * @returns A promise that resolves with the decrypted value or null if not found or decryption fails.
2626
- */
2627
- async function getDecryptedItem(key, secretKey, isRememberMe) {
2628
- if (typeof window === "undefined") {
2629
- return null;
2630
- }
2631
- const storage = isRememberMe ? window.localStorage : window.sessionStorage;
2632
- const encryptedData = storage.getItem(key);
2633
- if (!encryptedData) {
2634
- return null;
2635
- }
2636
- try {
2637
- return await decrypt(encryptedData, secretKey);
2638
- }
2639
- catch (error) {
2640
- return null;
2641
- }
2642
- }
2643
-
2644
2703
  /**
2645
2704
  * Clears all stored authentication data (access and refresh tokens)
2646
2705
  * from either sessionStorage or localStorage based on the provided preference.
@@ -2709,43 +2768,42 @@ const storeAuthRefreshToken = async (token, secretKey, preference) => {
2709
2768
  * Verifies the validity and expiration of the current authentication token.
2710
2769
  * If the token is missing, invalid, or expired, appropriate errors are thrown and credentials are cleaned.
2711
2770
  *
2712
- * @param {string} secretKey - The secret key used for token decryption.
2713
- * @param {SessionPreference} preference - The current session persistence preference.
2714
2771
  * @returns {Promise<boolean>} True if the token is valid and unexpired.
2715
2772
  * @throws {Error} "TOKEN_MISSING" if no token is found, "TOKEN_EXPIRED" if the token has expired,
2716
2773
  * "TOKEN_INVALID" if the token format is invalid.
2717
2774
  */
2718
- const verifyAuth = async (secretKey = getSecretKey(), preference = getSessionPersistencePreference()) => {
2775
+ const verifyAuth = async () => {
2776
+ const sessionPersistence = await getSessionPersistence();
2777
+ const handleAuthError = async (message, shouldClean = true) => {
2778
+ handleError(message, false);
2779
+ if (shouldClean) {
2780
+ await cleanCredentials(sessionPersistence);
2781
+ }
2782
+ return false;
2783
+ };
2719
2784
  try {
2720
- const token = await getAuthToken(secretKey, preference);
2785
+ const token = await getAuthToken(getAppKey(), sessionPersistence);
2721
2786
  if (!token) {
2722
- handleError("TOKEN_MISSING: No valid token found", false);
2723
- await cleanCredentials(preference);
2724
- return false;
2787
+ return await handleAuthError("TOKEN_MISSING: No valid token found");
2788
+ }
2789
+ let decoded;
2790
+ try {
2791
+ decoded = jwtDecode(token);
2792
+ }
2793
+ catch (decodeError) {
2794
+ return await handleAuthError("TOKEN_INVALID: Invalid token format");
2725
2795
  }
2726
- const decoded = jwtDecode(token);
2727
2796
  const currentTime = Date.now() / 1000;
2728
2797
  if (typeof decoded.exp !== "number") {
2729
- handleError("TOKEN_INVALID: Invalid expiration format", false);
2730
- await cleanCredentials(preference);
2731
- return false;
2798
+ return await handleAuthError("TOKEN_INVALID: Invalid expiration format");
2732
2799
  }
2733
2800
  if (decoded.exp <= currentTime) {
2734
- handleError("TOKEN_EXPIRED: Token is expired", false);
2735
- await cleanCredentials(preference);
2736
- return false;
2801
+ return await handleAuthError("TOKEN_EXPIRED: Token is expired");
2737
2802
  }
2738
2803
  return true;
2739
2804
  }
2740
2805
  catch (error) {
2741
- if (error instanceof Error && error.message.includes("Invalid")) {
2742
- handleError("TOKEN_INVALID: Invalid token format", false);
2743
- await cleanCredentials(preference);
2744
- return false;
2745
- }
2746
- handleError("AUTH_ERROR: An unexpected error occurred", false);
2747
- await cleanCredentials(preference);
2748
- return false;
2806
+ return await handleAuthError("AUTH_ERROR: An unexpected error occurred", true);
2749
2807
  }
2750
2808
  };
2751
2809
 
@@ -3309,7 +3367,7 @@ function useSorter(items, criteriaList, selectedCriteria) {
3309
3367
  * @property {(tokenPaths?: AuthTokenPaths) => Promise<AuthResponse>} refresh - Refreshes authentication tokens.
3310
3368
  * @property {function(): Promise<boolean>} verifyAuth - Verifies the validity and expiration of the current authentication token.
3311
3369
  * @property {(preference: SessionPreference) => void} setSessionPersistencePreference - Sets the user's preferred storage for authentication data.
3312
- * @property {function(): SessionPreference} getSessionPersistencePreference - Retrieves the user's current preferred storage for authentication data.
3370
+ * @property {function(): SessionPreference} getSessionPersistence - Retrieves the user's current preferred storage for authentication data.
3313
3371
  */
3314
3372
  /**
3315
3373
  * Custom hook for authentication logic, including login, logout, token management, and session preference.
@@ -3317,10 +3375,9 @@ function useSorter(items, criteriaList, selectedCriteria) {
3317
3375
  * @param {string} secretKey - The secret key used for token encryption/decryption.
3318
3376
  * @returns {AuthHook} An object containing authentication functions.
3319
3377
  */
3320
- function useAuth(secretKey = getSecretKey()) {
3378
+ function useAuth(secretKey = getAppKey()) {
3321
3379
  const axiosInstance = getAxiosInstance();
3322
3380
  const endpoints = getEndpointsConfig();
3323
- const currentPersistencePreference = getSessionPersistencePreference();
3324
3381
  /**
3325
3382
  * Logs out the user by making a POST request to the logout endpoint,
3326
3383
  * cleaning all stored credentials, and reloading the page.
@@ -3337,7 +3394,7 @@ function useAuth(secretKey = getSecretKey()) {
3337
3394
  handleError(error, false);
3338
3395
  }
3339
3396
  finally {
3340
- await cleanCredentials(currentPersistencePreference);
3397
+ await cleanCredentials(await getSessionPersistence());
3341
3398
  window.location.reload();
3342
3399
  }
3343
3400
  };
@@ -3393,7 +3450,7 @@ function useAuth(secretKey = getSecretKey()) {
3393
3450
  */
3394
3451
  const refresh = async (tokenPaths) => {
3395
3452
  try {
3396
- const refreshTokenFromStorage = await getAuthRefreshToken(secretKey, currentPersistencePreference);
3453
+ const refreshTokenFromStorage = await getAuthRefreshToken(secretKey, await getSessionPersistence());
3397
3454
  if (!refreshTokenFromStorage) {
3398
3455
  throw new Error("TOKEN_MISSING: No refresh token found in storage.");
3399
3456
  }
@@ -3415,8 +3472,8 @@ function useAuth(secretKey = getSecretKey()) {
3415
3472
  typeof refreshTokenAfterRefresh !== "string") {
3416
3473
  throw new Error(`REFRESH_ERROR: Refresh token not found or invalid at path '${refreshTokenPath}' in refresh response.`);
3417
3474
  }
3418
- await storeAuthToken(accessTokenAfterRefresh, secretKey, currentPersistencePreference);
3419
- await storeAuthRefreshToken(refreshTokenAfterRefresh, secretKey, currentPersistencePreference);
3475
+ await storeAuthToken(accessTokenAfterRefresh, secretKey, await getSessionPersistence());
3476
+ await storeAuthRefreshToken(refreshTokenAfterRefresh, secretKey, await getSessionPersistence());
3420
3477
  return data;
3421
3478
  }
3422
3479
  catch (error) {
@@ -3449,10 +3506,8 @@ const ArexVueCore = {
3449
3506
  console.warn("ArexVueCore: No configuration options were provided. The library may not function correctly.");
3450
3507
  return;
3451
3508
  }
3452
- configEndpoints({
3453
- loginEndpoint: options.endpoints.login,
3454
- refreshEndpoint: options.endpoints.refresh,
3455
- logoutEndpoint: options.endpoints.logout
3509
+ configAppKey({
3510
+ appKey: options.appKey
3456
3511
  });
3457
3512
  configTokenKeys({
3458
3513
  accessTokenKey: options.tokenKeys.accessToken,
@@ -3461,7 +3516,12 @@ const ArexVueCore = {
3461
3516
  configAxios({
3462
3517
  baseURL: options.apiUrl,
3463
3518
  });
3519
+ configEndpoints({
3520
+ loginEndpoint: options.endpoints.login,
3521
+ refreshEndpoint: options.endpoints.refresh,
3522
+ logoutEndpoint: options.endpoints.logout,
3523
+ });
3464
3524
  },
3465
3525
  };
3466
3526
 
3467
- export { AppTypes, ArchiveTypes, ArexVueCore, AudioTypes, AxiosService, ContentTypeEnum, DocumentTypes, ERROR_MESSAGES, ERROR_STYLES, ErrorEnum, ErrorMessages, ErrorStyles, ExceptionEnum, FontTypes, ImageTypes, KeyCodeEnum, OtherTypes, RestStd, ScreenBreakpoint, ScreenSize, StorageKeyEnum, StorageTypeEnum, TextTypes, VideoTypes, ab2hex, addCustomKeyboardShortcut, addDays, addDoubleClickListener, addKeyListener, ageAtDate, axiosFetch, blobToFormData, bufferToBlob, calculateAge, cleanCredentials, clickOutside, compareObject, configAxios, configEndpoints, configSession, configTokenKeys, copyToClipboard, countWords, createCustomAxiosInstance, createFetch, createKeyMap, customShortcut, daysBetween, daysToNextBirthday, debounce, debounceAsync, debounceAsyncValidator, debounceAsyncWithImmediate, debounceLeading, debounceLeadingTrailing, debounceTrailing, decrypt, deepClone, deepEqual, deepMerge, detectKeyHold, disableCopy, disableF12Key, disableMouseButtons, disableRightClick, disableSpecificKeys, downloadBlob, enableMouseButtons, enableRightClick, enableSpecificKeys, encrypt, exportToCSV, exportToExcel, exportToJSON, exportToText, exportToXML, filterObjectByKeys, flattenObject, formDataToObject, formatDate, generateRandomString, getAuthRefreshToken, getAuthToken, getAxiosInstance, getDecryptedItem, getEndOfMonth, getEndpointsConfig, getObjectDifferences, getObjectKeys, getQueryParam, getSecretKey, getSessionConfig, getSessionId, getSessionPersistencePreference, getStartOfMonth, getTokenConfig, handleError, hasNestedProperties, hex2ab, importKey, isEmptyObject, isLeapYear, isStrongPassword, isValidAge, isValidCreditCard, isValidDate, isValidEmail, isValidExpiryDate, isValidHexColor, isValidHexColorAlpha, isValidHexNumber, isValidIP, isValidPhoneNumber, isValidSSN, isValidTime, isValidURL, isValidUsername, isValidZIP, lowerFirst, objectToFormData, objectToFormDataEnhanced, objectToQueryString, openWindow, parseDate, proxyToPlainObject, readFileAsDataURL, readFileAsText, regenerateSessionId, registerKeyboardShortcuts, removeAccent, removeClickOutside, removeCustomKeyboardShortcut, removeCustomShortcuts, removeDoubleClickListener, removeEmptyProperties, removeKeyListeners, replaceAll, reverseString, safeGet, screenMap, scrollToTop, setSecretKey, simulateKeyPress, stopDetectingKeyHold, storeAuthRefreshToken, storeAuthToken, storeEncryptedItem, stringToBlob, subtractDays, throttle, toCamelCase, toKebabCase, toggleTabNavigation, truncateString, unregisterKeyboardShortcuts, upperFirst, useAuth, useBreakpoint, useFilter, usePagination, useSorter, useVueQuery, validateAlphanumeric, validateLetters, validateNumbers, verifyAuth };
3527
+ export { AppTypes, ArchiveTypes, ArexVueCore, AudioTypes, AxiosService, ContentTypeEnum, DocumentTypes, ERROR_MESSAGES, ERROR_STYLES, ErrorEnum, ErrorMessages, ErrorStyles, ExceptionEnum, FontTypes, ImageTypes, KeyCodeEnum, OtherTypes, RestStd, ScreenBreakpoint, ScreenSize, StorageKeyEnum, StorageTypeEnum, TextTypes, VideoTypes, ab2hex, addCustomKeyboardShortcut, addDays, addDoubleClickListener, addKeyListener, ageAtDate, axiosFetch, blobToFormData, bufferToBlob, calculateAge, cleanCredentials, clickOutside, compareObject, configAppKey, configAxios, configEndpoints, configSession, configTokenKeys, copyToClipboard, countWords, createCustomAxiosInstance, createFetch, createKeyMap, customShortcut, daysBetween, daysToNextBirthday, debounce, debounceAsync, debounceAsyncValidator, debounceAsyncWithImmediate, debounceLeading, debounceLeadingTrailing, debounceTrailing, decrypt, deepClone, deepEqual, deepMerge, detectKeyHold, disableCopy, disableF12Key, disableMouseButtons, disableRightClick, disableSpecificKeys, downloadBlob, enableMouseButtons, enableRightClick, enableSpecificKeys, encrypt, exportToCSV, exportToExcel, exportToJSON, exportToText, exportToXML, filterObjectByKeys, flattenObject, formDataToObject, formatDate, generateRandomString, getAppKey, getAuthRefreshToken, getAuthToken, getAxiosInstance, getDecryptedItem, getEndOfMonth, getEndpointsConfig, getObjectDifferences, getObjectKeys, getQueryParam, getSessionConfig, getSessionId, getSessionPersistence, getStartOfMonth, getTokenConfig, handleError, hasNestedProperties, hex2ab, importKey, isEmptyObject, isLeapYear, isStrongPassword, isValidAge, isValidCreditCard, isValidDate, isValidEmail, isValidExpiryDate, isValidHexColor, isValidHexColorAlpha, isValidHexNumber, isValidIP, isValidPhoneNumber, isValidSSN, isValidTime, isValidURL, isValidUsername, isValidZIP, lowerFirst, objectToFormData, objectToFormDataEnhanced, objectToQueryString, openWindow, parseDate, proxyToPlainObject, readFileAsDataURL, readFileAsText, registerKeyboardShortcuts, removeAccent, removeClickOutside, removeCustomKeyboardShortcut, removeCustomShortcuts, removeDoubleClickListener, removeEmptyProperties, removeKeyListeners, replaceAll, reverseString, safeGet, screenMap, scrollToTop, simulateKeyPress, stopDetectingKeyHold, storeAuthRefreshToken, storeAuthToken, storeEncryptedItem, stringToBlob, subtractDays, throttle, toCamelCase, toKebabCase, toggleTabNavigation, truncateString, unregisterKeyboardShortcuts, upperFirst, useAuth, useBreakpoint, useFilter, usePagination, useSorter, useVueQuery, validateAlphanumeric, validateLetters, validateNumbers, verifyAuth };
@@ -0,0 +1,3 @@
1
+ export interface AppKeyConfig {
2
+ appKey: string;
3
+ }
@@ -1,26 +1,13 @@
1
1
  export interface ArexVueCoreOptions {
2
- /**
3
- * The authentication endpoints.
4
- */
2
+ appKey: string;
5
3
  endpoints: {
6
- /** The login endpoint. */
7
4
  login: string;
8
- /** The token refresh endpoint. */
9
5
  refresh: string;
10
- /** The logout endpoint. */
11
6
  logout: string;
12
7
  };
13
- /**
14
- * The key prefixes for storing tokens.
15
- */
16
8
  tokenKeys: {
17
- /** The access token key prefix. */
18
9
  accessToken: string;
19
- /** The refresh token key prefix. */
20
10
  refreshToken: string;
21
11
  };
22
- /**
23
- * The base URL for the API.
24
- */
25
12
  apiUrl: string;
26
13
  }
@@ -1,3 +1,13 @@
1
+ export type SessionPreference = "local" | "session";
1
2
  export type SessionConfig = {
2
3
  SESSION_ID: string;
4
+ PERSISTENCE: SessionPreference;
3
5
  };
6
+ export interface InternalSessionState {
7
+ sessionId: string;
8
+ persistencePreference: SessionPreference;
9
+ }
10
+ export interface SessionConfigObject {
11
+ sessionId?: string;
12
+ persistencePreference?: SessionPreference;
13
+ }
@@ -7,3 +7,4 @@ export * from './Auth';
7
7
  export * from './SessionConfig';
8
8
  export * from './DecodedJwtPayload';
9
9
  export * from './ArexVueCoreOptions';
10
+ export * from './AppKeyConfig';
@@ -1,4 +1,4 @@
1
- import { SessionPreference } from "@config/global/sessionConfig";
1
+ import { SessionPreference } from "@/types/SessionConfig";
2
2
  /**
3
3
  * Clears all stored authentication data (access and refresh tokens)
4
4
  * from either sessionStorage or localStorage based on the provided preference.
@@ -49,10 +49,8 @@ export declare const storeAuthRefreshToken: (token: string, secretKey: string, p
49
49
  * Verifies the validity and expiration of the current authentication token.
50
50
  * If the token is missing, invalid, or expired, appropriate errors are thrown and credentials are cleaned.
51
51
  *
52
- * @param {string} secretKey - The secret key used for token decryption.
53
- * @param {SessionPreference} preference - The current session persistence preference.
54
52
  * @returns {Promise<boolean>} True if the token is valid and unexpired.
55
53
  * @throws {Error} "TOKEN_MISSING" if no token is found, "TOKEN_EXPIRED" if the token has expired,
56
54
  * "TOKEN_INVALID" if the token format is invalid.
57
55
  */
58
- export declare const verifyAuth: (secretKey?: string, preference?: SessionPreference) => Promise<boolean>;
56
+ export declare const verifyAuth: () => Promise<boolean>;
@@ -4,16 +4,16 @@
4
4
  * @param key The key under which to store the value.
5
5
  * @param value The value to encrypt and store.
6
6
  * @param secretKey The secret key for encryption.
7
- * @param isRememberMe If true, uses localStorage; otherwise, uses sessionStorage.
7
+ * @param persistent If true, uses localStorage; otherwise, uses sessionStorage.
8
8
  * @returns A promise that resolves when the item is stored. Throws an error if it fails.
9
9
  */
10
- export declare function storeEncryptedItem(key: string, value: string, secretKey: string, isRememberMe: boolean): Promise<void>;
10
+ export declare function storeEncryptedItem(key: string, value: string, secretKey: string, persistent: boolean): Promise<void>;
11
11
  /**
12
12
  * Retrieves and decrypts a value from local or session storage.
13
13
  * Assumes the `window` environment is available.
14
14
  * @param key The key of the item to retrieve.
15
15
  * @param secretKey The secret key for decryption.
16
- * @param isRememberMe If true, searches localStorage; otherwise, sessionStorage.
16
+ * @param persistent If true, searches localStorage; otherwise, sessionStorage.
17
17
  * @returns A promise that resolves with the decrypted value or null if not found or decryption fails.
18
18
  */
19
- export declare function getDecryptedItem(key: string, secretKey: string, isRememberMe: boolean): Promise<string | null>;
19
+ export declare function getDecryptedItem(key: string, secretKey: string, persistent: boolean): Promise<string | null>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arex95/vue-core",
3
- "version": "1.1.27",
3
+ "version": "1.1.30",
4
4
  "description": "Opinionated Vue Core",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",