{"version":3,"file":"index.cjs","sources":["../lib/requestToken.ts","../lib/csp-nonce.ts","../lib/guest.ts","../lib/user.ts"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\nimport { emit, subscribe, unsubscribe } from '@nextcloud/event-bus'\nimport { generateUrl } from '@nextcloud/router'\n\nexport interface CsrfTokenObserver {\n\t(token: string): void\n}\n\n_subscribeToTokenUpdates() // TODO: remove once we drop support for Nextcloud 33 and before\n\n/**\n * Get current request token\n *\n * @return Current request token or null if not set\n */\nexport function getRequestToken(): string | null {\n\tif (globalThis._nc_auth_requestToken) {\n\t\treturn globalThis._nc_auth_requestToken\n\t}\n\n\tif (globalThis.document) {\n\t\t// for service workers or other contexts without DOM we need to safeguard this\n\t\treturn document.head.dataset.requesttoken ?? null\n\t}\n\treturn null\n}\n\n/**\n * Set a new CSRF token (e.g. because of session refresh).\n * This also emits an event bus event for the updated token.\n *\n * @param token - The new token\n * @throws {Error} - If the passed token is not a potential valid token\n */\nexport function setRequestToken(token: string): void {\n\tif (!token || typeof token !== 'string') {\n\t\tthrow new Error('Invalid CSRF token given', { cause: { token } })\n\t}\n\n\tif (globalThis._nc_auth_requestToken === token) {\n\t\t// token is the same as before, no need to update and especially no need to notify the observers\n\t\treturn\n\t}\n\n\tglobalThis._nc_auth_requestToken = token\n\tif (globalThis.document) {\n\t\t// For DOM environments we also set the token to the DOM, so it is available for legacy code\n\t\tdocument.head.dataset.requesttoken = token\n\t}\n\n\temit('csrf-token-update', { token, _internal: true })\n}\n\n/**\n * Fetch the request token from the API.\n * This does also set it on the current context, see `setRequestToken`.\n *\n * @throws {Error} - If the request failed\n */\nexport async function fetchRequestToken(): Promise<string> {\n\tconst url = generateUrl('/csrftoken')\n\n\tconst response = await fetch(url)\n\tif (!response.ok) {\n\t\tthrow new Error('Could not fetch CSRF token from API', { cause: response })\n\t}\n\n\ttry {\n\t\tconst { token } = await response.json()\n\t\tsetRequestToken(token)\n\t\treturn token\n\t} catch (error) {\n\t\tthrow new Error('Could not parse CSRF token from API response', { cause: error })\n\t}\n}\n\n/**\n * Add an observer which is called when the CSRF token changes\n *\n * @param observer The observer\n * @return A function to unsubscribe the observer\n */\nexport function onRequestTokenUpdate(observer: CsrfTokenObserver): () => void {\n\tconst wrapper = async ({ token }: { token: string }) => {\n\t\ttry {\n\t\t\tobserver(token)\n\t\t} catch (error) {\n\t\t\t// we cannot use the logger as the logger uses this library = circular dependency\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.error('Error updating CSRF token observer', error)\n\t\t}\n\t}\n\n\tsubscribe('csrf-token-update', wrapper)\n\treturn () => unsubscribe('csrf-token-update', wrapper)\n}\n\n/**\n * Subscribe to token update events from server.\n *\n * @todo - This is legacy and not needed once all supported server versions use `setRequestToken` of this library.\n */\nfunction _subscribeToTokenUpdates(): void {\n\t// Listen to server event and keep token in sync\n\tsubscribe('csrf-token-update', ({ token, _internal }) => {\n\t\tif (!_internal) {\n\t\t\t// Only update the token if the event is not emitted from this library, otherwise we would end in a loop\n\t\t\tsetRequestToken(token)\n\t\t}\n\t})\n}\n","/**\n * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\nimport { getRequestToken } from './requestToken.ts'\n\n/**\n * Get the CSP nonce for script loading\n *\n * @return Current nonce if set\n * @example When using webpack this can be used to allow webpack to dynamically load additional modules:\n * ```js\n * import { getCSPNonce } from '@nextcloud/auth'\n *\n * __webpack_nonce__ = getCSPNonce()\n * ```\n */\nexport function getCSPNonce(): string | undefined {\n\tconst meta = document?.querySelector<HTMLMetaElement>('meta[name=\"csp-nonce\"]')\n\t// backwards compatibility with older Nextcloud versions (before Nextcloud 30)\n\tif (!meta) {\n\t\tconst token = getRequestToken()\n\t\treturn token ? btoa(token) : undefined\n\t}\n\treturn meta.nonce\n}\n","/*!\n * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\nimport type { NextcloudUser } from './user.ts'\n\nimport { getBuilder } from '@nextcloud/browser-storage'\nimport { emit, subscribe } from '@nextcloud/event-bus'\n\nconst browserStorage = getBuilder('public').persist().build()\n\nclass GuestUser implements NextcloudUser {\n\tprivate _displayName: string | null\n\treadonly uid: string\n\treadonly isAdmin: boolean\n\n\tconstructor() {\n\t\tif (!browserStorage.getItem('guestUid')) {\n\t\t\tbrowserStorage.setItem('guestUid', randomUUID())\n\t\t}\n\n\t\tthis._displayName = browserStorage.getItem('guestNickname') || ''\n\t\tthis.uid = browserStorage.getItem('guestUid') || randomUUID()\n\t\tthis.isAdmin = false\n\n\t\tsubscribe('user:info:changed', (guest) => {\n\t\t\tthis._displayName = guest.displayName\n\t\t\tbrowserStorage.setItem('guestNickname', guest.displayName || '')\n\t\t})\n\t}\n\n\tget displayName(): string | null {\n\t\treturn this._displayName\n\t}\n\n\tset displayName(displayName: string) {\n\t\tthis._displayName = displayName\n\t\tbrowserStorage.setItem('guestNickname', displayName)\n\t\temit('user:info:changed', this)\n\t}\n}\n\nlet currentUser: NextcloudUser | undefined\n\n/**\n * Get the currently Guest user or null if not logged in\n */\nexport function getGuestUser(): NextcloudUser {\n\tif (!currentUser) {\n\t\tcurrentUser = new GuestUser()\n\t}\n\n\treturn currentUser\n}\n\n/**\n * Get the guest nickname for public pages\n */\nexport function getGuestNickname(): string | null {\n\treturn getGuestUser()?.displayName || null\n}\n\n/**\n * Set the guest nickname for public pages\n *\n * @param nickname - The nickname to set\n */\nexport function setGuestNickname(nickname: string): void {\n\tif (!nickname || nickname.trim().length === 0) {\n\t\tthrow new Error('Nickname cannot be empty')\n\t}\n\n\tgetGuestUser().displayName = nickname\n}\n\n/**\n * Reset the guest user state.\n *\n * @internal\n */\nexport function resetGuestUser(): void {\n\tcurrentUser = undefined\n\tbrowserStorage.removeItem('guestUid')\n\tbrowserStorage.removeItem('guestNickname')\n}\n\n/**\n * Generate a random UUID (version 4) if the crypto API is not available.\n * If the crypto API is available, it uses the less secure `randomUUID` method.\n * Crypto API is available in modern browsers on secure contexts (HTTPS).\n *\n * @return A random UUID.\n */\nfunction randomUUID(): string {\n\t// Use the crypto API if available\n\tif (globalThis.crypto?.randomUUID) {\n\t\treturn globalThis.crypto.randomUUID()\n\t}\n\n\t// Generate a random UUID (version 4)\n\treturn 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n\t\tconst r = Math.random() * 16 | 0\n\t\tconst v = c === 'x' ? r : (r & 0x3 | 0x8)\n\t\treturn v.toString(16)\n\t})\n}\n","/**\n * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\nexport interface NextcloudUser {\n\tuid: string\n\tdisplayName: string | null\n\tisAdmin: boolean\n}\n\nlet currentUser: NextcloudUser | null | undefined\n\n/**\n * @param el - The element\n * @param attribute - The attribute to fetch\n */\nfunction getAttribute(el: HTMLHeadElement | undefined, attribute: string): string | null {\n\tif (el) {\n\t\treturn el.getAttribute(attribute)\n\t}\n\n\treturn null\n}\n\n/**\n * Get the currently logged in Nextcloud user or null if not logged in\n */\nexport function getCurrentUser(): NextcloudUser | null {\n\tif (currentUser !== undefined) {\n\t\treturn currentUser\n\t}\n\n\tconst head = document?.getElementsByTagName('head')[0]\n\tif (!head) {\n\t\treturn null\n\t}\n\n\t// No user logged in so cache and return null\n\tconst uid = getAttribute(head, 'data-user')\n\tif (uid === null) {\n\t\tcurrentUser = null\n\t\treturn currentUser\n\t}\n\n\tcurrentUser = {\n\t\tuid,\n\t\tdisplayName: getAttribute(head, 'data-user-displayname'),\n\t\tisAdmin: !!window._oc_isadmin,\n\t} as NextcloudUser\n\n\treturn currentUser\n}\n"],"names":["emit","generateUrl","subscribe","unsubscribe","getBuilder","currentUser"],"mappings":"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"}