Skip to content

Commit 27e16c5

Browse files
committed
Merge #17 Fix backchannel logout for Telekom and keep backward compatibility V31
2 parents 03f73c3 + b255fb5 commit 27e16c5

File tree

1 file changed

+66
-24
lines changed

1 file changed

+66
-24
lines changed

lib/Controller/LoginController.php

Lines changed: 66 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,26 @@ public function login(int $providerId, ?string $redirectUrl = null) {
165165
return $this->buildErrorTemplateResponse($message, Http::STATUS_NOT_FOUND, ['reason' => 'provider unreachable']);
166166
}
167167

168-
$state = $this->random->generate(32, ISecureRandom::CHAR_DIGITS . ISecureRandom::CHAR_UPPER);
169-
$this->session->set(self::STATE, $state);
170-
$this->session->set(self::REDIRECT_AFTER_LOGIN, $redirectUrl);
168+
// $state = $this->random->generate(32, ISecureRandom::CHAR_DIGITS . ISecureRandom::CHAR_UPPER);
169+
// $this->session->set(self::STATE, $state);
170+
// $this->session->set(self::REDIRECT_AFTER_LOGIN, $redirectUrl);
171171

172-
$nonce = $this->random->generate(32, ISecureRandom::CHAR_DIGITS . ISecureRandom::CHAR_UPPER);
173-
$this->session->set(self::NONCE, $nonce);
172+
// $nonce = $this->random->generate(32, ISecureRandom::CHAR_DIGITS . ISecureRandom::CHAR_UPPER);
173+
// $this->session->set(self::NONCE, $nonce);
174+
175+
// check if oidc state is present in session data
176+
if ($this->session->exists(self::STATE)) {
177+
$state = $this->session->get(self::STATE);
178+
$nonce = $this->session->get(self::NONCE);
179+
} else {
180+
$state = $this->random->generate(32, ISecureRandom::CHAR_DIGITS . ISecureRandom::CHAR_UPPER);
181+
$this->session->set(self::STATE, $state);
182+
$this->session->set(self::REDIRECT_AFTER_LOGIN, $redirectUrl);
183+
184+
$nonce = $this->random->generate(32, ISecureRandom::CHAR_DIGITS . ISecureRandom::CHAR_UPPER);
185+
$this->session->set(self::NONCE, $nonce);
186+
$this->session->set(self::PROVIDERID, $providerId);
187+
}
174188

175189
$oidcSystemConfig = $this->config->getSystemValue('user_oidc', []);
176190
$isPkceSupported = in_array('S256', $discovery['code_challenge_methods_supported'] ?? [], true);
@@ -182,7 +196,7 @@ public function login(int $providerId, ?string $redirectUrl = null) {
182196
$this->session->set(self::CODE_VERIFIER, $code_verifier);
183197
}
184198

185-
$this->session->set(self::PROVIDERID, $providerId);
199+
// $this->session->set(self::PROVIDERID, $providerId);
186200
$this->session->close();
187201

188202
// get attribute mapping settings
@@ -518,16 +532,21 @@ public function code(string $state = '', string $code = '', string $scope = '',
518532
$this->eventDispatcher->dispatchTyped(new UserLoggedInEvent($user, $user->getUID(), null, false));
519533
}
520534

521-
$tokenExchangeEnabled = (isset($oidcSystemConfig['token_exchange']) && $oidcSystemConfig['token_exchange'] === true);
522-
if ($tokenExchangeEnabled) {
535+
// remove code login session values
536+
$this->session->remove(self::STATE);
537+
$this->session->remove(self::NONCE);
538+
539+
// $tokenExchangeEnabled = (isset($oidcSystemConfig['token_exchange']) && $oidcSystemConfig['token_exchange'] === true);
540+
// if ($tokenExchangeEnabled) {
523541
// store all token information for potential token exchange requests
524-
$tokenData = array_merge(
525-
$data,
526-
['provider_id' => $providerId],
527-
);
528-
$this->tokenService->storeToken($tokenData);
529-
}
530-
$this->config->setUserValue($user->getUID(), Application::APP_ID, 'had_token_once', '1');
542+
// $tokenData = array_merge(
543+
// $data,
544+
// ['provider_id' => $providerId],
545+
// );
546+
// $this->tokenService->storeToken($tokenData);
547+
// }
548+
549+
// $this->config->setUserValue($user->getUID(), Application::APP_ID, 'had_token_once', '1');
531550

