Skip to content

feat(sharing): Allow custom share tokens for public links#3348

Open
Chartman123 wants to merge 3 commits into
nextcloud:mainfrom
alexander-rebello:feature/custom-share-links
Open

feat(sharing): Allow custom share tokens for public links#3348
Chartman123 wants to merge 3 commits into
nextcloud:mainfrom
alexander-rebello:feature/custom-share-links

Conversation

@Chartman123
Copy link
Copy Markdown
Collaborator

Closes #2815

This adds admin-gated custom tokens for public Forms share links. By default the feature is disabled, so existing instances keep the current random-token behavior. When enabled by an admin, form owners can edit the token of an existing public link directly in the sharing sidebar, save it explicitly, and the old URL becomes invalid immediately.

It also adds the necessary backend support for token updates, keeps public-link routing compatible with custom tokens, and includes tests plus API documentation updates.

New PR replacing #3311

@Chartman123 Chartman123 added this to the 5.3 milestone May 20, 2026
@Chartman123 Chartman123 added enhancement New feature or request 3. to review Waiting for reviews feature: 👥 sharing settings labels May 20, 2026
@Chartman123 Chartman123 requested review from pringelmann and susnux May 20, 2026 19:41
$existingShare = $this->shareMapper->findPublicShareByHash($token);
if ($existingShare->getId() !== $formShare->getId()) {
$this->logger->debug('Share hash already exists.');
throw new OCSBadRequestException('Share hash exists, please retry.');
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should not leak that information

Suggested change
throw new OCSBadRequestException('Share hash exists, please retry.');
throw new OCSBadRequestException('Invalid share token');

Copy link
Copy Markdown
Collaborator Author

@Chartman123 Chartman123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexander-rebello I added some comments/suggestions. Most of them, especially the suggestions need the rebase on current main first.

Comment thread lib/Constants.php
self::CONFIG_KEY_RESTRICTCREATION
];

public const PUBLIC_SHARE_TOKEN_MIN_LENGTH = 8;
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be consistens with file sharing we should use 1 here, but we need to add bruteforce protection on the PageController

Comment on lines +40 to +42
public function getAllowCustomPublicToken(): bool {
return json_decode($this->config->getAppValue($this->appName, Constants::CONFIG_KEY_ALLOWCUSTOMPUBLICTOKEN, 'false'));
}
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use IAppConfig and getAppValueBool() here.

Suggested change
public function getAllowCustomPublicToken(): bool {
return json_decode($this->config->getAppValue($this->appName, Constants::CONFIG_KEY_ALLOWCUSTOMPUBLICTOKEN, 'false'));
}
public function getAllowCustomPublicToken(): bool {
return json_decode($this->appConfig->getAppValueBool(Constants::CONFIG_KEY_ALLOWCUSTOMPUBLICTOKEN, false));
}

Comment on lines +88 to +101
<NcActionButton
v-if="appConfig.allowCustomPublicShareTokens"
:disabled="
locked
|| !isCurrentUserOwner
|| isShareTokenSaving(share)
|| !isShareTokenDirty(share)
"
@click="updateShareToken(share)">
<template #icon>
<IconCheck :size="20" />
</template>
{{ t('forms', 'Save token') }}
</NcActionButton>
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Value should be updated on input (with debounce).

Follow the implementation for file shares.

Image

Code can be found here:

https://github.com/nextcloud/server/blob/478d12c7d9f8064587c86d33f8985deaf0fabb9b/apps/files_sharing/src/views/SharingDetailsTab.vue#L120-L133

import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
import NcTextField from '@nextcloud/vue/components/NcTextField'
import IconAccountMultiple from 'vue-material-design-icons/AccountMultipleOutline.vue'
import IconCheck from 'vue-material-design-icons/Check.vue'
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use Material Symbol here

Suggested change
import IconCheck from 'vue-material-design-icons/Check.vue'
import IconCheck from '@material-symbols/svg-400/outlined/check.svg?raw'

NcIconSvgWrapper,
FormsIcon,
IconAccountMultiple,
IconCheck,
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
IconCheck,

Comment on lines 303 to 305
emits: ['addShare', 'updateShare', 'removeShare', 'update:formProp'],

setup() {
return {
FormsIcon,
IconCopyAll,
IconPlus,
IconCodeBrackets,
IconDelete,
IconLinkVariant,
IconLinkBoxVariantOutline,
IconAccountMultiple,
IconQr,
}
},

data() {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The icons need to be added to the setup().

Suggested change
emits: ['addShare', 'updateShare', 'removeShare', 'update:formProp'],
setup() {
return {
FormsIcon,
IconCopyAll,
IconPlus,
IconCodeBrackets,
IconDelete,
IconLinkVariant,
IconLinkBoxVariantOutline,
IconAccountMultiple,
IconQr,
}
},
data() {
emits: ['addShare', 'updateShare', 'removeShare', 'update:formProp'],
setup() {
return {
IconCheck,
}
},
data() {

Comment thread src/FormsSettings.vue
Comment on lines 35 to +41
<NcCheckboxRadioSwitch
ref="switchAllowCustomPublicShareTokens"
v-model="appConfig.allowCustomPublicShareTokens"
type="switch"
@update:modelValue="onAllowCustomPublicShareTokensChange">
{{ t('forms', 'Allow custom public share tokens') }}
</NcCheckboxRadioSwitch>
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<NcCheckboxRadioSwitch
ref="switchAllowCustomPublicShareTokens"
v-model="appConfig.allowCustomPublicShareTokens"
type="switch"
@update:modelValue="onAllowCustomPublicShareTokensChange">
{{ t('forms', 'Allow custom public share tokens') }}
</NcCheckboxRadioSwitch>
<NcCheckboxRadioSwitch
v-model="appConfig.allowCustomPublicShareTokens"
:loading="loading.allowCustomPublicShareTokens"
type="switch"
@update:modelValue="onAllowCustomPublicShareTokensChange">
{{ t('forms', 'Allow custom public share tokens') }}
</NcCheckboxRadioSwitch>

Comment thread src/FormsSettings.vue
Comment on lines +125 to 131
async onAllowCustomPublicShareTokensChange(newVal) {
const el = this.$refs.switchAllowCustomPublicShareTokens
el.loading = true
await this.saveAppConfig('allowCustomPublicShareTokens', newVal)
el.loading = false
},

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
async onAllowCustomPublicShareTokensChange(newVal) {
const el = this.$refs.switchAllowCustomPublicShareTokens
el.loading = true
await this.saveAppConfig('allowCustomPublicShareTokens', newVal)
el.loading = false
},
async onAllowCustomPublicShareTokensChange(newVal) {
this.loading.allowCustomPublicShareTokens= true
await this.saveAppConfig('allowCustomPublicShareTokens', newVal)
this.loading.allowCustomPublicShareTokens= false
},

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3. to review Waiting for reviews enhancement New feature or request feature: 👥 sharing settings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Readable links when sharing

3 participants