Merge pull request #55790 from nextcloud/feat/webhook-tokens

Feat(webhook_listeners): add auth tokens to webhook call
This commit is contained in:
Andy Scherzinger 2025-11-26 23:18:19 +01:00 committed by GitHub
commit 4dffa1a46a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 561 additions and 1 deletions

View file

@ -18,7 +18,7 @@ Administrators can configure webhook listeners via the app's OCS API. The app al
]]>
</description>
<version>1.4.1</version>
<version>1.5.0</version>
<licence>agpl</licence>
<author>Côme Chilliet</author>
<namespace>WebhookListeners</namespace>
@ -49,4 +49,8 @@ Administrators can configure webhook listeners via the app's OCS API. The app al
<admin-delegation>OCA\WebhookListeners\Settings\Admin</admin-delegation>
<admin-delegation-section>OCA\WebhookListeners\Settings\AdminSection</admin-delegation-section>
</settings>
<background-jobs>
<job>OCA\WebhookListeners\BackgroundJobs\WebhookTokenCleanup</job>
</background-jobs>
</info>

View file

@ -9,16 +9,21 @@ return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'OCA\\WebhookListeners\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php',
'OCA\\WebhookListeners\\BackgroundJobs\\WebhookCall' => $baseDir . '/../lib/BackgroundJobs/WebhookCall.php',
'OCA\\WebhookListeners\\BackgroundJobs\\WebhookTokenCleanup' => $baseDir . '/../lib/BackgroundJobs/WebhookTokenCleanup.php',
'OCA\\WebhookListeners\\Command\\ListWebhooks' => $baseDir . '/../lib/Command/ListWebhooks.php',
'OCA\\WebhookListeners\\Controller\\WebhooksController' => $baseDir . '/../lib/Controller/WebhooksController.php',
'OCA\\WebhookListeners\\Db\\AuthMethod' => $baseDir . '/../lib/Db/AuthMethod.php',
'OCA\\WebhookListeners\\Db\\EphemeralToken' => $baseDir . '/../lib/Db/EphemeralToken.php',
'OCA\\WebhookListeners\\Db\\EphemeralTokenMapper' => $baseDir . '/../lib/Db/EphemeralTokenMapper.php',
'OCA\\WebhookListeners\\Db\\WebhookListener' => $baseDir . '/../lib/Db/WebhookListener.php',
'OCA\\WebhookListeners\\Db\\WebhookListenerMapper' => $baseDir . '/../lib/Db/WebhookListenerMapper.php',
'OCA\\WebhookListeners\\Listener\\WebhooksEventListener' => $baseDir . '/../lib/Listener/WebhooksEventListener.php',
'OCA\\WebhookListeners\\Migration\\Version1000Date20240527153425' => $baseDir . '/../lib/Migration/Version1000Date20240527153425.php',
'OCA\\WebhookListeners\\Migration\\Version1001Date20240716184935' => $baseDir . '/../lib/Migration/Version1001Date20240716184935.php',
'OCA\\WebhookListeners\\Migration\\Version1500Date20251007130000' => $baseDir . '/../lib/Migration/Version1500Date20251007130000.php',
'OCA\\WebhookListeners\\ResponseDefinitions' => $baseDir . '/../lib/ResponseDefinitions.php',
'OCA\\WebhookListeners\\Service\\PHPMongoQuery' => $baseDir . '/../lib/Service/PHPMongoQuery.php',
'OCA\\WebhookListeners\\Service\\TokenService' => $baseDir . '/../lib/Service/TokenService.php',
'OCA\\WebhookListeners\\Settings\\Admin' => $baseDir . '/../lib/Settings/Admin.php',
'OCA\\WebhookListeners\\Settings\\AdminSection' => $baseDir . '/../lib/Settings/AdminSection.php',
);

View file

