port authorize with providers

This commit is contained in:
aina-esokia
2018-05-29 17:47:19 +04:00
parent cce12398dd
commit ff67cd5ac6
10 changed files with 210 additions and 20 deletions

View File

@@ -50,13 +50,15 @@ class Facebook extends AbstractProvider
/**
* {@inheritdoc}
*/
public function authenticate()
public function authenticate(array $params = array())
{
$params = array_merge(['providerId' => $this->getId()], $params);
return new RedirectResponse(
$this->facebook->getRedirectLoginHelper()->getLoginUrl(
$this->generator->generate(
'login_authentication_provider_callback',
['providerId' => $this->getId()],
$params,
UrlGenerator::ABSOLUTE_URL
),
['email']

View File

@@ -78,8 +78,10 @@ class Github extends AbstractProvider
/**
* {@inheritdoc}
*/
public function authenticate()
public function authenticate(array $params = array())
{
$params = array_merge(['providerId' => $this->getId()], $params);
$state = $this->createState();
$this->session->set('github.provider.state', $state);
@@ -90,7 +92,7 @@ class Github extends AbstractProvider
'state' => $state,
'redirect_uri' => $this->generator->generate(
'login_authentication_provider_callback',
['providerId' => $this->getId()],
$params,
UrlGenerator::ABSOLUTE_URL
),
], '', '&'));

View File

@@ -41,14 +41,6 @@ class GooglePlus extends AbstractProvider
'https://www.googleapis.com/auth/userinfo.profile',
]);
$this->client->setRedirectUri(
$this->generator->generate(
'login_authentication_provider_callback', [
'providerId' => $this->getId(),
], UrlGenerator::ABSOLUTE_URL
)
);
$this->client->setApprovalPrompt("auto");
if ($this->session->has('google-plus.provider.token')) {
@@ -115,8 +107,18 @@ class GooglePlus extends AbstractProvider
/**
* {@inheritdoc}
*/
public function authenticate()
public function authenticate(array $params = array())
{
$params = array_merge(['providerId' => $this->getId()], $params);
$this->client->setRedirectUri(
$this->generator->generate(
'login_authentication_provider_callback',
$params,
UrlGenerator::ABSOLUTE_URL
)
);
$state = $this->createState();
$this->session->set('google-plus.provider.state', $state);

View File

@@ -78,8 +78,10 @@ class Linkedin extends AbstractProvider
/**
* {@inheritdoc}
*/
public function authenticate()
public function authenticate(array $params = array())
{
$params = array_merge(['providerId' => $this->getId()], $params);
$state = $this->createState();
$this->session->set('linkedin.provider.state', $state);
@@ -91,7 +93,7 @@ class Linkedin extends AbstractProvider
'state' => $state,
'redirect_uri' => $this->generator->generate(
'login_authentication_provider_callback',
['providerId' => $this->getId()],
$params,
UrlGenerator::ABSOLUTE_URL
),
], '', '&'));

View File

@@ -43,9 +43,11 @@ interface ProviderInterface
/**
* Redirects to the actual authentication provider
*
* @param array $params
*
* @return RedirectResponse
*/
public function authenticate();
public function authenticate(array $params);
/**
* Logout from the provider, removes the token if possible

View File

@@ -69,14 +69,16 @@ class Twitter extends AbstractProvider
/**
* {@inheritdoc}
*/
public function authenticate()
public function authenticate(array $params = array())
{
$params = array_merge(['providerId' => $this->getId()], $params);
$code = $this->twitter->request(
'POST',
$this->twitter->url('oauth/request_token', ''),
['oauth_callback' => $this->generator->generate(
'login_authentication_provider_callback',
['providerId' => $this->getId()],
$params,
UrlGenerator::ABSOLUTE_URL
)]
);

View File

@@ -79,8 +79,10 @@ class Viadeo extends AbstractProvider
/**
* {@inheritdoc}
*/
public function authenticate()
public function authenticate(array $params = array())
{
$params = array_merge(['providerId' => $this->getId()], $params);
$state = $this->createState();
$this->session->set('viadeo.provider.state', $state);
@@ -91,7 +93,7 @@ class Viadeo extends AbstractProvider
'response_type' => 'code',
'redirect_uri' => $this->generator->generate(
'login_authentication_provider_callback',
['providerId' => $this->getId()],
$params,
UrlGenerator::ABSOLUTE_URL
),
]));

View File

