VYPR
High severity8.1NVD Advisory· Published Jun 1, 2026· Updated Jun 1, 2026

CVE-2026-45156

CVE-2026-45156

Description

A missing signature verification in the Nextcloud User OIDC app allows a malicious ID4me authority to impersonate any user.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A missing signature verification in the Nextcloud User OIDC app allows a malicious ID4me authority to impersonate any user.

Vulnerability

The vulnerability exists in the User OIDC application for Nextcloud, specifically within the handling of ID4me authentication tokens. The application fails to perform proper signature verification on incoming JWTs, which are used to authenticate users via the ID4me protocol. This flaw affects versions 0.3.0 through 3.0.x, 5.0.0 through 5.0.x, and 6.0.0 through 6.3.x [1], [2].

Exploitation

An attacker must control or compromise an ID4me authority that is trusted by the target Nextcloud instance. By leveraging the lack of signature validation, the malicious authority can craft and send arbitrary authentication tokens to the Nextcloud server. The server accepts these tokens as valid without verifying their authenticity, allowing the attacker to successfully authenticate as any user registered on the platform [2].

Impact

Successful exploitation allows an attacker to bypass authentication mechanisms and impersonate any user on the affected Nextcloud instance. This results in a complete compromise of the victim's account, granting the attacker unauthorized access to the user's private data, files, and collaboration features within the platform [2].

Mitigation

Users are advised to upgrade the User OIDC application to versions 3.1.0, 4.1.0, 5.1.0, 6.4.0, or 8.3.0 to resolve the vulnerability [2]. If an immediate upgrade is not possible, the ID4me feature can be disabled within the application configuration to mitigate the risk [2].

AI Insight generated on Jun 1, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1
  • Range: 0.3.0 <= v < 3.1.0, 5.0.0 <= v < 5.1.0, 6.0.0 <= v < 6.4.0

Patches

1
5797f5770266

Merge pull request #1285 from nextcloud/enh/noid/id4me-signature

