Skip to content

Commit

Permalink
Added autologin option in querystring to login with access token
Browse files Browse the repository at this point in the history
If a user is allready login, we simply redirect without reauthenticate

Added doc of autologin

Fixed, autologin when no token is aviaible
  • Loading branch information
jdeveloper authored and Chrysweel committed Jul 27, 2015
1 parent 2fc6cde commit 88ea553
Show file tree
Hide file tree
Showing 12 changed files with 311 additions and 2 deletions.
2 changes: 2 additions & 0 deletions ChateaSecureBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Ant\Bundle\ChateaSecureBundle;

use Ant\Bundle\ChateaSecureBundle\DependencyInjection\Factory\AutologinSecurityFactory;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Ant\Bundle\ChateaSecureBundle\DependencyInjection\Factory\SecurityFactory;
Expand All @@ -12,5 +13,6 @@ public function build(ContainerBuilder $container)
{
$extension = $container->getExtension('security');
$extension->addSecurityListenerFactory(new SecurityFactory());
$extension->addSecurityListenerFactory(new AutologinSecurityFactory());
}
}
49 changes: 49 additions & 0 deletions Client/HttpAdapter/GuzzleHttpAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,55 @@ public function withRefreshToken($refresh_token)
}
}

/**
* After the client has been authorized for access, they can use a access token to get a access token.
*
* @param string $access_token The client access token that you obtain in first request of credentials.
*
* @return array|string Associative array with client credentials | Message with error in json format
*
* @throws InvalidArgumentException This exception is thrown if any parameter has errors
*
* @throws AuthenticationException This exception is thrown if you do not credentials or you cannot use this method
*
* @example Get client credentials
*
* $authenticationInstande->withAccessToken('access-token-demo');
*
* array("access_token" => access-token-demo,
* "expires_in" => 3600,
* "token_type" => bearer,
* "scope" => access_token,
* "refresh_token" => access-token-demo
* );
*/
public function withAccessToken($access_token)
{
if (!is_string($access_token) || 0 >= strlen($access_token)) {
throw new InvalidArgumentException("access_token must be a non-empty string");
}

$command = $this->getCommand('withAccessToken',
array('client_id'=>$this->getClientId(),'client_secret'=>$this->getSecret(),'access_token'=>$access_token)
);

try{
return $command->execute();
}catch (ServerErrorResponseException $ex){
throw new ApiException();
}catch (BadResponseException $ex){
if($ex->getResponse()->getStatusCode() == 400){
throw new AuthenticationException($ex->getMessage(), 400, $ex);
}else{
throw new ApiException();
}
}catch(ClientErrorResponseException $ex){
throw new AuthenticationException($ex->getMessage(), 400, $ex);
}catch(CurlException $ex){
throw new ApiException();
}
}

/**
* After the client has been authorized for access, they can use a refresh token to get a new access token.
*
Expand Down
25 changes: 25 additions & 0 deletions Client/HttpAdapter/HttpAdapterInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ public function withAuthorizationCode($auth_code, $redirect_uri);
* );
*/
public function withClientCredentials();

/**
* After the client has been authorized for access, they can use a refresh token to get a new access token.
*
Expand All @@ -125,6 +126,30 @@ public function withClientCredentials();
*/
public function withRefreshToken($refresh_token);

/**
* After the client has been authorized for access, they can use a access token to get a access token.
*
* @param string $access_token The client access token that you obtain in first request of credentials.
*
* @return array|string Associative array with client credentials | Message with error in json format
*
* @throws InvalidArgumentException This exception is thrown if any parameter has errors
*
* @throws AuthenticationException This exception is thrown if you do not credentials or you cannot use this method
*
* @example Get client credentials
*
* $authenticationInstande->withAccessToken('access-token-demo');
*
* array("access_token" => access-token-demo,
* "expires_in" => 3600,
* "token_type" => bearer,
* "scope" => access_token,
* "refresh_token" => access-token-demo
* );
*/
public function withAccessToken($access_token);

/**
* After the client has been authorized for access, they can use a refresh token to get a new access token.
*
Expand Down
50 changes: 50 additions & 0 deletions DependencyInjection/Factory/AutologinSecurityFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php
namespace Ant\Bundle\ChateaSecureBundle\DependencyInjection\Factory;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;

class AutologinSecurityFactory implements SecurityFactoryInterface
{
public function getKey()
{
return 'antwebs_chateasecure_login';
}

protected function getListenerId()
{
return 'security.authentication.listener.autologin';
}

public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$providerId = 'ecurity.authentication_provider.antwebs_chateasecure.'.$id;
$container
->setDefinition($providerId, new DefinitionDecorator('security.authentication_provider.antwebs_chateasecure'))
->replaceArgument(0, new Reference($userProvider))
;

$listenerId = 'ant_bundle.chateasecurebundle.security.firewall.autologinlistener.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('ant_bundle.chateasecurebundle.security.firewall.autologinlistener'));

return array($providerId, $listenerId, $defaultEntryPoint);
}

/**
* Defines the position at which the provider is called.
* Possible values: pre_auth, form, http, and remember_me.
*
* @return string
*/
public function getPosition()
{
return 'pre_auth';
}

