Skip to content

Commit 5c87778

Browse files
committed
Fix login flow with SAML
Because of the strict samesite cookies SAML fails with the login flow. Because the post that comes back is not transfering the proper cookies to use the same session. Hence the token in use gets lost etc. Now we store this all (encrypted) in a cookie. So that when we come back we can restore the proper session. FAQ: * Is it elegant? Nope! * Does it work? Yes! Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
1 parent 800206a commit 5c87778

File tree

2 files changed

+35
-5
lines changed

2 files changed

+35
-5
lines changed

appinfo/info.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ While theoretically any other authentication provider implementing either one of
3333
<screenshot>https://raw.githubusercontent.com/nextcloud/user_saml/master/screenshots/1.png</screenshot>
3434
<screenshot>https://raw.githubusercontent.com/nextcloud/user_saml/master/screenshots/2.png</screenshot>
3535
<dependencies>
36-
<nextcloud min-version="20" max-version="20" />
36+
<nextcloud min-version="20" max-version="21" />
3737
</dependencies>
3838
<commands>
3939
<command>OCA\User_SAML\Command\GetMetadata</command>

lib/Controller/SAMLController.php

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
namespace OCA\User_SAML\Controller;
2424

2525
use Firebase\JWT\JWT;
26+
use OC\Core\Controller\ClientFlowLoginController;
27+
use OC\Core\Controller\ClientFlowLoginV2Controller;
2628
use OCA\User_SAML\Exceptions\NoUserFoundException;
2729
use OCA\User_SAML\SAMLSettings;
2830
use OCA\User_SAML\UserBackend;
@@ -184,11 +186,24 @@ public function login($idp) {
184186
$ssoUrl = $auth->login(null, [], false, false, true);
185187
$response = new Http\RedirectResponse($ssoUrl);
186188

189+
// Small hack to make user_saml work with the loginflows
190+
$flowData = [];
191+
192+
if ($this->session->get(ClientFlowLoginController::STATE_NAME) !== null) {
193+
$flowData['cf1'] = $this->session->get(ClientFlowLoginController::STATE_NAME);
194+
} else if ($this->session->get(ClientFlowLoginV2Controller::TOKEN_NAME) !== null) {
195+
$flowData['cf2'] = [
196+
'name' => $this->session->get(ClientFlowLoginV2Controller::TOKEN_NAME),
197+
'state' => $this->session->get(ClientFlowLoginV2Controller::STATE_NAME),
198+
];
199+
}
200+
187201
// Pack data as JSON so we can properly extract it later
188202
$data = json_encode([
189203
'AuthNRequestID' => $auth->getLastRequestID(),
190204
'OriginalUrl' => $this->request->getParam('originalUrl', ''),
191-
'Idp' => $idp
205+
'Idp' => $idp,
206+
'flow' => $flowData,
192207
]);
193208

194209
// Encrypt it
@@ -284,6 +299,15 @@ public function assertionConsumerService(): Http\RedirectResponse {
284299
}
285300
$data = json_decode($cookie, true);
286301

302+
if (isset($data['flow'])) {
303+
if (isset($data['flow']['cf1'])) {
304+
$this->session->set(ClientFlowLoginController::STATE_NAME, $data['flow']['cf1']);
305+
} else if (isset($data['flow']['cf2'])) {
306+
$this->session->set(ClientFlowLoginV2Controller::TOKEN_NAME, $data['flow']['cf2']['token']);
307+
$this->session->set(ClientFlowLoginV2Controller::STATE_NAME, $data['flow']['cf2']['state']);
308+
}
309+
310+
}
287311

288312
$AuthNRequestID = $data['AuthNRequestID'];
289313
$idp = $data['Idp'];
@@ -308,7 +332,9 @@ public function assertionConsumerService(): Http\RedirectResponse {
308332

309333
if (!$auth->isAuthenticated()) {
310334
$this->logger->info('Auth failed', ['app' => $this->appName]);
311-
return new Http\RedirectResponse($this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.notProvisioned'));
335+
$response = new Http\RedirectResponse($this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.notProvisioned'));
336+
$response->invalidateCookie('saml_data');
337+
return $response;
312338
}
313339

314340
// Check whether the user actually exists, if not redirect to an error page
@@ -317,7 +343,9 @@ public function assertionConsumerService(): Http\RedirectResponse {
317343
$this->autoprovisionIfPossible($auth->getAttributes());
318344
} catch (NoUserFoundException $e) {
319345
$this->logger->error($e->getMessage(), ['app' => $this->appName]);
320-
return new Http\RedirectResponse($this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.notProvisioned'));
346+
$response = new Http\RedirectResponse($this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.notProvisioned'));
347+
$response->invalidateCookie('saml_data');
348+
return $response;
321349
}
322350

323351
$this->session->set('user_saml.samlUserData', $auth->getAttributes());
@@ -338,7 +366,9 @@ public function assertionConsumerService(): Http\RedirectResponse {
338366
}
339367
} catch (\Exception $e) {
340368
$this->logger->logException($e, ['app' => $this->appName]);
341-
return new Http\RedirectResponse($this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.notProvisioned'));
369+
$response = new Http\RedirectResponse($this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.notProvisioned'));
370+
$response->invalidateCookie('saml_data');
371+
return $response;
342372
}
343373

344374
$originalUrl = $data['OriginalUrl'];

0 commit comments

Comments
 (0)