Coder Social home page Coder Social logo

sensiolabsconnectbundle's Introduction

SensioLabsConnectBundle

About

This is the official bundle of the SensioLabs Connect SDK.

Installation

Step 1: Install SensioLabsConnectBundle using Composer

$ composer require sensiolabs/connect-bundle

Step 2: Enable the bundle

<?php

// app/AppKernel.php
public function registerBundles()
{
    $bundles = array(
        // ...
        new SensioLabs\Bundle\ConnectBundle\SensioLabsConnectBundle(),
        // ...
    );
}

Step 3: Configure your config.yml file

# app/config/config.yml
sensio_labs_connect:
    app_id:     Your app id
    app_secret: Your app secret
    scope:      Your app scope # SCOPE_EMAIL SCOPE_PUBLIC

Usage

Use SensioLabsConnect to authenticate your user

Step 1: Configure the security

Note: If you want to persist your users, read the Cookbooks section.

If you don't want to persist your users, you can use ConnectInMemoryUserProvider:

# app/config/security.yml
security:
    providers:
        sensiolabs_connect:
            connect_memory: ~
    firewalls:
        dev: { pattern:  "^/(_(profiler|wdt)|css|images|js)/",  security: false }
        secured_area:
            pattern:    ^/
            sensiolabs_connect:
                check_path: oauth_callback
                login_path: sensiolabs_connect_new_session
                failure_path: homepage # need to be adapted to your config, see step 5
                remember_me: false
                provider: sensiolabs_connect
            anonymous: true

You can also load specific roles for some users:

# app/config/security.yml
security:
    providers:
        sensiolabs_connect:
            connect_memory:
                users:
                    90f28e69-9ce9-4a42-8b0e-e8c7fcc27713: "ROLE_CONNECT_USER ROLE_ADMIN"

Note: The username is the user uuid.

Step 2: Configure the routing

Import the default routing

# app/config/routing.yml
_sensiolabs_connect:
    resource: "@SensioLabsConnectBundle/Resources/config/routing.xml"

Step 3: Add some link to your templates

You can generate a link to the SensioLabs Connect login page:

<a href="{{ url('sensiolabs_connect_new_session') }}">Connect</a>

You can also specify the target URL after connection:

<a href="{{ url('sensiolabs_connect_new_session') }}?target=XXX">Connect</a>

Step 4: Play with the user

The API user is available through the security token:

$user = $this->container->get('security.context')->getToken()->getApiUser();

You can also get access to the API root object:

$accessToken = $this->container->get('security.context')->getToken()->getAccessToken();

$api = $this->get('sensiolabs_connect.api');
$api->setAccessToken($accessToken);

$root = $api->getRoot();
$user = $root->getCurrentUser();

If you use the built-in security component, you can access to the root api directly:

$api = $this->get('sensiolabs_connect.api');
$user = $api->getRoot()->getCurrentUser();

Step 5: Handling Failures

Note: this feature requires sensiolabs/connect v3.0.0

Several errors can occur during the OAuth dance, for example the user can deny your application or the scope you defined in config.yml can be different from what you selected while creating your application on SensioLabsConnect. Theses failures need to be handled.

Since sensiolabs/connect v3.0.0, failures handling is restored to the default Symfony failure handling.

Therefore, if an error occurred, the error is stored in the session (with a fallback on query attributes) and the user is redirected to the route/path specificed in failure_path node of the sensiolabs_connect section of your firewall in security.yml.

Warning: You need to specifiy failure_path. If you don't, the user will be redirected back to login_path, meaning that will launch the SensioLabsConnect authentication and redirect the user to SensioLabsConnect which can lead to a redirection loop.

This means you need to fetch the authentication error if there is one and display it in the view. This is similar to what you do for a typical login form on Symfony (here we assume you have a homepage route pointing to the AppBundle:Default:homepage controller):

// src/AppBundle/Controller/DefaultController.php

namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\SecurityContextInterface;

class DefaultController extends Controller
{
    public function homepageAction(Request $request)
    {
        $session = $request->getSession();

        // get the authentication error if there is one
        if ($request->attributes->has(SecurityContextInterface::AUTHENTICATION_ERROR)) {
            $error = $request->attributes->get(
                SecurityContextInterface::AUTHENTICATION_ERROR
            );
        } elseif (null !== $session && $session->has(SecurityContextInterface::AUTHENTICATION_ERROR)) {
            $error = $session->get(SecurityContextInterface::AUTHENTICATION_ERROR);
            $session->remove(SecurityContextInterface::AUTHENTICATION_ERROR);
        } else {
            $error = '';
        }

        return $this->render('default/homepage.html.twig', array('error' => $error));
    }
}

