Skip to content
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<name>OpenID Connect user backend</name>
<summary>Use an OpenID Connect backend to login to your Nextcloud</summary>
<description>Allows flexible configuration of an OIDC server as Nextcloud login user backend.</description>
<version>8.11.0-dev.0</version>
<version>8.11.0</version>
<licence>agpl</licence>
<author>Roeland Jago Douma</author>
<author>Julius Härtl</author>
Expand Down
15 changes: 8 additions & 7 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
use OCA\UserOIDC\Listener\InternalTokenRequestedListener;
use OCA\UserOIDC\Listener\TimezoneHandlingListener;
use OCA\UserOIDC\Listener\TokenInvalidatedListener;
use OCA\UserOIDC\MagentaBearer\MBackend;
use OCA\UserOIDC\Service\ID4MeService;
use OCA\UserOIDC\Service\RequestClassificationService;
use OCA\UserOIDC\Service\SettingsService;
use OCA\UserOIDC\Service\TokenService;
use OCA\UserOIDC\User\Backend;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
Expand All @@ -42,7 +42,6 @@ class Application extends App implements IBootstrap {
public const APP_ID = 'user_oidc';
public const OIDC_API_REQ_HEADER = 'Authorization';

private $backend;
private $cachedProviders;

public function __construct(array $urlParams = []) {
Expand All @@ -54,14 +53,14 @@ public function register(IRegistrationContext $context): void {
$userManager = $this->getContainer()->get(IUserManager::class);

/* Register our own user backend */
$this->backend = $this->getContainer()->get(Backend::class);
$backend = $this->getContainer()->get(MBackend::class);

$config = $this->getContainer()->get(IConfig::class);
if (version_compare($config->getSystemValueString('version', '0.0.0'), '32.0.0', '>=')) {
// see https://docs.nextcloud.com/server/latest/developer_manual/app_publishing_maintenance/app_upgrade_guide/upgrade_to_32.html#id3
$userManager->registerBackend($this->backend);
$userManager->registerBackend($backend);
} else {
\OC_User::useBackend($this->backend);
\OC_User::useBackend($backend);
}

$context->registerEventListener(LoadAdditionalScriptsEvent::class, TimezoneHandlingListener::class);
Expand All @@ -83,8 +82,10 @@ public function register(IRegistrationContext $context): void {
}

public function boot(IBootContext $context): void {
$context->injectFn(\Closure::fromCallable([$this->backend, 'injectSession']));
$context->injectFn(\Closure::fromCallable([$this, 'checkLoginToken']));
/** @var MBackend $backend */
$backend = $this->getContainer()->get(MBackend::class);
$context->injectFn(\Closure::fromCallable([$backend, 'injectSession']));
// $context->injectFn(\Closure::fromCallable([$this, 'checkLoginToken']));
/** @var IUserSession $userSession */
$userSession = $this->getContainer()->get(IUserSession::class);
if ($userSession->isLoggedIn()) {
Expand Down
148 changes: 148 additions & 0 deletions lib/MagentaBearer/MBackend.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2026 T-Systems International
* SPDX-License-Identifier: AGPL-3.0-only
*/

namespace OCA\UserOIDC\MagentaBearer;

use OCA\UserOIDC\AppInfo\Application;
use OCA\UserOIDC\Db\ProviderMapper;
use OCA\UserOIDC\Db\UserMapper;
use OCA\UserOIDC\Service\DiscoveryService;
use OCA\UserOIDC\Service\ProviderService;
use OCA\UserOIDC\Service\ProvisioningDeniedException;
use OCA\UserOIDC\Service\ProvisioningEventService;
use OCA\UserOIDC\User\AbstractOidcBackend;
use OCP\Authentication\IApacheBackend;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IConfig;
use OCP\IRequest;
use OCP\ISession;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\Security\ICrypto;
use Psr\Log\LoggerInterface;

class MBackend extends AbstractOidcBackend implements IApacheBackend {

public function __construct(
IConfig $config,
UserMapper $userMapper,
LoggerInterface $logger,
IRequest $request,
ISession $session,
IURLGenerator $urlGenerator,
IEventDispatcher $eventDispatcher,
DiscoveryService $discoveryService,
ProviderMapper $providerMapper,
ProviderService $providerService,
IUserManager $userManager,
protected ICrypto $crypto,
protected TokenService $mtokenService,
protected ProvisioningEventService $provisioningService,
) {
parent::__construct(
$config,
$userMapper,
$logger,
$request,
$session,
$urlGenerator,
$eventDispatcher,
$discoveryService,
$providerMapper,
$providerService,
$userManager,
);
}

public function getBackendName(): string {
return Application::APP_ID . '\\MagentaBearer';
}

/**
* Backend is activated if a bearer token header is detected.
*/
public function isSessionActive(): bool {
$headerToken = $this->request->getHeader(Application::OIDC_API_REQ_HEADER);

return preg_match('/^\s*bearer\s+/i', $headerToken) === 1;
}

public function getCurrentUserId(): string {
$headerToken = $this->request->getHeader(Application::OIDC_API_REQ_HEADER);

if (preg_match('/^\s*bearer\s+/i', $headerToken) !== 1) {
$this->logger->debug('No Bearer token');
return '';
}

$headerToken = preg_replace('/^\s*bearer\s+/i', '', $headerToken);
if (!is_string($headerToken) || $headerToken === '') {
$this->logger->debug('No Bearer token');
return '';
}

$providers = $this->providerMapper->getProviders();
if (count($providers) === 0) {
$this->logger->debug('No OIDC providers');
return '';
}

foreach ($providers as $provider) {
if ($this->providerService->getSetting($provider->getId(), ProviderService::SETTING_CHECK_BEARER, '0') !== '1') {
continue;
}

try {
$sharedSecret = $this->crypto->decrypt($provider->getBearerSecret());
$bearerToken = $this->mtokenService->decryptToken($headerToken, $sharedSecret);
$this->mtokenService->verifySignature($bearerToken, $sharedSecret);

$payload = $this->mtokenService->decode($bearerToken);
$this->mtokenService->verifyClaims($payload, ['http://auth.magentacloud.de']);
} catch (InvalidTokenException $e) {
$this->logger->debug('Invalid token: ' . $e->getMessage() . '. Trying another provider.');
continue;
} catch (SignatureException $e) {
$this->logger->debug($e->getMessage() . '. Trying another provider.');
continue;
} catch (\Throwable $e) {
$this->logger->debug('General non-matching provider problem: ' . $e->getMessage());
continue;
}

$uidAttribute = $this->providerService->getSetting($provider->getId(), ProviderService::SETTING_MAPPING_UID, 'sub');
$userId = is_object($payload) ? ($payload->{$uidAttribute} ?? null) : null;

if (!$this->isAcceptableUserId($userId)) {
$this->logger->debug('No extractable user id, check mapping!');
return '';
}

try {
$provisioningResult = $this->provisioningService->provisionUser($userId, $provider->getId(), $payload);
$provisionedUser = $provisioningResult['user'] ?? null;

if ($provisionedUser instanceof IUser) {
$userId = $provisionedUser->getUID();
}

$this->checkFirstLogin($userId);

return $userId;
} catch (ProvisioningDeniedException $e) {
$this->logger->error('Bearer token access denied: ' . $e->getMessage());
return '';
}
}

$this->logger->debug('Could not find provider for token');

return '';
}
}
Loading
Loading