https://github.com/nextcloud/user_oidcJulien VeyssierJan 12, 2026via nvd-ref
3 files changed · +52 2
  • lib/Controller/Id4meController.php+19 1 modified
    @@ -17,6 +17,7 @@
     use OCA\UserOIDC\Db\UserMapper;
     use OCA\UserOIDC\Helper\HttpClientHelper;
     use OCA\UserOIDC\Service\ID4MeService;
    +use OCA\UserOIDC\Vendor\Firebase\JWT\JWT;
     use OCP\AppFramework\Db\DoesNotExistException;
     use OCP\AppFramework\Db\MultipleObjectsReturnedException;
     use OCP\AppFramework\Http;
    @@ -249,7 +250,24 @@ public function code(string $state = '', string $code = '', string $scope = '')
     		$plainHeaders = json_decode(base64_decode($header), true);
     		$plainPayload = json_decode(base64_decode($payload), true);
     
    -		/** TODO: VALIATE SIGNATURE! */
    +		// validate the JWT signature
    +		$idTokenRaw = $data['id_token'];
    +		$jwkUri = $openIdConfig->getJwksUri();
    +		JWT::$leeway = 60;
    +		try {
    +			$jwks = $this->id4MeService->obtainJWK($jwkUri, $data['id_token'], true);
    +			$idTokenPayload = JWT::decode($idTokenRaw, $jwks);
    +		} catch (\Exception|\Throwable $e) {
    +			$this->logger->debug('Failed to decode the JWT token, retrying with fresh JWK');
    +			try {
    +				$jwks = $this->id4MeService->obtainJWK($jwkUri, $idTokenRaw, false);
    +				$idTokenPayload = JWT::decode($idTokenRaw, $jwks);
    +			} catch (\Exception|\Throwable $e) {
    +				$this->logger->debug('Failed to decode the JWT token with fresh JWK');
    +				$message = $this->l10n->t('Failed to authenticate');
    +				return $this->build403TemplateResponse($message, Http::STATUS_FORBIDDEN, ['reason' => 'token signature check failed']);
    +			}
    +		}
     
     		// Check expiration
     		if ($plainPayload['exp'] < $this->timeFactory->getTime()) {
    
  • lib/Service/DiscoveryService.php+1 1 modified
    @@ -178,7 +178,7 @@ private function validateKeyStrength(array $key, string $alg): void {
     	 * @return array The modified JWKS
     	 * @throws \RuntimeException if no matching key is found or algorithm is unsupported
     	 */
    -	private function fixJwksAlg(array $jwks, string $jwt): array {
    +	public function fixJwksAlg(array $jwks, string $jwt): array {
     		$jwtParts = explode('.', $jwt, 3);
     		$header = json_decode(JWT::urlsafeB64Decode($jwtParts[0]), true);
     		$kid = $header['kid'] ?? null;
    
  • lib/Service/ID4MeService.php+32 0 modified
    @@ -9,13 +9,25 @@
     namespace OCA\UserOIDC\Service;
     
     use OCA\UserOIDC\AppInfo\Application;
    +use OCA\UserOIDC\Helper\HttpClientHelper;
    +use OCA\UserOIDC\Vendor\Firebase\JWT\JWK;
     use OCP\IAppConfig;
    +use OCP\ICache;
    +use OCP\ICacheFactory;
    +use Psr\Log\LoggerInterface;
     
     class ID4MeService {
     
    +	private ICache $cache;
    +
     	public function __construct(
     		private IAppConfig $appConfig,
    +		private DiscoveryService $discoveryService,
    +		private LoggerInterface $logger,
    +		private HttpClientHelper $clientService,
    +		ICacheFactory $cacheFactory,
     	) {
    +		$this->cache = $cacheFactory->createDistributed('user_oidc');
     	}
     
     	public function setID4ME(bool $enabled): void {
    @@ -25,4 +37,24 @@ public function setID4ME(bool $enabled): void {
     	public function getID4ME(): bool {
     		return $this->appConfig->getValueString(Application::APP_ID, 'id4me_enabled', '0', lazy: true) === '1';
     	}
    +
    +	public function obtainJWK(string $jwkUri, string $tokenToDecode, bool $useCache = true): array {
    +		$cacheKey = 'jwks-' . $jwkUri;
    +		$cachedJwks = $this->cache->get($cacheKey);
    +		if ($cachedJwks !== null && $useCache) {
    +			$rawJwks = json_decode($cachedJwks, true, flags: JSON_THROW_ON_ERROR);
    +			$this->logger->debug('[ID4ME-obtainJWK] jwks cache content', ['jwks_cache' => $rawJwks]);
    +		} else {
    +			$responseBody = (string)$this->clientService->get($jwkUri);
    +			$rawJwks = json_decode($responseBody, true, flags: JSON_THROW_ON_ERROR);
    +			$this->logger->debug('[ID4ME-obtainJWK] getting fresh jwks', ['jwks' => $rawJwks]);
    +			$this->cache->set($cacheKey, $responseBody, DiscoveryService::INVALIDATE_JWKS_CACHE_AFTER_SECONDS);
    +		}
    +
    +		$fixedJwks = $this->discoveryService->fixJwksAlg($rawJwks, $tokenToDecode);
    +		$this->logger->debug('[ID4ME-obtainJWK] fixed jwks', ['fixed_jwks' => $fixedJwks]);
    +		$jwks = JWK::parseKeySet($fixedJwks, 'RS256');
    +		$this->logger->debug('Parsed the jwks');
    +		return $jwks;
    +	}
     }
    

Vulnerability mechanics

No source-code context for this CVE — mechanics is only generated when we can read the actual fix diff. Without that, the four sections (root cause, attack vector, affected code, fix) would be speculation rather than analysis.

References

3

News mentions

0

No linked articles in our index yet.