Skip to content

Commit

Permalink
fix(Session): avoid password confirmation on SSO
Browse files Browse the repository at this point in the history
SSO backends like SAML and OIDC tried a trick to suppress password
confirmations as they are not possible by design. At least for SAML it was
not reliable when existing user backends where used as user repositories.

Now we are setting a special scope with the token, and also make sure that
the scope is taken over when tokens are regenerated.

Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
  • Loading branch information
blizzz committed Mar 4, 2024
1 parent e0705f1 commit 9ed2917
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 41 deletions.
5 changes: 4 additions & 1 deletion core/Controller/OCJSController.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
namespace OC\Core\Controller;

use bantu\IniGetWrapper\IniGetWrapper;
use OC\Authentication\Token\IProvider;
use OC\CapabilitiesManager;
use OC\Template\JSConfigHelper;
use OCP\App\IAppManager;
Expand Down Expand Up @@ -65,6 +66,7 @@ public function __construct(
IURLGenerator $urlGenerator,
CapabilitiesManager $capabilitiesManager,
IInitialStateService $initialStateService,
IProvider $tokenProvider,
) {
parent::__construct($appName, $request);

Expand All @@ -79,7 +81,8 @@ public function __construct(
$iniWrapper,
$urlGenerator,
$capabilitiesManager,
$initialStateService
$initialStateService,
$tokenProvider
);
}