And then adapt your twig template:

{# app/Resources/views/default/homepage.html.twig #}

{% if app.user %}
    Congrats! You are authenticated with SensioLabsConnect
{% elseif error %}
    {{ error.messageKey | trans(error.messageData, 'security') }}
{% else %}
    <a href="{{ url('sensiolabs_connect_new_session') }}">Connect with SensioLabsConnect</a>
{% endif %}

Cookbooks

How to persist users

Step 1 - Create a User entity

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use SensioLabs\Connect\Api\Entity\User as ConnectApiUser;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * @ORM\Table(name="sl_user")
 * @ORM\Entity(repositoryClass="Sensiolabs\Bundle\HowToBundle\Repository\UserRepository")
 */
class User implements UserInterface
{
    /** @ORM\Column(type="integer") @ORM\Id @ORM\GeneratedValue(strategy="AUTO") */
    private $id;

    /** @ORM\Column(type="string", length=255) */
    private $uuid;

    /** @ORM\Column(type="string", length=255) */
    private $username;

    /** @ORM\Column(type="string", length=255) */
    private $name;

    public function __construct($uuid)
    {
        $this->uuid = $uuid;
    }

    public function updateFromConnect(ConnectApiUser $apiUser)
    {
        $this->username = $apiUser->getUsername();
        $this->name = $apiUser->getName();
    }

    public function getUuid()
    {
        return $this->uuid;
    }

    public function getUsername()
    {
        return $this->username;
    }

    public function getName()
    {
        return $this->name;
    }

    public function getRoles()
    {
        return array('ROLE_USER');
    }

    public function getPassword()
    {
    }

    public function getSalt()
    {
    }

    public function eraseCredentials()
    {
    }
}

Step 2 - Create the repository

<?php

namespace AppBundle\Repository;

use Doctrine\ORM\EntityRepository;
use AppBundle\Entity\User;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;

class UserRepository extends EntityRepository implements UserProviderInterface
{
    public function loadUserByUsername($uuid)
    {
        $user = $this->findOneByUuid($uuid);

        if (!$user) {
            $user = new User($uuid);
        }

        return $user;
    }

    public function refreshUser(UserInterface $user)
    {
        if (!$user instanceof User) {
            throw new UnsupportedUserException(sprintf('class %s is not supported', get_class($user)));
        }

        return $this->loadUserByUsername($user->getUuid());
    }

    public function supportsClass($class)
    {
        return 'AppBundle\Entity\User' === $class;
    }
}

Don't forget to update your database.

Step 3 - Create the event listener

<?php

namespace AppBundle\EventListener;

use Doctrine\ORM\EntityManager;
use SensioLabs\Connect\Security\Authentication\Token\ConnectToken;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;

class SecurityInteractiveLoginListener implements EventSubscriberInterface
{
    private $em;

    public static function getSubscribedEvents()
    {
        return array(
            SecurityEvents::INTERACTIVE_LOGIN => 'registerUser',
        );
    }

    public function __construct(EntityManager $em)
    {
        $this->em = $em;
    }

    public function registerUser(InteractiveLoginEvent $event)
    {
        $token = $event->getAuthenticationToken();

        if (!$token instanceof ConnectToken) {
            return;
        }

        $user = $token->getUser();
        $user->updateFromConnect($token->getApiUser());

        $this->em->persist($user);
        $this->em->flush($user);
    }
}

Step 4 - Wire everything

Step 4.1 - Add new services
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <service id="app.repository.user" class="AppBundle\Repository\UserRepository" factory-service="doctrine" factory-method="getRepository">
            <argument>SensiolabsHowToBundle:User</argument>
        </service>
        <service id="app.event_listener.interactive_login" class="AppBundle\EventListener\SecurityInteractiveLoginListener">
            <tag name="kernel.event_subscriber" />
            <argument type="service" id="doctrine.orm.entity_manager" />
        </service>
    </services>
</container>
Step 4.2 - Configure security
# app/config/security.yml
security:
    encoders:
        AppBundle\Entity\User: plaintext

    providers:
        sensiolabs_connect:
            id: app.repository.user

Step 5 - Enjoy

You can store more things if you want. But don't forget to update your application scope.

License

This bundle is licensed under the MIT license.

sensiolabsconnectbundle's People

Contributors

fabpot avatar javiereguiluz avatar lyrixx avatar marcw avatar saro0h avatar tucksaun avatar

Watchers

 avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.