{"version":3,"file":"index.cjs","names":["stringToArrayBuffer","arrayBufferToBase64","fromB64Url","arrayBufferToString","now","randomBytes","sha256"],"sources":["../../src/utils/index.ts"],"sourcesContent":["import type {\n  AuthState,\n  CallbackParams,\n  IdTokenClaims,\n  MonoCloudSession,\n  MonoCloudUser,\n} from '../types';\nimport {\n  arrayBufferToBase64,\n  arrayBufferToString,\n  fromB64Url,\n  now,\n  randomBytes,\n  sha256,\n  stringToArrayBuffer,\n} from './internal';\n\nconst PBKDF2_ITERATIONS = 100_000;\nconst SALT_LENGTH = 16;\nconst GCM_IV_LENGTH = 12;\n\nconst deriveEncryptionKey = async (\n  secret: string,\n  salt: Uint8Array\n): Promise<CryptoKey> => {\n  const baseKey = await crypto.subtle.importKey(\n    'raw',\n    stringToArrayBuffer(secret) as BufferSource,\n    'PBKDF2',\n    false,\n    ['deriveKey']\n  );\n\n  return crypto.subtle.deriveKey(\n    {\n      name: 'PBKDF2',\n      salt: salt as BufferSource,\n      iterations: PBKDF2_ITERATIONS,\n      hash: 'SHA-256',\n    },\n    baseKey,\n    { name: 'AES-GCM', length: 256 },\n    false,\n    ['encrypt', 'decrypt']\n  );\n};\n\n/**\n * Parses callback parameters from a URL, a URLSearchParams object, or a query string.\n */\nexport const parseCallbackParams = (\n  queryOrUrl: string | URL | URLSearchParams\n): CallbackParams => {\n  let params;\n\n  if (queryOrUrl instanceof URL) {\n    params = queryOrUrl.searchParams;\n  } else if (queryOrUrl instanceof URLSearchParams) {\n    params = queryOrUrl;\n  } else {\n    try {\n      params = new URL(queryOrUrl).searchParams;\n    } catch {\n      // eslint-disable-next-line no-param-reassign\n      queryOrUrl =\n        queryOrUrl.startsWith('?') || queryOrUrl.startsWith('#')\n          ? queryOrUrl.substring(1)\n          : queryOrUrl;\n      params = new URLSearchParams(queryOrUrl);\n    }\n  }\n\n  const expiresIn = params.get('expires_in');\n\n  return {\n    state: params.get('state') ?? undefined,\n    accessToken: params.get('access_token') ?? undefined,\n    idToken: params.get('id_token') ?? undefined,\n    refreshToken: params.get('refresh_token') ?? undefined,\n    sessionState: params.get('session_state') ?? undefined,\n    expiresIn: expiresIn ? parseInt(expiresIn, 10) : undefined,\n    code: params.get('code') ?? undefined,\n    scope: params.get('scope') ?? undefined,\n    error: params.get('error') ?? undefined,\n    errorDescription: params.get('error_description') ?? undefined,\n  };\n};\n\n/**\n * Encrypts a given string using a secret with AES-GCM.\n *\n * @param data - The plaintext data to encrypt.\n * @param secret - The secret used to derive the encryption key.\n * @returns Base64-encoded ciphertext.\n */\nexport const encrypt = async (\n  data: string,\n  secret: string\n): Promise<string> => {\n  const salt = crypto.getRandomValues(new Uint8Array(SALT_LENGTH));\n  const iv = crypto.getRandomValues(new Uint8Array(GCM_IV_LENGTH));\n  const plaintextBuffer = stringToArrayBuffer(data);\n  const key = await deriveEncryptionKey(secret, salt);\n\n  const ciphertext = await crypto.subtle.encrypt(\n    {\n      name: 'AES-GCM',\n      iv,\n    },\n    key,\n    plaintextBuffer as BufferSource\n  );\n\n  const resultBuffer = new Uint8Array(\n    salt.byteLength + iv.byteLength + ciphertext.byteLength\n  );\n  resultBuffer.set(salt, 0);\n  resultBuffer.set(iv, salt.byteLength);\n  resultBuffer.set(new Uint8Array(ciphertext), salt.byteLength + iv.byteLength);\n\n  return arrayBufferToBase64(resultBuffer);\n};\n\n/**\n * Decrypts an encrypted string using a secret with AES-GCM.\n *\n * @param encrypted - The ciphertext to decrypt.\n * @param secret - The secret used to derive the decryption key.\n *\n * @returns Decrypted plaintext string or undefined if decryption fails.\n */\nexport const decrypt = async (\n  encrypted: string,\n  secret: string\n): Promise<string | undefined> => {\n  try {\n    const ciphertextBuffer = Uint8Array.from(atob(fromB64Url(encrypted)), c =>\n      c.charCodeAt(0)\n    );\n\n    if (ciphertextBuffer.byteLength <= SALT_LENGTH + GCM_IV_LENGTH) {\n      return undefined;\n    }\n\n    const salt = ciphertextBuffer.slice(0, SALT_LENGTH);\n    const iv = ciphertextBuffer.slice(SALT_LENGTH, SALT_LENGTH + GCM_IV_LENGTH);\n    const encryptedPayload = ciphertextBuffer.slice(\n      SALT_LENGTH + GCM_IV_LENGTH\n    );\n    const key = await deriveEncryptionKey(secret, salt);\n    const decryptedBuffer = await crypto.subtle.decrypt(\n      {\n        name: 'AES-GCM',\n        iv,\n      },\n      key,\n      encryptedPayload\n    );\n    return arrayBufferToString(decryptedBuffer);\n  } catch {\n    return undefined;\n  }\n};\n\n/**\n * Encrypts a MonoCloud session object with a secret and optional time-to-live (TTL).\n *\n * @param session - The session object to encrypt.\n * @param secret - The secret used for encryption.\n * @param ttl - Optional time-to-live in seconds, after which the session expires.\n * @returns Encrypted session string.\n */\nexport const encryptSession = (\n  session: MonoCloudSession,\n  secret: string,\n  ttl?: number\n): Promise<string> => {\n  let expiresAt;\n\n  if (typeof ttl === 'number') {\n    expiresAt = now() + ttl;\n  }\n  return encrypt(JSON.stringify({ session, expiresAt }), secret);\n};\n\n/**\n * Decrypts an encrypted MonoCloud session.\n *\n * @param encryptedSession - The encrypted session string to decrypt.\n * @param secret - The secret used for decryption.\n *\n * @returns Session object on success.\n *\n * @throws If decryption fails or the session has expired.\n */\nexport const decryptSession = async (\n  encryptedSession: string,\n  secret: string\n): Promise<MonoCloudSession> => {\n  const decryptedText = await decrypt(encryptedSession, secret);\n\n  if (!decryptedText) {\n    throw new Error('Invalid session data');\n  }\n\n  let payload: { session: MonoCloudSession; expiresAt?: number };\n  try {\n    payload = JSON.parse(decryptedText);\n  } catch {\n    throw new Error('Invalid session data');\n  }\n\n  const { session, expiresAt } = payload;\n\n  if (!session) {\n    throw new Error('Invalid session data');\n  }\n\n  if (typeof expiresAt === 'number' && expiresAt < now()) {\n    throw new Error('Session Expired');\n  }\n\n  return session;\n};\n\n/**\n * Encrypts an AuthState object with a secret and optional time-to-live (TTL).\n *\n * @param authState - A type that extends the AuthState interface.\n * @param secret - The secret used for encryption.\n * @param ttl - Optional time-to-live in seconds, after which the auth state expires.\n *\n * @returns Encrypted auth state string.\n */\nexport const encryptAuthState = <T extends AuthState>(\n  authState: T,\n  secret: string,\n  ttl?: number\n): Promise<string> => {\n  let expiresAt;\n\n  if (typeof ttl === 'number') {\n    expiresAt = now() + ttl;\n  }\n\n  return encrypt(JSON.stringify({ authState, expiresAt }), secret);\n};\n\n/**\n * Decrypts an encrypted AuthState.\n *\n * @param encryptedAuthState - The encrypted auth state string to decrypt.\n * @param secret - The secret used for decryption.\n *\n * @returns State object on success.\n *\n * @throws If decryption fails or the auth state has expired.\n *\n */\nexport const decryptAuthState = async <T extends AuthState>(\n  encryptedAuthState: string,\n  secret: string\n): Promise<T> => {\n  const decryptedText = await decrypt(encryptedAuthState, secret);\n\n  if (!decryptedText) {\n    throw new Error('Invalid auth state');\n  }\n\n  let payload: { authState: T; expiresAt?: number };\n  try {\n    payload = JSON.parse(decryptedText);\n  } catch {\n    throw new Error('Invalid auth state');\n  }\n\n  const { authState, expiresAt } = payload;\n\n  if (!authState) {\n    throw new Error('Invalid auth state');\n  }\n\n  if (typeof expiresAt === 'number' && expiresAt < now()) {\n    throw new Error('Auth state expired');\n  }\n\n  return authState;\n};\n\n/**\n * Checks if a user is a member of a specified group or groups.\n *\n * @param user - The user.\n * @param groups - An array of group names or IDs to check membership against.\n * @param groupsClaim - The claim in the user object that contains groups.\n * @param matchAll - If `true`, requires the user to be in all specified groups; if `false`, checks if the user is in at least one of the groups.\n *\n * @returns `true` if the user is in the specified groups, `false` otherwise.\n */\nexport const isUserInGroup = (\n  user: MonoCloudUser | IdTokenClaims,\n  groups: string[],\n  groupsClaim = 'groups',\n  matchAll = false\n): boolean => {\n  const userGroups = (user[groupsClaim] ?? []) as (\n    string | { id: string; name: string }\n  )[];\n\n  if (!Array.isArray(groups) || groups.length === 0) {\n    return true;\n  }\n\n  if (!Array.isArray(userGroups) || userGroups.length === 0) {\n    return false;\n  }\n\n  let matched = false;\n\n  for (const expectedGroup of groups) {\n    const userInGroup = userGroups.some(\n      g =>\n        (typeof g === 'string' && g === expectedGroup) ||\n        (typeof g === 'object' &&\n          (g.id === expectedGroup || g.name === expectedGroup))\n    );\n\n    if (!matchAll && userInGroup) {\n      return userInGroup;\n    }\n\n    if (matchAll && !userInGroup) {\n      return false;\n    }\n\n    matched = userInGroup;\n  }\n\n  return matched;\n};\n\n/**\n * Generates a random state string.\n */\nexport const generateState = (): string => randomBytes(32);\n\n/**\n * Generates a PKCE (Proof Key for Code Exchange) code verifier and code challenge.\n */\nexport const generatePKCE = async (): Promise<{\n  codeVerifier: string;\n  codeChallenge: string;\n}> => {\n  const codeVerifier = randomBytes(32);\n  return {\n    codeVerifier,\n    codeChallenge: await sha256(codeVerifier),\n  };\n};\n\n/**\n * Generates a random nonce string.\n */\nexport const generateNonce = (): string => randomBytes(32);\n\n/**\n * @ignore\n * Merges multiple arrays of strings, removing duplicates.\n *\n * @param args - List of arrays to merge.\n *\n * @returns A new array containing unique strings from both input arrays, or `undefined` if both inputs are `undefined`.\n */\nexport const mergeArrays = (\n  ...args: (string[] | undefined)[]\n): string[] | undefined => {\n  const arrays = args.filter(x => Array.isArray(x));\n  return arrays.length > 0\n    ? Array.from(new Set(arrays.reduce((acc, x) => [...acc, ...x], [])))\n    : undefined;\n};\n"],"mappings":"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"}