PageSourceSearch

https://www.americaneagle.ae/scripts/helpers/user-utils.js

js americaneagle.ae collected 2026-10-02 13:25:27 UTC 6,663 bytes, 243 lines download raw bytes

1import { setCookie, getCookie } from '../utils.js';
2
3let cachedAuthToken = null;
4
5/** Utility to set secure key */
6function setKey(name, value, exdays) {
7  setCookie(name, value, exdays, window.location.hostname, true);
8}
9
10/** Utility to get secure key */
11function getKey(name) {
12  return getCookie(name);
13}
14
15/** Utility to clear secure key */
16function clearKeyCookie(name) {
17  setCookie(name, '', -1, window.location.hostname, true);
18}
19
20/** Convert ArrayBuffer to Base64 */
21function bufToBase64(buf) {
22  return btoa(String.fromCharCode(...new Uint8Array(buf)));
23}
24
25/** Convert Base64 to ArrayBuffer */
26function base64ToBuf(b64) {
27  return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
28}
29
30/**
31 * Import a raw Base64-encoded AES key into CryptoKey format
32 * @param {string} b64Key - Base64-encoded AES key
33 * @returns {Promise<CryptoKey>} - Imported CryptoKey
34 */
35async function importKey(b64Key) {
36  const raw = base64ToBuf(b64Key);
37  return crypto.subtle.importKey(
38    'raw',
39    raw,
40    { name: 'AES-CBC' },
41    true,
42    ['encrypt', 'decrypt'],
43  );
44}
45
46/**
47 * Decrypt data using AES-CBC
48 * @param {string} encryptedBase64 - Base64 ciphertext
49 * @param {string} ivBase64 - Base64 initialization vector
50 * @param {CryptoKey} key - AES key
51 * @returns {Promise<string>} - Decrypted plaintext
52 */
53async function decrypt(encryptedBase64, ivBase64, key) {
54  const encrypted = base64ToBuf(encryptedBase64);
55  const encryptionIv = base64ToBuf(ivBase64);
56  try {
57    const decrypted = await window.crypto.subtle.decrypt(
58      { name: 'AES-CBC', iv: encryptionIv },
59      key,
60      encrypted,
61    );
62    return new TextDecoder().decode(decrypted);
63  } catch (e) {
64    console.warn('Decryption failed:', e);
65    return null;
66  }
67}
68
69/**
70 * Initialize and retrieve the decrypted auth user token.
71 * Reads the encrypted token, encryptionIv, and stored key from cookies/session storage,
72 * then decrypts it and caches the result.
73 *
74 * @returns {Promise<string|null>} The decrypted token or null if unavailable.
75 */
76async function initAuthUserToken() {
77  try {
78    const encrypted = getCookie('auth_user_token');
79    const encryptionIv = getCookie('iv_auth_user_token');
80    const exportedKey = getKey('sKey');
81
82    if (!encrypted || !encryptionIv || !exportedKey) return null;
83
84    const sKey = await importKey(exportedKey);
85    cachedAuthToken = await decrypt(encrypted, encryptionIv, sKey);
86    return cachedAuthToken;
87  } catch {
88    return null;
89  }
90}
91
92/** Generate a new AES-256 key */
93async function generateKey() {
94  return window.crypto.subtle.generateKey(
95    { name: 'AES-CBC', length: 256 },
96    true,
97    ['encrypt', 'decrypt'],
98  );
99}
100
101/** Export AES key to Base64 */
102async function exportKey(key) {
103  const raw = await crypto.subtle.exportKey('raw', key);
104  return bufToBase64(raw);
105}
106
107/**
108 * Retrieve or generate AES key used for encryption
109 * Stores key in cookie if not already present
110 * @returns {Promise<CryptoKey>}
111 */
112async function getCryptoKey() {
113  let exportedKey = getKey('sKey');
114  let sKey;
115
116  if (!exportedKey) {
117    sKey = await generateKey();
118    exportedKey = await exportKey(sKey);
119    setKey('sKey', exportedKey, 7);
120  } else {
121    sKey = await importKey(exportedKey);
122  }
123
124  return sKey;
125}
126
127/**
128 * Encrypt text using AES-CBC with random encryptionIv
129 */
130async function encrypt(text, key) {
131  const encryptionIv = window.crypto.getRandomValues(new Uint8Array(16));
132  const encoded = new TextEncoder().encode(text);
133
134  const encrypted = await window.crypto.subtle.encrypt(
135    { name: 'AES-CBC', iv: encryptionIv },
136    key,
137    encoded,
138  );
139
140  return {
141    encrypted: bufToBase64(encrypted),
142    encryptionIv: bufToBase64(encryptionIv),
143  };
144}
145
146/**
147 * Retrieve and decrypt a secure item stored in sessionStorage
148 * @param {string} key - The key name
149 * @returns {Promise<string|null>} - Decrypted value or null
150 */
151export async function getSecureItem(key) {
152  try {
153    const encrypted = sessionStorage.getItem(key);
154    const encryptionIv = sessionStorage.getItem(`iv_${key}`);
155    const exportedKey = getKey('sKey');
156
157    if (!encrypted || !encryptionIv || !exportedKey) return null;
158
159    const sKey = await importKey(exportedKey);
160    return await decrypt(encrypted, encryptionIv, sKey);
161  } catch {
162    return null;
163  }
164}
165
166/**
167 * Encrypt and store a secure item in sessionStorage
168 * @param {string} key - The key name
169 * @param {string} value - Plaintext value
170 */
171export async function setSecureItem(key, value) {
172  if (typeof value !== 'string') return;
173
174  const sKey = await getCryptoKey();
175  const { encrypted, encryptionIv }
175 = await encrypt(value, sKey);
176
177  sessionStorage.setItem(key, encrypted);
178  sessionStorage.setItem(`iv_${key}`, encryptionIv);
179}
180
181// Aliases for email and userId using generic functions
182export const setUserEmail = (key, value) => setSecureItem(key, value);
183export const getUserEmail = (key) => getSecureItem(key);
184
185export const setUserId = (key, value) => setSecureItem(key, value);
186export const getUserId = (key) => getSecureItem(key);
187
188// Restrict special characters not allowed in standard emails
189export function restrictSpecialCharacterEmail(emailInp) {
190  emailInp.addEventListener('input', () => {
191    emailInp.value = emailInp.value.replace(/[^a-zA-Z0-9@._\-+]/g, '');
192  });
193}
194
195// Restrict input to allow only letters, spaces, apostrophes, and hyphens
196export function restrictSpecialCharacterText(input) {
197  input.addEventListener('input', () => {
198    input.value = input.value.replace(/[^0-9\s.,!?'"()\-\p{L}]/gu, '');
199  });
200}
201
202/** Get cached auth user token */
203export function getAuthUserToken() {
204  return cachedAuthToken;
205}
206
207/** Clear cookies & cache */
208function clearAuthUserToken() {
209  setCookie('auth_user_token', '', -1);
210  setCookie('iv_auth_user_token', '', -1);
211  clearKeyCookie('sKey');
212  // Remove cached token
213  cachedAuthToken = null;
214}
215
216/**
217 * Encrypt and set auth user token in cookies
218 * @param {string} data - Plaintext token
219 * @param {number} cookieExpiryDays - Expiry in days
220 */
221export async function setAuthUserToken(data, cookieExpiryDays) {
222  // If clearing empty string or negative expiry, just wipe everything
223  if (!data || cookieExpiryDays < 0) {
224    clearAuthUserToken();
225    return;
226  }
227
228  if (typeof data !== 'string') return;
229
230  const sKey = await getCryptoKey();
231  const { encrypted, encryptionIv } = await encrypt(data, sKey);
232
233  // Store encrypted token + encryptionIv in cookies
234  setCookie('auth_user_token', encrypted, cookieExpiryDays);
235  setCookie('iv_auth_user_token', encryptionIv, cookieExpiryDays);
236  // Update cache immediately with plain token
237  cachedAuthToken = data;
238}
239
240// At app startup
241(async () => {
242  await initAuthUserToken();
243})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.