avris/micrus-social

Social login handler for the Micrus framework

v3.4.1 2017-07-20 21:16 UTC

This package is auto-updated.

Last update: 2022-05-28 05:15:48 UTC


README

This is a module for Micrus framework that provides an implementation of user registration, login and account management, including social media login (OAuth2).

For an example implentation, check out Dibsy (git repo).

Currently it supports:

  • Facebook
  • Twitter
  • Google Plus
  • Github
  • Gitlab
  • LinkedIn
  • Microsoft
  • Yahoo

To install this module, open the file app/Config/modules.yml and add:

 - Avris\Micrus\Social\SocialModule

Then run:

composer require avris/micrus-social

The module uses:

Basic login

The module defines two abstract models: BaseUser and BaseAuthenticator. You need to extend them in App\Model namespace.

<?php
namespace App\Model;

use Avris\Micrus\Social\Model\BaseUser;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 */
class User extends BaseUser
{
    // project specific logic here
}

<?php
namespace App\Model;

use Avris\Micrus\Social\Model\BaseAuthenticator;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 */
class Authenticator extends BaseAuthenticator
{
    // project specific logic here
}

The same goes for the controller. To activate it, you need to "bring" it to the App\Controller namespace,where you can extend it as you wish:

<?php
namespace App\Controller;

use Avris\Micrus\Social\SocialController;
use Avris\Micrus\Annotations as M;

class UserController extends SocialController
{
}

Now all you need to do, is to render links to login/account/logout:

{% include 'User/indicator' %}

The module will provide all the necessary controllers, forms, views, translations (only English so far) and sending emails (see: Micrus Mailer).

You can customize almost everything, mainly by creating your own templates in App\View\Crud, extending the above models and controller and adding your translations in App\Locale.

Settings

In config.yml you can change the list of enabled sources:

social:
  sources: [facebook, google, twitter]
  assertHasMethodLeft: false

When the option assertHasMethodLeft is set, SocialModule will disallow the user to revoke their permissions for a social login, if they don't have any alternative login option enabled (like password).

Social media login

You will be asked for the app credentails for the OAuth providers. You can get them at:

Handler

This module will take care of the communication between your app and OAuth providers, but you will have to handle the result (persist a user in database, log them in). To do it, declare a handler service like that:

userManager:
  class: App\Service\User\UserManager
  parameters: [ '@orm.entityManager', '@securityManager', '@request']
  tags: [defaultParameters]

Example content:

<?php
namespace App\Service;

use App\Model\Authenticator;
use App\Model\User;
use Avris\Micrus\Controller\Http\Request;
use Avris\Micrus\Model\User\AuthenticatorInterface;
use Avris\Micrus\Social\SocialLoginException;
use Avris\Micrus\Social\SocialLoginHandlerInterface;
use Avris\Micrus\Tool\SecurityManager;
use Doctrine\ORM\EntityManager;

class UserManager implements SocialLoginHandlerInterface
{
    /** @var EntityManager */
    protected $em;

    /** @var SecurityManager */
    protected $sm;

    /** @var Request */
    protected $request;

    public function __construct(
        EntityManager $em,
        SecurityManager $sm,
        Request $request
    ) {
        $this->em = $em;
        $this->sm = $sm;
        $this->request = $request;
    }

    public function login(User $user)
    {
        if (!$user->isActive()) {
            $this->request->addFlash('danger', l('entity.User.custom.inactive'));
            return 'home';
        }

        $this->em->persist($user);
        $this->em->flush();
        $this->sm->login($user);

        return 'serverList';
    }

    public function loginSocial($source, $payload)
    {
        $user = $this->em->getRepository('Authenticator')->findUserBySocialAuth($source, $payload['id'])
            ?: new User($payload['email']);

        if (!$user->getAuthenticators($source)->count()) {
            $user->createAuthenticator($source, $payload);
        }

        return $this->login($user);
    }

    public function loginSocialFail(SocialLoginException $e)
    {
        $this->request->addFlash('danger', l('entity.User.socialLogin.loginFailed', ['%source%' => $e->getSource()]));

        return 'home';
    }

    public function revokeSocial(AuthenticatorInterface $auth)
    {
        /** @var Authenticator $auth */
        $auth->setValid(false);
        $this->em->persist($auth);
        $this->em->flush();
    }

    public function updateSocial(AuthenticatorInterface $auth, array $old, array $new)
    {
        /** @var Authenticator $auth */
        $auth->setPayload($new);
        $this->em->persist($auth);
    }
}

Login buttons

To present a user with a link to social login, use the route socialLogin with parameter source (facebook, twitter or google). You can also display all the available sources like that:

return $this->render([
    // ...
    'socialSources' => $this->get('socialManager')->getSources(),
]);

and in the view:

<ul>
    {% for source in socialSources %}
        <li>
            <a href="{{ route('socialLogin', {source: source}) }}">
                <span class="{{ source.icon }}"></span>
            </a>
        </li>
    {% endfor %}
</ul>

Extend forms

To extend any of the forms defined by the Social Module, simply overwrite the SocialController::FORM_* constants:

class UserController extends SocialController
{
    const FORM_ACCOUNT = 'App\Form\MyAccountForm';
}

Use username

By default, the Social Module uses email as a user identifier. It has however a built-in support for usernames too. If you enable it, selecting a username will be required during registration and the users will be able to log in either using that username or an email.

In config.yml:

social:
  useUsername: true

In App\Model\User:

<?php
namespace App\Model;

use Avris\Micrus\Social\Model\BaseUser;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(options={"collate"="utf8mb4_unicode_ci", "charset"="utf8mb4"})
 */
class User extends BaseUser
{
    /**
     * @var string
     * @ORM\Column(type="string", unique=true, nullable=true, length=36)
     */
    private $username;

    public function __construct($username = null, $email = null)
    {
        $this->username = $username;
        parent::__construct($email);
    }

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

    /**
     * @return string
     */
    public function getUsername()
    {
        return $this->username;
    }

    /**
     * @param string $username
     * @return User
     */
    public function setUsername($username)
    {
        $this->username = $username;
        return $this;
    }
}

And finally in services.yml:

userProvider:
  class: Avris\Micrus\Social\EmailOrUsernameUserProvider
  params: ['@orm']

Copyright