@ -24,16 +24,21 @@ class ComposerStaticInitWebhookListeners
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'OCA\\WebhookListeners\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php',
'OCA\\WebhookListeners\\BackgroundJobs\\WebhookCall' => __DIR__ . '/..' . '/../lib/BackgroundJobs/WebhookCall.php',
'OCA\\WebhookListeners\\BackgroundJobs\\WebhookTokenCleanup' => __DIR__ . '/..' . '/../lib/BackgroundJobs/WebhookTokenCleanup.php',
'OCA\\WebhookListeners\\Command\\ListWebhooks' => __DIR__ . '/..' . '/../lib/Command/ListWebhooks.php',
'OCA\\WebhookListeners\\Controller\\WebhooksController' => __DIR__ . '/..' . '/../lib/Controller/WebhooksController.php',
'OCA\\WebhookListeners\\Db\\AuthMethod' => __DIR__ . '/..' . '/../lib/Db/AuthMethod.php',
'OCA\\WebhookListeners\\Db\\EphemeralToken' => __DIR__ . '/..' . '/../lib/Db/EphemeralToken.php',
'OCA\\WebhookListeners\\Db\\EphemeralTokenMapper' => __DIR__ . '/..' . '/../lib/Db/EphemeralTokenMapper.php',
'OCA\\WebhookListeners\\Db\\WebhookListener' => __DIR__ . '/..' . '/../lib/Db/WebhookListener.php',
'OCA\\WebhookListeners\\Db\\WebhookListenerMapper' => __DIR__ . '/..' . '/../lib/Db/WebhookListenerMapper.php',
'OCA\\WebhookListeners\\Listener\\WebhooksEventListener' => __DIR__ . '/..' . '/../lib/Listener/WebhooksEventListener.php',
'OCA\\WebhookListeners\\Migration\\Version1000Date20240527153425' => __DIR__ . '/..' . '/../lib/Migration/Version1000Date20240527153425.php',
'OCA\\WebhookListeners\\Migration\\Version1001Date20240716184935' => __DIR__ . '/..' . '/../lib/Migration/Version1001Date20240716184935.php',
'OCA\\WebhookListeners\\Migration\\Version1500Date20251007130000' => __DIR__ . '/..' . '/../lib/Migration/Version1500Date20251007130000.php',
'OCA\\WebhookListeners\\ResponseDefinitions' => __DIR__ . '/..' . '/../lib/ResponseDefinitions.php',
'OCA\\WebhookListeners\\Service\\PHPMongoQuery' => __DIR__ . '/..' . '/../lib/Service/PHPMongoQuery.php',
'OCA\\WebhookListeners\\Service\\TokenService' => __DIR__ . '/..' . '/../lib/Service/TokenService.php',
'OCA\\WebhookListeners\\Settings\\Admin' => __DIR__ . '/..' . '/../lib/Settings/Admin.php',
'OCA\\WebhookListeners\\Settings\\AdminSection' => __DIR__ . '/..' . '/../lib/Settings/AdminSection.php',
);

View file