Expand Down
3 changes: 2 additions & 1 deletion lib/private/AppFramework/DependencyInjection/DIContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,8 @@ public function __construct(string $appName, array $urlParams = [], ServerContai
$c->get(IControllerMethodReflector::class),
$c->get(ISession::class),
$c->get(IUserSession::class),
$c->get(ITimeFactory::class)
$c->get(ITimeFactory::class),
$c->get(\OC\Authentication\Token\IProvider::class),
)
);
$dispatcher->registerMiddleware(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

use OC\AppFramework\Middleware\Security\Exceptions\NotConfirmedException;
use OC\AppFramework\Utility\ControllerMethodReflector;
use OC\Authentication\Token\IProvider;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
use OCP\AppFramework\Middleware;
Expand All @@ -45,6 +46,7 @@ class PasswordConfirmationMiddleware extends Middleware {
private $timeFactory;
/** @var array */
private $excludedUserBackEnds = ['user_saml' => true, 'user_globalsiteselector' => true];
private IProvider $tokenProvider;

/**
* PasswordConfirmationMiddleware constructor.
Expand All @@ -57,11 +59,14 @@ class PasswordConfirmationMiddleware extends Middleware {
public function __construct(ControllerMethodReflector $reflector,
ISession $session,
IUserSession $userSession,
ITimeFactory $timeFactory) {
ITimeFactory $timeFactory,
IProvider $tokenProvider
) {
$this->reflector = $reflector;
$this->session = $session;
$this->userSession = $userSession;
$this->timeFactory = $timeFactory;
$this->tokenProvider = $tokenProvider;
}

/**
Expand All @@ -86,8 +91,16 @@ public function beforeController($controller, $methodName) {
$backendClassName = $user->getBackendClassName();
}

$sessionId = $this->session->getId();
$token = $this->tokenProvider->getToken($sessionId);
$scope = $token->getScopeAsArray();
if (isset($scope['sso-based-login']) && $scope['sso-based-login'] === true) {
// Users logging in from SSO backends cannot confirm their password by design
return;
}

$lastConfirm = (int) $this->session->get('last-password-confirm');
// we can't check the password against a SAML backend, so skip password confirmation in this case
// TODO: confirm excludedUserBackEnds can go away and remove it
if (!isset($this->excludedUserBackEnds[$backendClassName]) && $lastConfirm < ($this->timeFactory->getTime() - (30 * 60 + 15))) { // allow 15 seconds delay
throw new NotConfirmedException();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ public function renewSessionToken(string $oldSessionId, string $sessionId): OCPI
OCPIToken::TEMPORARY_TOKEN,
$token->getRemember()
);
$newToken->setScope($token->getScopeAsArray());
$this->cacheToken($newToken);

$this->cacheInvalidHash($token->getToken());
Expand Down
64 changes: 28 additions & 36 deletions lib/private/Template/JSConfigHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
namespace OC\Template;

use bantu\IniGetWrapper\IniGetWrapper;
use OC\Authentication\Token\IProvider;
use OC\CapabilitiesManager;
use OC\Share\Share;
use OCP\App\AppPathNotFoundException;
Expand All @@ -53,43 +54,24 @@
use OCP\Util;

class JSConfigHelper {
protected IL10N $l;
protected Defaults $defaults;
protected IAppManager $appManager;
protected ISession $session;
protected ?IUser $currentUser;
protected IConfig $config;
protected IGroupManager $groupManager;
protected IniGetWrapper $iniWrapper;
protected IURLGenerator $urlGenerator;
protected CapabilitiesManager $capabilitiesManager;
protected IInitialStateService $initialStateService;

/** @var array user back-ends excluded from password verification */
private $excludedUserBackEnds = ['user_saml' => true, 'user_globalsiteselector' => true];

public function __construct(IL10N $l,
Defaults $defaults,
IAppManager $appManager,
ISession $session,
?IUser $currentUser,
IConfig $config,
IGroupManager $groupManager,
IniGetWrapper $iniWrapper,
IURLGenerator $urlGenerator,
CapabilitiesManager $capabilitiesManager,
IInitialStateService $initialStateService) {
$this->l = $l;
$this->defaults = $defaults;
$this->appManager = $appManager;
$this->session = $session;
$this->currentUser = $currentUser;
$this->config = $config;
$this->groupManager = $groupManager;
$this->iniWrapper = $iniWrapper;
$this->urlGenerator = $urlGenerator;
$this->capabilitiesManager = $capabilitiesManager;
$this->initialStateService = $initialStateService;
public function __construct(
protected IL10N $l,
protected Defaults $defaults,
protected IAppManager $appManager,
protected ISession $session,
protected ?IUser $currentUser,
protected IConfig $config,
protected IGroupManager $groupManager,
protected IniGetWrapper $iniWrapper,
protected IURLGenerator $urlGenerator,
protected CapabilitiesManager $capabilitiesManager,
protected IInitialStateService $initialStateService,
protected IProvider $tokenProvider,
) {
}

public function getConfig(): string {
Expand Down Expand Up @@ -155,9 +137,13 @@ public function getConfig(): string {
}

if ($this->currentUser instanceof IUser) {
$lastConfirmTimestamp = $this->session->get('last-password-confirm');
if (!is_int($lastConfirmTimestamp)) {
$lastConfirmTimestamp = 0;
if ($this->isUserLoggedInViaSSO()) {
$lastConfirmTimestamp = PHP_INT_MAX;
} else {
$lastConfirmTimestamp = $this->session->get('last-password-confirm');
if (!is_int($lastConfirmTimestamp)) {
$lastConfirmTimestamp = 0;
}
}
} else {
$lastConfirmTimestamp = 0;
Expand Down Expand Up @@ -311,4 +297,10 @@ public function getConfig(): string {

return $result;
}

protected function isUserLoggedInViaSSO(): bool {
$token = $this->tokenProvider->getToken($this->session->getId());
$scope = $token->getScopeAsArray();
return isset($scope['sso-based-login']) && $scope['sso-based-login'] === true;
}
}
4 changes: 3 additions & 1 deletion lib/private/TemplateLayout.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
namespace OC;

use bantu\IniGetWrapper\IniGetWrapper;
use OC\Authentication\Token\IProvider;
use OC\Search\SearchQuery;
use OC\Template\CSSResourceLocator;
use OC\Template\JSConfigHelper;
Expand Down Expand Up @@ -259,7 +260,8 @@ public function __construct($renderAs, $appId = '') {
\OC::$server->get(IniGetWrapper::class),
\OC::$server->getURLGenerator(),
\OC::$server->getCapabilitiesManager(),
\OCP\Server::get(IInitialStateService::class)
\OCP\Server::get(IInitialStateService::class),
\OCP\Server::get(IProvider::class),
);
$config = $jsConfigHelper->getConfig();
if (\OC::$server->getContentSecurityPolicyNonceManager()->browserSupportsCspV3()) {
Expand Down
9 changes: 9 additions & 0 deletions lib/private/legacy/OC_User.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
*
*/

use OC\Authentication\Token\IProvider;
use OC\User\LoginException;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IGroupManager;
Expand Down Expand Up @@ -196,6 +197,14 @@ public static function loginWithApache(\OCP\Authentication\IApacheBackend $backe

$userSession->createSessionToken($request, $uid, $uid, $password);
$userSession->createRememberMeToken($userSession->getUser());

if (empty($password)) {
$tokenProvider = \OC::$server->get(IProvider::class);
$token = $tokenProvider->getToken($userSession->getSession()->getId());
$token->setScope(['sso-based-login' => true]);
$tokenProvider->updateToken($token);
}

// setup the filesystem
OC_Util::setupFS($uid);
// first call the post_login hooks, the login-process needs to be
Expand Down

0 comments on commit 9ed2917

Please sign in to comment.