diff --git a/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/README.md b/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/README.md new file mode 100644 index 000000000..4cfa51b2f --- /dev/null +++ b/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/README.md @@ -0,0 +1,15 @@ +## About +The goal of this module is to give ability +to log into Vitrual Y using OAuth2 protocol, in case if your CRM is Reclique. + +## How to use this integration. + +1. Enable this module. +2. Setup your Reclique SSO OAuth2 credentials +here: /admin/openy/virtual-ymca/gc-auth-settings/provider/reclique_sso +3. Set Reclique SSO OAuth2 as your main authorization plugin +at the Virtual YMCA settings: /admin/openy/openy-gc-auth/settings + +## I need help. +In case, if you need help, please write your question +at the #developers channel at Open Y slack. diff --git a/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/config/install/openy_gc_auth.provider.reclique_sso.yml b/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/config/install/openy_gc_auth.provider.reclique_sso.yml new file mode 100644 index 000000000..452a827d2 --- /dev/null +++ b/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/config/install/openy_gc_auth.provider.reclique_sso.yml @@ -0,0 +1,5 @@ +authorization_server: 'https://[association_slug].recliquecore.com' +client_id: '' +client_secret: '' +error_accompanying_message: 'Please contact us if you have any questions.' +login_mode: 'present_login_button' diff --git a/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/openy_gc_auth_reclique_sso.info.yml b/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/openy_gc_auth_reclique_sso.info.yml new file mode 100644 index 000000000..ca8589412 --- /dev/null +++ b/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/openy_gc_auth_reclique_sso.info.yml @@ -0,0 +1,9 @@ +name: Reclique SSO OAuth2 Virtual YMCA integration +type: module +description: 'Provides Reclique SSO OAuth2 authentication plugin for Virtual YMCA' +package: Open Y Virtual YMCA +version: 0.1 +core: 8.x +core_version_requirement: ^8 || ^9 +dependencies: + - drupal:openy_gc_auth diff --git a/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/openy_gc_auth_reclique_sso.module b/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/openy_gc_auth_reclique_sso.module new file mode 100644 index 000000000..8d40ec875 --- /dev/null +++ b/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/openy_gc_auth_reclique_sso.module @@ -0,0 +1,19 @@ +bundle(); + if ($bundle === 'gated_content_login') { + $build['#cache']['max-age'] = 0; + } +} diff --git a/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/openy_gc_auth_reclique_sso.routing.yml b/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/openy_gc_auth_reclique_sso.routing.yml new file mode 100644 index 000000000..ad60ea5a1 --- /dev/null +++ b/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/openy_gc_auth_reclique_sso.routing.yml @@ -0,0 +1,19 @@ +openy_gc_auth_reclique_sso.authenticate_redirect: + path: '/openy-gc-auth-reclique-sso/authenticate-redirect' + defaults: + _controller: '\Drupal\openy_gc_auth_reclique_sso\Controller\SSOController::authenticateRedirect' + _title: 'Reclique SSO OAuth2 authenticate redirect' + requirements: + _permission: 'access content' + options: + no_cache: 'TRUE' + +openy_gc_auth_reclique_sso.authenticate_callback: + path: '/openy-gc-auth-reclique-sso/authenticate-callback' + defaults: + _controller: '\Drupal\openy_gc_auth_reclique_sso\Controller\SSOController::authenticateCallback' + _title: 'Reclique SSO OAuth2 authenticate callback' + requirements: + _permission: 'access content' + options: + no_cache: 'TRUE' diff --git a/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/openy_gc_auth_reclique_sso.services.yml b/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/openy_gc_auth_reclique_sso.services.yml new file mode 100644 index 000000000..647498a90 --- /dev/null +++ b/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/openy_gc_auth_reclique_sso.services.yml @@ -0,0 +1,6 @@ +services: + openy_gc_auth.reclique_sso_client: + class: Drupal\openy_gc_auth_reclique_sso\SSOClient + arguments: [ '@logger.factory', '@config.factory', + '@http_client', '@csrf_token', + '@user.private_tempstore' ] diff --git a/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/src/Controller/SSOController.php b/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/src/Controller/SSOController.php new file mode 100644 index 000000000..0862afbf2 --- /dev/null +++ b/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/src/Controller/SSOController.php @@ -0,0 +1,162 @@ +configFactory = $configFactory; + $this->configOpenyGatedContent = $configFactory->get('openy_gated_content.settings'); + $this->gcUserAuthorizer = $gcUserAuthorizer; + $this->recliqueSSOClient = $recliqueSSOClient; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('config.factory'), + $container->get('openy_gc_auth.user_authorizer'), + $container->get('openy_gc_auth.reclique_sso_client') + ); + } + + /** + * Redirect, login user and return authorization code. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * Current request object. + * + * @return \Drupal\Core\Routing\TrustedRedirectResponse + * Redirect to the Reclique login page. + */ + public function authenticateRedirect(Request $request): TrustedRedirectResponse { + if (!empty($this->response)) { + return $this->response; + } + + $oAuth2AuthenticateUrl = $this->recliqueSSOClient->buildAuthenticationUrl($request); + $this->response = new TrustedRedirectResponse($oAuth2AuthenticateUrl); + $this->response->addCacheableDependency((new CacheableMetadata())->setCacheMaxAge(0)); + return $this->response; + } + + /** + * Receive authorization code, load user data and authorize user. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * Current request object. + * + * @return mixed + * Returns RedirectResponse or JsonResponse. + */ + public function authenticateCallback(Request $request) { + // Check code that was generated by Open Y. + if (!$this->recliqueSSOClient->validateCsrfToken($request->get('state'))) { + return new RedirectResponse( + URL::fromUserInput( + $this->configOpenyGatedContent->get('virtual_y_login_url'), + ['query' => ['error' => '1']] + )->toString() + ); + } + + $access_token = $this->recliqueSSOClient->exchangeCodeForAccessToken($request->get('code')); + + if (!$access_token) { + return new RedirectResponse( + URL::fromUserInput( + $this->configOpenyGatedContent->get('virtual_y_login_url'), + ['query' => ['error' => '1']] + )->toString() + ); + } + + $userData = $this->recliqueSSOClient->requestUserData($access_token); + + if (!$userData) { + return new RedirectResponse( + URL::fromUserInput( + $this->configOpenyGatedContent->get('virtual_y_login_url'), + ['query' => ['error' => '1']] + )->toString() + ); + } + + if ($this->recliqueSSOClient->validateUserSubscription($userData)) { + [$name, $email] = $this->recliqueSSOClient + ->prepareUserNameAndEmail($userData); + + // Authorize user (register, login, log, etc). + $this->gcUserAuthorizer->authorizeUser($name, $email); + + return new RedirectResponse($this->configOpenyGatedContent->get('virtual_y_url')); + } + + // Redirect back to Virual Y login page. + return new RedirectResponse( + URL::fromUserInput( + $this->configOpenyGatedContent->get('virtual_y_login_url'), + ['query' => ['error' => '1']] + )->toString() + ); + } + +} diff --git a/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/src/Form/ContinueWithRecliqueLoginForm.php b/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/src/Form/ContinueWithRecliqueLoginForm.php new file mode 100644 index 000000000..29f58687f --- /dev/null +++ b/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/src/Form/ContinueWithRecliqueLoginForm.php @@ -0,0 +1,55 @@ + 'link', + '#url' => Url::fromRoute('openy_gc_auth_reclique_sso.authenticate_redirect'), + '#title' => $this->t('Enter Virtual Y'), + '#attributes' => [ + 'class' => [ + 'gc-button', + ], + ], + ]; + + $form['#attributes'] = [ + 'class' => 'text-center', + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $form_state->setRedirectUrl(Url::fromRoute('openy_gc_auth_reclique_sso.authenticate_redirect')); + } + +} diff --git a/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/src/Form/TryAgainForm.php b/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/src/Form/TryAgainForm.php new file mode 100644 index 000000000..3c8c4efc6 --- /dev/null +++ b/modules/openy_gc_auth/modules/openy_gc_auth_reclique_sso/src/Form/TryAgainForm.php @@ -0,0 +1,105 @@ +currentRequest = $requestStack->getCurrentRequest(); + $this->configFactory = $config_factory; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('request_stack'), + $container->get('config.factory') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'openy_gc_auth_reclique_sso_try_again'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + if (!empty($this->currentRequest->query->get('error'))) { + $form['error'] = [ + '#markup' => '