From ad1a2dacd150a559f18cc3f1bff730abff9f82ba Mon Sep 17 00:00:00 2001 From: Hoang Pham Date: Fri, 28 Jun 2024 18:51:10 +0700 Subject: [PATCH] feat: IdP Post --- lib/Controller/SAMLController.php | 41 +++++++++++++++++++++++++------ templates/login_post.php | 15 +++++++++++ 2 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 templates/login_post.php diff --git a/lib/Controller/SAMLController.php b/lib/Controller/SAMLController.php index 984cc660..3860927d 100644 --- a/lib/Controller/SAMLController.php +++ b/lib/Controller/SAMLController.php @@ -6,6 +6,7 @@ namespace OCA\User_SAML\Controller; +use Exception; use Firebase\JWT\JWT; use Firebase\JWT\Key; use OC\Core\Controller\ClientFlowLoginController; @@ -177,9 +178,9 @@ protected function assertGroupMemberships(): void { * @OnlyUnauthenticatedUsers * @NoCSRFRequired * - * @throws \Exception + * @throws Exception */ - public function login(int $idp = 1): Http\RedirectResponse { + public function login(int $idp = 1) { $originalUrl = (string)$this->request->getParam('originalUrl', ''); if (!$this->trustedDomainHelper->isTrustedUrl($originalUrl)) { $originalUrl = ''; @@ -192,6 +193,31 @@ public function login(int $idp = 1): Http\RedirectResponse { $returnUrl = $originalUrl ?: $this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.login'); $ssoUrl = $auth->login($returnUrl, [], false, false, true); + + $method = $this->request->getParam('method', 'get'); + if ($method === 'post') { + $query = parse_url($ssoUrl, PHP_URL_QUERY); + parse_str($query, $params); + $samlRequest = $params['SAMLRequest']; + $relayState = $params['RelayState'] ?? ''; + $sigAlg = $params['SigAlg'] ?? ''; + $signature = $params['Signature'] ?? ''; + + $nonce = base64_encode(random_bytes(16)); + $ssoUrl = explode('?', $ssoUrl)[0]; + + $response = new Http\TemplateResponse($this->appName, 'login_post', [ + 'ssoUrl' => $ssoUrl, + 'samlRequest' => $samlRequest, + 'relayState' => $relayState, + 'sigAlg' => $sigAlg, + 'signature' => $signature, + 'nonce' => $nonce, + ], 'guest'); + $response->addHeader('Content-Security-Policy', "script-src 'self' 'nonce-$nonce' 'strict-dynamic' 'unsafe-eval';"); + return $response; + } + $response = new Http\RedirectResponse($ssoUrl); // Small hack to make user_saml work with the loginflows @@ -257,7 +283,7 @@ public function login(int $idp = 1): Http\RedirectResponse { } break; default: - throw new \Exception( + throw new Exception( sprintf( 'Type of "%s" is not supported for user_saml', $type @@ -314,7 +340,7 @@ public function assertionConsumerService(): Http\RedirectResponse { // Decrypt and deserialize try { $cookie = $this->crypto->decrypt($cookie); - } catch (\Exception) { + } catch (Exception) { $this->logger->debug('Could not decrypt SAML cookie', ['app' => 'user_saml']); return new Http\RedirectResponse($this->urlGenerator->getAbsoluteURL('/')); } @@ -394,7 +420,7 @@ public function assertionConsumerService(): Http\RedirectResponse { } } catch (NoUserFoundException) { throw new \InvalidArgumentException('User "' . $this->userBackend->getCurrentUserId() . '" is not valid'); - } catch (\Exception $e) { + } catch (Exception $e) { $this->logger->critical($e->getMessage(), ['exception' => $e, 'app' => $this->appName]); $response = new Http\RedirectResponse($this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.notProvisioned')); $response->invalidateCookie('saml_data'); @@ -450,7 +476,7 @@ public function singleLogoutService(): Http\RedirectResponse { $idp = $decoded['idp'] ?? null; $pass = true; - } catch (\Exception) { + } catch (Exception) { } } else { // standard request : need read CRSF check @@ -617,7 +643,8 @@ private function getSSOUrl(string $redirectUrl, string $idp): string { [ 'requesttoken' => $csrfToken->getEncryptedValue(), 'originalUrl' => $originalUrl, - 'idp' => $idp + 'idp' => $idp, + 'method' => 'post', ] ); diff --git a/templates/login_post.php b/templates/login_post.php new file mode 100644 index 00000000..1fe3f59d --- /dev/null +++ b/templates/login_post.php @@ -0,0 +1,15 @@ +
+ + + + + +
+