532551
// Set last password confirm to the future as we don't have passwords to confirm against with SSO
533552
$this->session->set('last-password-confirm', strtotime('+4 year', time()));
@@ -536,7 +555,8 @@ public function code(string $state = '', string $code = '', string $scope = '',
536555
try {
537556
$authToken = $this->authTokenProvider->getToken($this->session->getId());
538557
$this->sessionMapper->createSession(
539-
$idTokenPayload->sid ?? 'fallback-sid',
558+
//$idTokenPayload->sid ?? 'fallback-sid',
559+
$idTokenPayload->{'urn:telekom.com:session_token'} ?? 'fallback-sid',
540560
$idTokenPayload->sub ?? 'fallback-sub',
541561
$idTokenPayload->iss ?? 'fallback-iss',
542562
$authToken->getId(),
@@ -628,7 +648,9 @@ public function singleLogoutService() {
628648
}
629649

630650
// cleanup related oidc session
631-
$this->sessionMapper->deleteFromNcSessionId($this->session->getId());
651+
// it is not a good idea to remove the session early as some IDM send a backchannel logout also to the initiating system.
652+
// This will falsely fail if already deleted. So rely always on backchannel cleanup or make this an option?
653+
// $this->sessionMapper->deleteFromNcSessionId($this->session->getId());
632654

633655
$this->userSession->logout();
634656

@@ -716,7 +738,9 @@ public function backChannelLogout(string $providerIdentifier, string $logout_tok
716738
}
717739

718740
$sub = $logoutTokenPayload->sub;
719-
if ($oidcSession->getSub() !== $sub) {
741+
// if ($oidcSession->getSub() !== $sub) {
742+
// handle sub only if it is available; session is enough to identify a logout
743+
if (isset($logoutTokenPayload->sub) && ($oidcSession->getSub() !== $sub)) {
720744
return $this->getBackchannelLogoutErrorResponse(
721745
'invalid SUB',
722746
'The sub does not match the one from the login ID token',
@@ -741,17 +765,19 @@ public function backChannelLogout(string $providerIdentifier, string $logout_tok
741765
$userId = $authToken->getUID();
742766
$this->authTokenProvider->invalidateTokenById($userId, $authToken->getId());
743767
} catch (InvalidTokenException $e) {
744-
return $this->getBackchannelLogoutErrorResponse(
745-
'nc session not found',
746-
'The authentication session was not found in Nextcloud',
747-
['nc_auth_session_not_found' => $authTokenId]
748-
);
768+
// it is not a problem if the auth token is already deleted, so no error
769+
// return $this->getBackchannelLogoutErrorResponse(
770+
// 'nc session not found',
771+
// 'The authentication session was not found in Nextcloud',
772+
// ['nc_auth_session_not_found' => $authTokenId]
773+
// );
749774
}
750775

751776
// cleanup
752777
$this->sessionMapper->delete($oidcSession);
753778

754-
return new JSONResponse([], Http::STATUS_OK);
779+
// return new JSONResponse([], Http::STATUS_OK);
780+
return new JSONResponse();
755781
}
756782

757783
/**
@@ -785,4 +811,20 @@ private function toCodeChallenge(string $data): string {
785811
$s = str_replace('/', '_', $s); // 63rd char of encoding
786812
return $s;
787813
}
814+
815+
/**
816+
* Backward compatible function for MagentaCLOUD to smoothly transition to new config
817+
*
818+
* @PublicPage
819+
* @NoCSRFRequired
820+
* @BruteForceProtection(action=userOidcBackchannelLogout)
821+
*
822+
* @param string $logout_token
823+
* @return JSONResponse
824+
* @throws Exception
825+
* @throws \JsonException
826+
*/
827+
public function telekomBackChannelLogout(string $logout_token = '') {
828+
return $this->backChannelLogout('Telekom', $logout_token);
829+
}
788830
}

0 commit comments

Comments
 (0)