@@ -167,6 +167,103 @@ class OAuth2Controller extends Controller
return '';
}
public function authorizeWithProviderAction(Request $request, $providerId)
{
$context = new Context(Context::CONTEXT_OAUTH2_NATIVE);
$this->dispatch(PhraseaEvents::PRE_AUTHENTICATE, new PreAuthenticate($request, $context));
//Check for auth params, send error or redirect if not valid
$params = $this->oAuth2Adapter->getAuthorizationRequestParameters($request);
/** @var ApiApplicationRepository $appRepository */
$appRepository = $this->app['repo.api-applications'];
if (null === $client = $appRepository->findByClientId($params['client_id'])) {
throw new NotFoundHttpException(sprintf('Application with client id %s could not be found', $params['client_id']));
}
$provider = $this->findProvider($providerId);
return $provider->authenticate($request->query->all());
}
public function authorizeCallbackAction(Request $request, $providerId)
{
$context = new Context(Context::CONTEXT_OAUTH2_NATIVE);
$provider = $this->findProvider($providerId);
$params = $this->oAuth2Adapter->getAuthorizationRequestParameters($request);
// triggers what's necessary
try {
$provider->onCallback($request);
$token = $provider->getToken();
} catch (NotAuthenticatedException $e) {
$this->getSession()->getFlashBag()->add('error', $this->app->trans('Unable to authenticate with %provider_name%', ['%provider_name%' => $provider->getName()]));
return $this->app->redirectPath('oauth2_authorize', array_merge(array('error' => 'login'), $params));
}
$userAuthProvider = $this->getUserAuthProviderRepository()
->findWithProviderAndId($token->getProvider()->getId(), $token->getId());
if($userAuthProvider == null){
unset($params['state']);
return $this->app->redirectPath('oauth2_authorize', array_merge(array('error' => 'login'), $params));
}
try {
$user = $this->getAuthenticationSuggestionFinder()->find($token);
} catch (NotAuthenticatedException $e) {
$this->app->addFlash('error', $this->app->trans('Unable to retrieve provider identity'));
return $this->app->redirectPath('oauth2_authorize', array_merge(array('error' => 'login'), $params));
}
$this->getAuthenticator()->openAccount($userAuthProvider->getUser());
$event = new PostAuthenticate($request, new Response(), $user, $context);
$this->dispatch(PhraseaEvents::POST_AUTHENTICATE, $event);
/** @var ApiApplicationRepository $appRepository */
$appRepository = $this->app['repo.api-applications'];
if (null === $client = $appRepository->findByClientId($params['client_id'])) {
throw new NotFoundHttpException(sprintf('Application with client id %s could not be found', $params['client_id']));
}
$this->oAuth2Adapter->setClient($client);
//check if current client is already authorized by current user
$clients = $appRepository->findAuthorizedAppsByUser($this->getAuthenticatedUser());
$appAuthorized = false;
foreach ($clients as $authClient) {
if ($client->getClientId() == $authClient->getClientId()) {
$appAuthorized = true;
break;
}
}
$account = $this->oAuth2Adapter->updateAccount($this->getAuthenticatedUser());
$params['account_id'] = $account->getId();
//if native app show template
if ($this->oAuth2Adapter->isNativeApp($params['redirect_uri'])) {
$params = $this->oAuth2Adapter->finishNativeClientAuthorization($appAuthorized, $params);
$r = new Response($this->render("api/auth/native_app_access_token.html.twig", $params));
$r->headers->set('Content-Type', 'text/html');
return $r;
}
$this->oAuth2Adapter->finishClientAuthorization($appAuthorized, $params);
// As OAuth2 library already outputs response content, we need to send an empty
// response to avoid breaking silex controller
return '';
}
/**
* TOKEN ENDPOINT
* Token endpoint - used to exchange an authorization grant for an access token.
@@ -206,4 +303,41 @@ class OAuth2Controller extends Controller
{
return $this->app['manipulator.api-account'];
}
/**
* @param string $providerId
* @return ProviderInterface
*/
private function findProvider($providerId)
{
try {
return $this->getAuthenticationProviders()->get($providerId);
} catch (InvalidArgumentException $e) {
throw new NotFoundHttpException('The requested provider does not exist');
}
}
/**
* @return ProvidersCollection
*/
private function getAuthenticationProviders()
{
return $this->app['authentication.providers'];
}
/**
* @return UsrAuthProviderRepository
*/
private function getUserAuthProviderRepository()
{
return $this->app['repo.usr-auth-providers'];
}
/**
* @return SuggestionFinder
*/
private function getAuthenticationSuggestionFinder()
{
return $this->app['authentication.suggestion-finder'];
}
}

View File

@@ -18,9 +18,12 @@ use Silex\Application;
use Silex\ControllerCollection;
use Silex\ControllerProviderInterface;
use Silex\ServiceProviderInterface;
use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait;
class OAuth2 extends Api implements ControllerProviderInterface, ServiceProviderInterface
{
use ControllerProviderTrait;
public function register(Application $app)
{
$app['controller.oauth2'] = $app->share(function (PhraseaApplication $app) {
@@ -35,6 +38,16 @@ class OAuth2 extends Api implements ControllerProviderInterface, ServiceProvider
public function connect(Application $app)
{
$firewall = $this->getFirewall($app);
$requireUnauthenticated = function () use ($firewall) {
if (null !== $response = $firewall->requireNotAuthenticated()) {
return $response;
}
return null;
};
if (! $this->isApiEnabled($app)) {
return $app['controllers_factory'];
}
@@ -48,6 +61,15 @@ class OAuth2 extends Api implements ControllerProviderInterface, ServiceProvider
$controllers->post('/token', 'controller.oauth2:tokenAction');
$controllers->get('/provider/{providerId}/authorize/', 'controller.oauth2:authorizeWithProviderAction')
->before($requireUnauthenticated)
->bind('oauth2_provider_authorize');
// AuthProviders callbacks
$controllers->get('/provider/{providerId}/callback/', 'controller.oauth2:authorizeCallbackAction')
->before($requireUnauthenticated)
->bind('login_authentication_provider_callback');
return $controllers;
}
}

View File

@@ -50,6 +50,26 @@
<input id="myPass" name="password" class="span6" type="password" placeholder="{{ 'admin::compte-utilisateur mot de passe' | trans }}" />
<input id="button_login" name="action_login" class="btn btn-inverse btn-large span6" type="submit" value="{{ 'Se connecter' | trans }}" />
</form>
<div class="well-large">
<div class="row-fluid">
<div class="text-center">
{{ "Or login with" | trans }}
</div>
</div>
<div class="row-fluid">
<div class="text-center">
<ul class="unstyled inline provider-list">
{% for provider in app['authentication.providers'] %}
<li>
<a href="{{ path('oauth2_provider_authorize', { 'providerId' : provider.getId() }|merge(app.request.query.all)) }}">
<img src="{{ provider.getIconURI() }}" />
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
<p>
<a href="#">{{ 'Problemes de connexion ?' | trans }}</a>
</p>