public function addConfiguration(NodeDefinition $builder)
{
}
}
33 changes: 32 additions & 1 deletion Resources/config/api-services.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,41 @@
}
}
},
"withAccessToken":{
"httpMethod": "POST",
"uri": "oauth/v2/token",
"summary": "Auth client with access token",
"parameters": {
"grant_type":{
"location": "json",
"type": "string",
"default": "access_token",
"description": "the grant_type"
},
"client_id":{
"location": "json",
"type": "string",
"required": true,
"description": "the client_id"
},
"client_secret":{
"location": "json",
"type": "string",
"required": true,
"description": "the secret"
},
"access_token":{
"location": "json",
"type": "string",
"required": true,
"description": "the access_token"
}
}
},
"withRefreshToken":{
"httpMethod": "POST",
"uri": "oauth/v2/token",
"summary": "Auth client with Authorization Code",
"summary": "Auth client with refresh token",
"parameters": {
"grant_type":{
"location": "json",
Expand Down
6 changes: 6 additions & 0 deletions Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<parameter key="antwebs_chateasecure.guzzle_client.class">Guzzle\Service\Client</parameter>

<parameter key="antwewebs_revoke_access_on_logout_handler.class">Ant\Bundle\ChateaSecureBundle\Security\Http\Logout\RevokeAccessOnLogoutHandler</parameter>
<parameter key="ant_bundle.chateasecurebundle.security.firewall.autologinlistener.class">Ant\Bundle\ChateaSecureBundle\Security\Firewall\AutologinListener</parameter>

</parameters>

Expand Down Expand Up @@ -42,5 +43,10 @@
<argument type="service" id="security.context" />
<argument type="service" id="chat_secure.adapter" />
</service>

<service class="%ant_bundle.chateasecurebundle.security.firewall.autologinlistener.class%" id="ant_bundle.chateasecurebundle.security.firewall.autologinlistener">
<argument id="security.context" type="service"/>
<argument id="security.authentication.manager" type="service"/>
</service>
</services>
</container>
8 changes: 8 additions & 0 deletions Resources/doc/login-redirect.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,12 @@ también lo como servicio en services.xml (en el security bundle):
if(!$request->getSession()->get('referer')){
$request->getSession()->set('referer', $this->getRequest()->headers->get('referer'));
}
```

# Autologin

Si se tiene el access token de un usuario se puede loguear al usuario a traves de cualquier URL añadiendo el parametro querystring ```autologin``` con el access token por ejemplo:

```
http://misuperchatsocial.com/usuarios?autologin?ACCESS_TOKEN
```
17 changes: 16 additions & 1 deletion Security/Authentication/AuthenticationProvider.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<?php
namespace Ant\Bundle\ChateaSecureBundle\Security\Authentication;

use Ant\Bundle\ChateaSecureBundle\Security\Token\AccessTokenToken;
use Ant\Bundle\ChateaSecureBundle\Security\User\ChateaUserProviderInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\Authentication\Provider\UserAuthenticationProvider;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
Expand Down Expand Up @@ -44,7 +46,11 @@ protected function retrieveUser($username, UsernamePasswordToken $token)
return $user;
}

return $this->userProvider->loadUser($username,$token->getCredentials());
if($token instanceof AccessTokenToken){
return $this->userProvider->loadUserByAccessToken($token->getCredentials());
}else{
return $this->userProvider->loadUser($username,$token->getCredentials());
}
}

/**
Expand All @@ -60,4 +66,13 @@ protected function checkAuthentication(UserInterface $user, UsernamePasswordToke
{
//throw new AuthenticationException("This method is not supported yet.");
}

/**
* {@inheritdoc}
*/
public function supports(TokenInterface $token)
{
return $token instanceof AccessTokenToken ||
parent::supports($token);
}
}
77 changes: 77 additions & 0 deletions Security/Firewall/AutologinListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

namespace Ant\Bundle\ChateaSecureBundle\Security\Firewall;

use Ant\Bundle\ChateaSecureBundle\Security\Token\AccessTokenToken;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Http\Firewall\ListenerInterface;

class AutologinListener implements ListenerInterface
{
protected $securityContext;
protected $authenticationManager;

/**
* @param SecurityContextInterface $securityContext
* @param AuthenticationManagerInterface $authenticationManager
*/
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager)
{
$this->securityContext = $securityContext;
$this->authenticationManager = $authenticationManager;
}

/**
* This interface must be implemented by firewall listeners.
*
* @param GetResponseEvent $event
*/
public function handle(GetResponseEvent $event)
{
$request = $event->getRequest();

if($request->query->has('autologin')){
$token = new AccessTokenToken($request->query->get('autologin'));

try{
$this->authenticateIfUserIsNotLoggedIn($token);
$this->setRedirectResponse($event);
}catch(\Exception $failed) {
$this->setRedirectResponse($event);
}
}
}

/**
* @param GetResponseEvent $event
* @param $request
*/
private function setRedirectResponse(GetResponseEvent $event)
{
$request = $event->getRequest();

$request->query->remove('autologin');
$request->overrideGlobals();

$redirectResponse = new RedirectResponse($request->getUri());
$event->setResponse($redirectResponse);
}

/**
* @param $token
*/
private function authenticateIfUserIsNotLoggedIn($token)
{
if($this->securityContext->getToken() !== null && $this->securityContext->isGranted('IS_AUTHENTICATED_FULLY')){
return;
}

$authToken = $this->authenticationManager->authenticate($token);

$this->securityContext->setToken($authToken);
}
}
23 changes: 23 additions & 0 deletions Security/Token/AccessTokenToken.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Ant\Bundle\ChateaSecureBundle\Security\Token;


use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;

class AccessTokenToken extends UsernamePasswordToken
{
public function __construct($accessToken, $roles = array())
{
$this->accessToken = $accessToken;
parent::__construct('', $accessToken, 'access-token', $roles);
}

/**
* @return mixed
*/
public function getAccessToken()
{
return $this->accessToken;
}
}
2 changes: 2 additions & 0 deletions Security/User/ChateaUserProviderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ interface ChateaUserProviderInterface extends UserProviderInterface
public function loadUser($username, $password);

public function loadUserByFacebookId($facebookId);

public function loadUserByAccessToken($accessToken);
}
Loading

0 comments on commit 88ea553

Please sign in to comment.