@ -12,6 +12,7 @@ namespace OCA\WebhookListeners\BackgroundJobs;
use OCA\AppAPI\PublicFunctions;
use OCA\WebhookListeners\Db\AuthMethod;
use OCA\WebhookListeners\Db\WebhookListenerMapper;
use OCA\WebhookListeners\Service\TokenService;
use OCP\App\IAppManager;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\QueuedJob;
@ -30,6 +31,7 @@ class WebhookCall extends QueuedJob {
private WebhookListenerMapper $mapper,
private LoggerInterface $logger,
private IAppManager $appManager,
private TokenService $tokenService,
ITimeFactory $timeFactory,
) {
parent::__construct($timeFactory);
@ -42,6 +44,9 @@ class WebhookCall extends QueuedJob {
[$data, $webhookId] = $argument;
$webhookListener = $this->mapper->getById($webhookId);
$client = $this->clientService->newClient();
// adding Ephemeral auth tokens to the call
$data['tokens'] = $this->tokenService->getTokens($webhookListener, $data['user']['uid'] ?? null);
$options = [
'verify' => $this->certificateManager->getAbsoluteBundlePath(),
'headers' => $webhookListener->getHeaders() ?? [],

View file

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\WebhookListeners\BackgroundJobs;
use OCA\WebhookListeners\Db\EphemeralTokenMapper;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;
class WebhookTokenCleanup extends TimedJob {
public function __construct(
private EphemeralTokenMapper $tokenMapper,
ITimeFactory $timeFactory,
) {
parent::__construct($timeFactory);
// every 5 min
$this->setInterval(5 * 60);
}
/**
* @param array $argument
*/
protected function run($argument): void {
$this->tokenMapper->invalidateOldTokens();
}
}

View file

@ -112,6 +112,11 @@ class WebhooksController extends OCSController {
* @param ?array<string,string> $headers Array of headers to send
* @param "none"|"header"|null $authMethod Authentication method to use
* @param ?array<string,mixed> $authData Array of data for authentication
* @param ?array{user_ids?:list<string>,user_roles?:list<string>} $tokenNeeded
* List of user ids for which to include auth tokens in the event.
* Has two fields: "user_ids" list of user uids for which tokens are needed, "user_roles" list of roles (users not defined by their ID but by the role they have in the webhook event) for which tokens can be included.
* Possible roles: "owner" for the user creating the webhook, "trigger" for the user triggering the webhook call.
* Requested auth tokens are valid for 1 hour after receiving them in the event call request.
*
* @return DataResponse<Http::STATUS_OK, WebhookListenersWebhookInfo, array{}>
*
@ -134,6 +139,7 @@ class WebhooksController extends OCSController {
?string $authMethod,
#[\SensitiveParameter]
?array $authData,
?array $tokenNeeded = null,
): DataResponse {
$appId = null;
if ($this->session->get('app_api') === true) {
@ -156,6 +162,7 @@ class WebhooksController extends OCSController {
$headers,
$authMethod,
$authData,
$tokenNeeded,
);
return new DataResponse($webhookListener->jsonSerialize());
} catch (\UnexpectedValueException $e) {
@ -180,6 +187,11 @@ class WebhooksController extends OCSController {
* @param ?array<string,string> $headers Array of headers to send
* @param "none"|"header"|null $authMethod Authentication method to use
* @param ?array<string,mixed> $authData Array of data for authentication
* @param ?array{user_ids?:list<string>,user_roles?:list<string>} $tokenNeeded
* List of user ids for which to include auth tokens in the event.
* Has two fields: "user_ids" list of user uids for which tokens are needed, "user_roles" list of roles (users not defined by their ID but by the role they have in the webhook event) for which tokens can be included.
* Possible roles: "owner" for the user creating the webhook, "trigger" for the user triggering the webhook call.
* Requested auth tokens are valid for 1 hour after receiving them in the event call request.
*
* @return DataResponse<Http::STATUS_OK, WebhookListenersWebhookInfo, array{}>
*
@ -203,6 +215,7 @@ class WebhooksController extends OCSController {
?string $authMethod,
#[\SensitiveParameter]
?array $authData,
?array $tokenNeeded = null,
): DataResponse {
$appId = null;
if ($this->session->get('app_api') === true) {
@ -226,6 +239,7 @@ class WebhooksController extends OCSController {
$headers,
$authMethod,
$authData,
$tokenNeeded,
);
return new DataResponse($webhookListener->jsonSerialize());
} catch (\UnexpectedValueException $e) {

View file

@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\WebhookListeners\Db;
use OCP\AppFramework\Db\Entity;
/**
* @method int getTokenId()
* @method ?string getUserId()
* @method int getCreatedAt()
* @psalm-suppress PropertyNotSetInConstructor
*/
class EphemeralToken extends Entity implements \JsonSerializable {
/**
* @var int id of the token in the oc_authtoken db table
*/
protected $tokenId;
/**
* @var ?string id of the user wich the token belongs to
* @psalm-suppress PropertyNotSetInConstructor
*/
protected $userId = null;
/**
* @var int token creation timestamp
* @psalm-suppress PropertyNotSetInConstructor
*/
protected $createdAt;
public function __construct() {
$this->addType('tokenId', 'integer');
$this->addType('userId', 'string');
$this->addType('createdAt', 'integer');
}
public function jsonSerialize(): array {
$fields = array_keys($this->getFieldTypes());
return array_combine(
$fields,
array_map(
fn ($field) => $this->getter($field),
$fields
)
);
}
}

View file

@ -0,0 +1,121 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\WebhookListeners\Db;
use OC\Authentication\Token\PublicKeyTokenMapper;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\AppFramework\Db\QBMapper;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\DB\Exception;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use Psr\Log\LoggerInterface;
/**
* @template-extends QBMapper<EphemeralToken>
*/
class EphemeralTokenMapper extends QBMapper {
public const TABLE_NAME = 'webhook_tokens';
public const TOKEN_LIFETIME = 1 * 1 * 60; // one hour in seconds
public function __construct(
IDBConnection $db,
private LoggerInterface $logger,
private ITimeFactory $time,
private PublicKeyTokenMapper $tokenMapper,
) {
parent::__construct($db, self::TABLE_NAME, EphemeralToken::class);
}
/**
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
* @throws Exception
*/
public function getById(int $id): EphemeralToken {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
return $this->findEntity($qb);
}
/**
* @throws Exception
* @return EphemeralToken[]
*/
public function getAll(): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName());
return $this->findEntities($qb);
}
/**
* @param int $olderThan
* @return EphemeralToken[]
* @throws Exception
*/
public function getOlderThan($olderThan): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->lt('created_at', $qb->createNamedParameter($olderThan, IQueryBuilder::PARAM_INT)));
return $this->findEntities($qb);
}
/**
* @throws Exception
*/
public function addEphemeralToken(
int $tokenId,
?string $userId,
int $createdAt,
): EphemeralToken {
$tempToken = EphemeralToken::fromParams(
[
'tokenId' => $tokenId,
'userId' => $userId,
'createdAt' => $createdAt,
]
);
return $this->insert($tempToken);
}
public function invalidateOldTokens(int $token_lifetime = self::TOKEN_LIFETIME) {
$olderThan = $this->time->getTime() - $token_lifetime;
try {
$tokensToDelete = $this->getOlderThan($olderThan);
} catch (Exception $e) {
$this->logger->error('Webhook token deletion failed: ' . $e->getMessage(), ['exception' => $e]);
return;
}
$this->logger->debug('Invalidating ephemeral webhook tokens older than ' . date('c', $olderThan), ['app' => 'webhook_listeners']);
foreach ($tokensToDelete as $token) {
try {
$this->tokenMapper->delete($this->tokenMapper->getTokenById($token->getTokenId())); // delete token itself
$this->delete($token); // delete db row in webhook_tokens
} catch (Exception $e) {
$this->logger->error('Webhook token deletion failed: ' . $e->getMessage(), ['exception' => $e]);
}
}
}
}

View file

@ -23,6 +23,7 @@ use OCP\Server;
* @method ?string getAuthData()
* @method void setAuthData(?string $data)
* @method string getAuthMethod()
* @method ?array getTokenNeeded()
* @psalm-suppress PropertyNotSetInConstructor
*/
class WebhookListener extends Entity implements \JsonSerializable {
@ -84,8 +85,15 @@ class WebhookListener extends Entity implements \JsonSerializable {
*/
protected $authData = null;
/**
* @var array
* @psalm-suppress PropertyNotSetInConstructor
*/
protected $tokenNeeded;
private ICrypto $crypto;
public function __construct(
?ICrypto $crypto = null,
) {
@ -103,6 +111,7 @@ class WebhookListener extends Entity implements \JsonSerializable {
$this->addType('headers', 'json');
$this->addType('authMethod', 'string');
$this->addType('authData', 'string');
$this->addType('tokenNeeded', 'json');
}
public function getAuthMethodEnum(): AuthMethod {

View file

@ -82,6 +82,7 @@ class WebhookListenerMapper extends QBMapper {
AuthMethod $authMethod,
#[\SensitiveParameter]
?array $authData,
?array $tokenNeeded = [],
): WebhookListener {
/* Remove any superfluous antislash */
$event = ltrim($event, '\\');
@ -99,6 +100,7 @@ class WebhookListenerMapper extends QBMapper {
'userIdFilter' => $userIdFilter ?? '',
'headers' => $headers,
'authMethod' => $authMethod->value,
'tokenNeeded' => $tokenNeeded ?? [],
]
);
$webhookListener->setAuthDataClear($authData);
@ -122,6 +124,7 @@ class WebhookListenerMapper extends QBMapper {
AuthMethod $authMethod,
#[\SensitiveParameter]
?array $authData,
?array $tokenNeeded = [],
): WebhookListener {
/* Remove any superfluous antislash */
$event = ltrim($event, '\\');
@ -140,6 +143,7 @@ class WebhookListenerMapper extends QBMapper {
'userIdFilter' => $userIdFilter ?? '',
'headers' => $headers,
'authMethod' => $authMethod->value,
'tokenNeeded' => $tokenNeeded ?? [],
]
);
$webhookListener->setAuthDataClear($authData);

View file

@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\WebhookListeners\Migration;
use Closure;
use OCA\WebhookListeners\Db\EphemeralTokenMapper;
use OCA\WebhookListeners\Db\WebhookListenerMapper;
use OCP\DB\ISchemaWrapper;
use OCP\DB\Types;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
class Version1500Date20251007130000 extends SimpleMigrationStep {
/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
* @return null|ISchemaWrapper
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
$schema = $schemaClosure();
$schemaHasChanged = false;
if ($schema->hasTable(WebhookListenerMapper::TABLE_NAME)) {
$table = $schema->getTable(WebhookListenerMapper::TABLE_NAME);
if (!$table->hasColumn('token_needed')) {
$schemaHasChanged = true;
$table->addColumn('token_needed', Types::TEXT, [
'notnull' => false,
]);
}
}
if (!$schema->hasTable(EphemeralTokenMapper::TABLE_NAME)) {
$schemaHasChanged = true;
$table = $schema->createTable(EphemeralTokenMapper::TABLE_NAME);
$table->addColumn('id', Types::BIGINT, [
'autoincrement' => true,
'notnull' => true,
]);
$table->addColumn('token_id', Types::BIGINT, [
'notnull' => true,
'length' => 4,
'unsigned' => true,
]);
$table->addColumn('user_id', Types::STRING, [
'notnull' => false,
'length' => 64,
]);
$table->addColumn('created_at', Types::BIGINT, [
'notnull' => true,
'length' => 4,
'unsigned' => true,
]);
$table->setPrimaryKey(['id']);
}
return $schemaHasChanged ? $schema : null;
}
}

View file

@ -21,6 +21,7 @@ namespace OCA\WebhookListeners;
* headers?: array<string,string>,
* authMethod: string,
* authData?: array<string,mixed>,
* tokenNeeded?: ?array{user_ids?:array<string,string>,user_roles?:array<string,string>},
* }
*/
class ResponseDefinitions {

View file

@ -0,0 +1,122 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\WebhookListeners\Service;
use OC\Authentication\Token\IProvider;
use OCA\WebhookListeners\Db\EphemeralTokenMapper;
use OCA\WebhookListeners\Db\WebhookListener;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Authentication\Token\IToken;
use OCP\IUserManager;
use OCP\L10N\IFactory;
use OCP\Security\ISecureRandom;
use Psr\Log\LoggerInterface;
class TokenService {
public function __construct(
private IProvider $tokenProvider,
private ISecureRandom $random,
private EphemeralTokenMapper $tokenMapper,
private LoggerInterface $logger,
private ITimeFactory $time,
private IFactory $l10nFactory,
private IUserManager $userManager,
) {
}
/**
* creates an array which includes two arrays of tokens: 'user_ids' and 'user_roles'
* The array ['user_ids' => ['jane', 'bob'], 'user_roles' => ['owner', 'trigger']]
* as requested tokens in the registered webhook produces a result like
* ['user_ids' => [['jane' => 'abcdtokenabcd1'], ['bob','=> 'abcdtokenabcd2']], 'user_roles' => ['owner' => ['admin' => 'abcdtokenabcd3'], 'trigger' => ['user1' => 'abcdtokenabcd4']]]
* Created auth tokens are valid for 1 hour.
*
* @param WebhookListener $webhookListener
* @param ?string $triggerUserId the user that triggered the webhook call
* @return array{user_ids?:array<string,string>,user_roles?:array{owner?:array<string,string>,trigger?:array<string,string>}}
*/
public function getTokens(WebhookListener $webhookListener, ?string $triggerUserId): array {
$tokens = [
'user_ids' => [],
'user_roles' => [],
];
$tokenNeeded = $webhookListener->getTokenNeeded();
if (isset($tokenNeeded['user_ids'])) {
foreach ($tokenNeeded['user_ids'] as $userId) {
try {
$tokens['user_ids'][$userId] = $this->createEphemeralToken($userId);
} catch (\Exception $e) {
$this->logger->error('Webhook token creation for user ' . $userId . ' failed: ' . $e->getMessage(), ['exception' => $e]);
}
}
}
if (isset($tokenNeeded['user_roles'])) {
foreach ($tokenNeeded['user_roles'] as $user_role) {
switch ($user_role) {
case 'owner':
// token for the person who created the flow
$ownerId = $webhookListener->getUserId();
if (is_null($ownerId)) { // no owner uid available
break;
}
$tokens['user_roles']['owner'] = [
$ownerId => $this->createEphemeralToken($ownerId)
];
break;
case 'trigger':
// token for the person who triggered the webhook
if (is_null($triggerUserId)) { // no trigger uid available
break;
}
$tokens['user_roles']['trigger'] = [
$triggerUserId => $this->createEphemeralToken($triggerUserId)
];
break;
default:
$this->logger->error('Webhook token creation for user role ' . $user_role . ' not defined. ', ['Not defined' => $user_role]);
}
}
}
return $tokens;
}
private function createEphemeralToken(string $userId): string {
$token = $this->generateRandomDeviceToken();
// we need the user`s language to have the token name showing up in the session list in the correct language
$user = $this->userManager->get($userId);
$lang = $this->l10nFactory->getUserLanguage($user);
$l = $this->l10nFactory->get('webhook_listeners', $lang);
$name = $l->t('Ephemeral webhook authentication');
$password = null;
$deviceToken = $this->tokenProvider->generateToken(
$token,
$userId,
$userId,
$password,
$name,
IToken::PERMANENT_TOKEN);
$this->tokenMapper->addEphemeralToken(
$deviceToken->getId(),
$userId,
$this->time->getTime());
return $token;
}
private function generateRandomDeviceToken(): string {
$groups = [];
for ($i = 0; $i < 5; $i++) {
$groups[] = $this->random->generate(5, ISecureRandom::CHAR_HUMAN_READABLE);
}
return implode('-', $groups);
}
}

View file

@ -92,6 +92,24 @@
"additionalProperties": {
"type": "object"
}
},
"tokenNeeded": {
"type": "object",
"nullable": true,
"properties": {
"user_ids": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"user_roles": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
}
@ -304,6 +322,26 @@
"additionalProperties": {
"type": "object"
}
},
"tokenNeeded": {
"type": "object",
"nullable": true,
"default": null,
"description": "List of user ids for which to include auth tokens in the event. Has two fields: \"user_ids\" list of user uids for which tokens are needed, \"user_roles\" list of roles (users not defined by their ID but by the role they have in the webhook event) for which tokens can be included. Possible roles: \"owner\" for the user creating the webhook, \"trigger\" for the user triggering the webhook call. Requested auth tokens are valid for 1 hour after receiving them in the event call request.",
"properties": {
"user_ids": {
"type": "array",
"items": {
"type": "string"
}
},
"user_roles": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
@ -703,6 +741,26 @@
"additionalProperties": {
"type": "object"
}
},
"tokenNeeded": {
"type": "object",
"nullable": true,
"default": null,
"description": "List of user ids for which to include auth tokens in the event. Has two fields: \"user_ids\" list of user uids for which tokens are needed, \"user_roles\" list of roles (users not defined by their ID but by the role they have in the webhook event) for which tokens can be included. Possible roles: \"owner\" for the user creating the webhook, \"trigger\" for the user triggering the webhook call. Requested auth tokens are valid for 1 hour after receiving them in the event call request.",
"properties": {
"user_ids": {
"type": "array",
"items": {
"type": "string"
}
},
"user_roles": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}

View file

@ -4687,6 +4687,24 @@
"additionalProperties": {
"type": "object"
}
},
"tokenNeeded": {
"type": "object",
"nullable": true,
"properties": {
"user_ids": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"user_roles": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
}
@ -38254,6 +38272,26 @@
"additionalProperties": {
"type": "object"
}
},
"tokenNeeded": {
"type": "object",
"nullable": true,
"default": null,
"description": "List of user ids for which to include auth tokens in the event. Has two fields: \"user_ids\" list of user uids for which tokens are needed, \"user_roles\" list of roles (users not defined by their ID but by the role they have in the webhook event) for which tokens can be included. Possible roles: \"owner\" for the user creating the webhook, \"trigger\" for the user triggering the webhook call. Requested auth tokens are valid for 1 hour after receiving them in the event call request.",
"properties": {
"user_ids": {
"type": "array",
"items": {
"type": "string"
}
},
"user_roles": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
@ -38653,6 +38691,26 @@
"additionalProperties": {
"type": "object"
}
},
"tokenNeeded": {
"type": "object",
"nullable": true,
"default": null,
"description": "List of user ids for which to include auth tokens in the event. Has two fields: \"user_ids\" list of user uids for which tokens are needed, \"user_roles\" list of roles (users not defined by their ID but by the role they have in the webhook event) for which tokens can be included. Possible roles: \"owner\" for the user creating the webhook, \"trigger\" for the user triggering the webhook call. Requested auth tokens are valid for 1 hour after receiving them in the event call request.",
"properties": {
"user_ids": {
"type": "array",
"items": {
"type": "string"
}
},
"user_roles": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}