src/Platform/SecurityBundle/Controller/System/OAuthController.php line 123

Open in your IDE?
  1. <?php
  2. namespace Platform\SecurityBundle\Controller\System;
  3. use Cms\CoreBundle\Util\Controller;
  4. use Cms\TenantBundle\Entity\Tenant;
  5. use Platform\SecurityBundle\Controller\SingleSignOnController;
  6. use Platform\SecurityBundle\Model\OAuth\OAuthOptions;
  7. use Platform\SecurityBundle\Service\OAuth\OAuthProviderService;
  8. use Platform\SecurityBundle\Service\OAuth\Providers\AbstractOAuthProvider;
  9. use Symfony\Component\HttpFoundation\Exception\BadRequestException;
  10. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  11. use Symfony\Component\Routing\Annotation\Route;
  12. use Symfony\Component\HttpFoundation\RedirectResponse;
  13. use Symfony\Component\HttpFoundation\Request;
  14. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  15. use Laminas\Uri\Uri;
  16. /**
  17.  * Class OAuthEndpointController
  18.  * @package Platform\SecurityBundle\Controller\System
  19.  */
  20. class OAuthController extends Controller
  21. {
  22.     const ROUTES__CALLBACK 'platform.security.oauth.callback';
  23.     const ROUTES__INSTANT_LOGIN 'platform.security.oauth.instant_login';
  24.     /**
  25.      * @param Request $request
  26.      * @param string $provider
  27.      * @return RedirectResponse
  28.      * @throws \Exception
  29.      *
  30.      * @Route(
  31.      *     "/{provider}",
  32.      *     name = OAuthController::ROUTES__INSTANT_LOGIN
  33.      * )
  34.      */
  35.     public function instantAction(Request $requeststring $provider)
  36.     {
  37.         // obtain the provider
  38.         $provider $this->getOAuthProviderService()->getProvider($provider);
  39.         // make sure the provider supports instant logins
  40.         if ( ! $provider->isInstant()) {
  41.             throw new \Exception(sprintf(
  42.                 'OAuth provider "%s" does not support instant logins.',
  43.                 $provider->getId()
  44.             ));
  45.         }
  46.         // get a token
  47.         $token $provider->getAccessToken(
  48.             $request->query->get('code'),
  49.             new OAuthOptions([
  50.                 'callback' => strtok($request->getUri(), '?')
  51.             ])
  52.         );
  53.         // get the identity
  54.         $identity $provider->getMe($token);
  55.         // obtain the client id
  56.         // the format of this varies but uses the same field from the customized payload
  57.         $client $identity['client'];
  58.         if (empty($client)) {
  59.             throw new \Exception(sprintf(
  60.                 'Client ID not found in OAuth identity for provider "%s"',
  61.                 $provider->getId()
  62.             ));
  63.         }
  64.         // find the tenant for the district
  65.         $field sprintf(
  66.             '%sCustomerId',
  67.             $provider->getId()
  68.         );
  69.         $tenant $this->getEntityManager()->getRepository(Tenant::class)->findOneBy([
  70.             $field => $client,
  71.         ]);
  72.         if (empty($tenant)) {
  73.             throw new \Exception('Could not associate tenant to instant login attempt.');
  74.         }
  75.         /*
  76.          * TODO: make this cleaner...
  77.          * currently this just restarts the login process by forcing the user through the full flow
  78.          * this was actually suggested by clever or gg4l docs
  79.          * however, since we have kind of processed a login by this point, we should be able to just let them go in
  80.          */
  81.         // generate a new endpoint
  82.         $uri = new Uri($this->generateUrl(
  83.             SingleSignOnController::ROUTES__START,
  84.             [
  85.                 'id' => $provider->getId(),
  86.             ],
  87.             UrlGeneratorInterface::ABSOLUTE_URL
  88.         ));
  89.         $uri->setHost(sprintf(
  90.             '%s.%s',
  91.             $tenant->getSlug(),
  92.             $this->getGlobalContext()->getDashboard(true)
  93.         ));
  94.         // do the redirect
  95.         return new RedirectResponse($uri->toString());
  96.     }
  97.     /**
  98.      * Handles the OAuth callback endpoint.
  99.      * The job of this controller is to basically forward the request to the proper tenanted route.
  100.      *
  101.      * @param Request $request
  102.      * @return RedirectResponse
  103.      * @throws \Exception
  104.      *
  105.      * @Route(
  106.      *     "",
  107.      *     name = OAuthController::ROUTES__CALLBACK
  108.      * )
  109.      */
  110.     public function callbackAction(Request $request)
  111.     {
  112.         // if there is no state, that means somebody hit this directly or there was an error
  113.         if ( ! $request->query->has('state')) {
  114.             throw new BadRequestException();
  115.         }
  116.         // we should always have a state bag, grab its details
  117.         $state AbstractOauthProvider::parseState($request->query->get('state'));
  118.         // we should now have a redirect in the state
  119.         if (empty($state->get('redirect'))) {
  120.             throw new \Exception();
  121.         }
  122.         $redirect = new Uri($state->get('redirect'));
  123.         // need to merge in the query string from this request
  124.         $redirect->setQuery(array_merge(
  125.             $redirect->getQueryAsArray(),
  126.             $request->query->all()
  127.         ));
  128.         // perform the redirect
  129.         return new RedirectResponse($redirect->toString());
  130.     }
  131.     /**
  132.      * @return OAuthProviderService|object
  133.      */
  134.     private function getOAuthProviderService(): OAuthProviderService
  135.     {
  136.         return $this->get(__METHOD__);
  137.     }
  138. }