Giter Site home page Giter Site logo

silex-oauth's Introduction

OAuthServiceProvider

The OAuthServiceProvider integrates the lusitanian/oauth library with the Security component to provide social logins for the Silex microframework.

This library only provides the authentication system. You would have to supply your own user provider, or you can make use of the in-memory provider for testing.

Features

  • Supports most popular providers such as Facebook, Twitter, Google and GitHub
  • Extensible via event hooks so you can plug in your own listeners and user providers
  • Supports optional CSRF protection mechanism

Example

Check out the demo application for a code example.

Installation

Use Composer to install the gigablah/silex-oauth library by adding it to your composer.json.

You should use a version that is compatible with your Silex installation.

Take note that the versions specified for symfony/security (or any other Symfony component) in the examples below are based on compatibility with Silex. OAuthServiceProvider itself should be compatible with component versions from 2.3.0 onwards (please open an issue if this is not the case).

Silex 2.0

{
    "require": {
        "silex/silex": "~2.0@dev",
        "symfony/security": "~2.7,<3.0",
        "gigablah/silex-oauth": "~2.0@dev"
    }
}

Silex 1.3

{
    "require": {
        "silex/silex": "~1.3,<2.0",
        "symfony/security": "~2.4,<3.0",
        "gigablah/silex-oauth": "~1.3"
    }
}

Silex 1.0

{
    "require": {
        "silex/silex": ">=1.0 <1.3",
        "symfony/security": "~2.3.0",
        "gigablah/silex-oauth": "~1.0.0"
    }
}

Usage

First, you need to register the service provider and configure it with the application keys, secrets, scopes and user API endpoints for each OAuth provider you wish to support. Some examples are shown below:

ini_set('display_errors', 1);
error_reporting(-1);

require_once __DIR__.'/vendor/autoload.php';

define('FACEBOOK_API_KEY',    '');
define('FACEBOOK_API_SECRET', '');
define('TWITTER_API_KEY',     '');
define('TWITTER_API_SECRET',  '');
define('GOOGLE_API_KEY',      '');
define('GOOGLE_API_SECRET',   '');
define('GITHUB_API_KEY',      '');
define('GITHUB_API_SECRET',   '');

$app = new Silex\Application();
$app['debug'] = true;

$app->register(new Gigablah\Silex\OAuth\OAuthServiceProvider(), array(
    'oauth.services' => array(
        'Facebook' => array(
            'key' => FACEBOOK_API_KEY,
            'secret' => FACEBOOK_API_SECRET,
            'scope' => array('email'),
            'user_endpoint' => 'https://graph.facebook.com/me'
        ),
        'Twitter' => array(
            'key' => TWITTER_API_KEY,
            'secret' => TWITTER_API_SECRET,
            'scope' => array(),
            // Note: permission needs to be obtained from Twitter to use the include_email parameter
            'user_endpoint' => 'https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true',
            'user_callback' => function ($token, $userInfo, $service) {
                $token->setUser($userInfo['name']);
                $token->setEmail($userInfo['email']);
                $token->setUid($userInfo['id']);
            }
        ),
        'Google' => array(
            'key' => GOOGLE_API_KEY,
            'secret' => GOOGLE_API_SECRET,
            'scope' => array(
                'https://www.googleapis.com/auth/userinfo.email',
                'https://www.googleapis.com/auth/userinfo.profile'
            ),
            'user_endpoint' => 'https://www.googleapis.com/oauth2/v1/userinfo'
        ),
        'GitHub' => array(
            'key' => GITHUB_API_KEY,
            'secret' => GITHUB_API_SECRET,
            'scope' => array('user:email'),
            'user_endpoint' => 'https://api.github.com/user'
        )
    )
));

Next, register the oauth authentication provider in your firewall.

// Provides CSRF token generation
// You will have to include symfony/form in your composer.json
$app->register(new Silex\Provider\FormServiceProvider());

// Provides session storage
$app->register(new Silex\Provider\SessionServiceProvider(), array(
    'session.storage.save_path' => '/tmp'
));

$app->register(new Silex\Provider\SecurityServiceProvider(), array(
    'security.firewalls' => array(
        'default' => array(
            'pattern' => '^/',
            'anonymous' => true,
            'oauth' => array(
                //'login_path' => '/auth/{service}',
                //'callback_path' => '/auth/{service}/callback',
                //'check_path' => '/auth/{service}/check',
                'failure_path' => '/login',
                'with_csrf' => true
            ),
            'logout' => array(
                'logout_path' => '/logout',
                'with_csrf' => true
            ),
            // OAuthInMemoryUserProvider returns a StubUser and is intended only for testing.
            // Replace this with your own UserProvider and User class.
            'users' => new Gigablah\Silex\OAuth\Security\User\Provider\OAuthInMemoryUserProvider()
        )
    ),
    'security.access_rules' => array(
        array('^/auth', 'ROLE_USER')
    )
));

Note that the library assumes the default login, callback and check paths to be prefixed with /auth, so this path needs to be secured. You can uncomment the path options and change the defaults.

You will need to configure each of your OAuth providers with the correct absolute callback_path. For example, the default callback for Facebook would be http://your.domain/auth/facebook/callback.

Finally, you can provide a login/logout interface. This example assumes usage of the Twig templating engine:

// Provides Twig template engine
$app->register(new Silex\Provider\TwigServiceProvider(), array(
    'twig.path' => __DIR__.'/views'
));

$app->before(function (Symfony\Component\HttpFoundation\Request $request) use ($app) {
    if (isset($app['security.token_storage'])) {
        $token = $app['security.token_storage']->getToken();
    } else {
        $token = $app['security']->getToken();
    }

    $app['user'] = null;

    if ($token && !$app['security.trust_resolver']->isAnonymous($token)) {
        $app['user'] = $token->getUser();
    }
});

$app->get('/login', function (Symfony\Component\HttpFoundation\Request $request) use ($app) {
    $services = array_keys($app['oauth.services']);

    return $app['twig']->render('index.twig', array(
        'login_paths' => $app['oauth.login_paths'],
        'logout_path' => $app['url_generator']->generate('logout', array(
            '_csrf_token' => $app['oauth.csrf_token']('logout')
        )),
        'error' => $app['security.last_error']($request)
    ));
});

$app->match('/logout', function () {})->bind('logout');

The template itself:

<div>
  {% if error %}
  <p>{{ error }}</p>
  {% endif %}
  {% if app.user %}
    <p>Hello {{ app.user.username }}! Your email is {{ app.user.email }}</p>
    <a href="{{ logout_path }}">Logout</a>
  {% else %}
    <ul>
      <li><a href="{{ login_paths.facebook }}">Login with Facebook</a></li>
      <li><a href="{{ login_paths.twitter }}">Login with Twitter</a></li>
      <li><a href="{{ login_paths.google }}">Login with Google</a></li>
      <li><a href="{{ login_paths.github }}">Login with GitHub</a></li>
    </ul>
  {% endif %}
</div>

Custom Event Handlers

Two default event listeners are registered by default:

  • UserInfoListener executes right after an OAuth access token is successfully generated. The security token is then populated with user profile information from the configured API endpoint.
  • UserProviderListener executes at the point where the authentication provider queries for a user object from the user provider.

Depending on your application, you might want to automatically register OAuth users who do not already have an existing user account. This can be done by overriding UserProviderListener and placing your registration code in the listener function, or by simply registering a separate listener in the chain.

Custom Services

You can register your own services or override existing ones by manually specifying the class to instantiate:

$app->register(new Gigablah\Silex\OAuth\OAuthServiceProvider(), array(
    'oauth.services' => array(
        'my_service' => array(
            'class' => 'My\\Custom\\Namespace\\MyOAuthService',
            'key' => MY_API_KEY,
            'secret' => MY_API_SECRET,
            'scope' => array(),
            'user_endpoint' => 'https://my.domain/userinfo',
            'user_callback' => function ($token, $userInfo, $service) {
                ...
            }
        ),
        // ...
    )
));

License

Released under the MIT license. See the LICENSE file for details.

silex-oauth's People

Contributors

gigablah avatar hkdobrev avatar pitpit avatar rwos avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

silex-oauth's Issues

Facebook Missing app_id

Landing here on FB itself:
image

Here is my code that works great for twitter but not FB:

$app->register(new OAuthServiceProvider(), [
        'oauth.services' => [
            'twitter' => [
                'key' => $twitter['api_key'],
                'secret' => $twitter['api_secret'],
                'scope' => [],
                'user_endpoint' => $twitter['auth_endpoint']
            ],
            'facebook' => [
                'key' => $facebook['api_key'],
                'secret' => $facebook['api_secret'],
                'scope' => ['email'],
                'user_endpoint' => $facebook['auth_endpoint']
            ],
        ]
    ]);

Either a bug or me missing something obvious

Question regarding the redirect_uri

I ran into a weird bug and I am not sure what caused it.
The redirect url is not correctly generated anymore (it was working earlier and I failed to find what changed in the meantime).
It has something to do with the callback_route not using an absolute url.

Error example with a Google login:

Invalid parameter value for redirect_uri: Missing scheme: /xxx/auth/google/callback

What I found so far is that the url is incorrectly generated here: https://github.com/gigablah/silex-oauth/blob/master/src/OAuthServiceRegistry.php#L110

The true last parameter is leading to the ABSOLUTE_PATH constant of the UrlGeneratorInterface (see https://github.com/symfony/routing/blob/master/Generator/UrlGeneratorInterface.php#L42 )

Of course, if I change the true parameter to 0 (ABSOLUTE_URL) there is no more error.

Is there something I am missing?

Redirect to specified path?

I'm trying to have a login redirected back to a specified path on success.

I've tried what appears to be the Symfony default, _target_path, and that doesn't work - does anyone know how to achieve this?

Question: how persist in database?

It is possible to persist user data, (user id, email, name) in database maybe mysql or sqlite, could you give me an example or link that could help me. I was searching a lot without any result.

Thanks in advance.

Compatibility with Silex 1.3 and symfony/form 2.4+

Hi!

After updating to Silex 1.3 while using 2.4+ version of symfony the following error occurs:

Argument 12 passed to Gigablah\Silex\OAuth\Security\Firewall\OAuthAuthenticationListener::__construct() must be an instance of Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface, instance of Symfony\Component\Security\Csrf\CsrfTokenManager given, called in /vagrant/vendor/gigablah/silex-oauth/src/Gigablah/Silex/OAuth/OAuthServiceProvider.php on line 132

This is because of this change in Silex 1.3:
https://github.com/silexphp/Silex/blob/1.3/src/Silex/Provider/FormServiceProvider.php#L109

It looks like Silex 1.3 now returns a CsrfTokenManager instead of an object of CsrfProviderInterface, but only when using symfony/form 2.4+.

Any suggestions/ideas on how to fix this?
It looks like it might be hard for you to be compatible with the different variations here.

SymfonySession contains 5 abstract methods

I've fixed this just implementing the Interface methods, I don't know if I did something wrong or if this is a bug.

FatalErrorException: Error: Class Gigablah\Silex\OAuth\Storage\SymfonySession contains 5 abstract methods and must therefore be declared abstract or implement the remaining methods (OAuth\Common\Storage\TokenStorageInterface::storeAuthorizationState, OAuth\Common\Storage\TokenStorageInterface::hasAuthorizationState, OAuth\Common\Storage\TokenStorageInterface::retrieveAuthorizationState, ...) in /var/www/vox-developers/vendor/gigablah/silex-oauth/src/Gigablah/Silex/OAuth/Storage/SymfonySession.php line 113

Please create a new tag

Last tag was 2014, since then there was some progress. Please tag the latest version so i can use it properly for composer ๐Ÿ‘

Unable to set different pattern for the protected area

Hello,

I am implementing OAuth on an area of my application. I am moving from Form based auth to OAuth.

I have been trying to set the pattern for the login to something other that ^/ but I keep getting an error message when I click on my Google button:

image

The firewall is setup as follows:

    $app['security.firewalls'] = [
      'login' => [
        'pattern' => '^/admin',
        'anonymous' => true,
        'oauth' => [
          'failure_path' => '/login',
          'with_csrf' => true
        ],
        'users' => $app -> share(function() use ($app) {
          return $app['model.account'];
        })
      ]
    ];

If I leave the pattern as `^/' then it works, does this mean that it is only possible to use OAuth across the whole website?

Thanks, Russell

Error login with github

Again I don't know if I did something wrong or not. If I did please show me how can I configure the github login.

I'm using the Google's login, and it's working all right, but when I use the github I got this error:

FatalErrorException: Error: Call to a member function getAuthorizationUri() on a non-object in /var/www/vox-developers/vendor/gigablah/silex-oauth/src/Gigablah/Silex/OAuth/Security/Firewall/OAuthAuthenticationListener.php line 125

Seems like the register object can not find the github object, but it reads the correct configuration:

$app['oauth.services'] = array(
'google' => array(
'key' => 'myKey',
'secret' => 'mysecret',
'scope' => array(
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile'
),
'user_endpoint' => 'https://www.googleapis.com/oauth2/v1/userinfo'
),
'github' => array(
'key' => 'myKey',
'secret' => 'mysecret',
'scope' => array('user:email'),
'user_endpoint' => 'https://api.github.com/user'
)
);

Thank you for the help.

Allow passing of additional authorization uri parameters

I see that authorization uri is built here https://github.com/gigablah/silex-oauth/blob/master/src/Security/Firewall/OAuthAuthenticationListener.php#L135

The method getAuthorizationUri accepts an array of parameters so it would be cool if we could use that to pass additional information during the uri build process. I'm asking this because I would like to dinamically configure an authorization url parameter and, untill now, I've failed to find an alternative solution.

Injecting parameters into OAuth services

Hi,
I'm trying to inject some parameters through setters into the OAuth services. The base library you are using has some services which have setter methods to configure specific options. For example:

https://github.com/Lusitanian/PHPoAuthLib/blob/29bda6e176df6c292a9b1c43ae8a44d56a87c30b/src/OAuth/OAuth2/Service/Strava.php#L131

I've built a custom OAuth service and now I would like to do a similar thing using silex-oauth but I can't access services directly because they are stored in the ServiceRegistry and if I try to get a service from there with a getService call I get an exception:

RouteNotFoundException in UrlGenerator.php line 134: Unable to generate a URL for the named route "_auth_service_callback" as such route does not exist.

I guess that's because the route is created when the OAuthAuthenticationListener is created too while I'm trying to access the OAuth Service from another ServiceProvider I'm writing.

Is there a way provided by the library to achieve what I'm trying?

Purpose of check_path

Hey,

currently i do try to use this bundle.

What works is:

  • POST to google API
  • redirect to callback_route

What is not working:

  • check_path is called without query parameters
  • => results in missing code Exception

I am curious about what is the purpose of the check_path route and does it need to do anything ?
When i get the redirect to callback_route the code is in the query but then it does a redirect to check_path and there is the code removed from httpUtils. It seems like i need to recreate some response in order to serve the code i get in the callback_route ?

Mapping/normalizing user endpoint

see UserInforLister.php for:
$token->setUser($userInfo['name']);

"name" exists in some endpoints, but Dropbox for example doesn't return a name; they give "display_name".

Your code will trigger the following exception:

InvalidArgumentException: $user must be an instanceof UserInterface, an object implementing a __toString method, or a primitive string. (uncaught exception)

Any thoughts on mapping the different responses to a single normalized user object?

Confused with Authentication

One item I am really stuck with is the idea that I could leverage Google Open ID to login a user. I have modified some controller code to accept the incoming response at a certain route. Let's assume that all data is valid and I end up in this code block in my /auth/google controller :

if($openid->validate()) {

  $data = $openid->getAttributes();
  $email = $data['contact/email'];
  $first = $data['namePerson/first'];

  $message = "Identity: $openid->identity <br>"
  ."Email: $email <br>"
  ."First name: $first";

  // How do I now assign this User to the session?
  /*
  $provider = new UserProvider($this->app['db']);
  $user = $provider->loadUserByEmail($email);

  $checker = new UserChecker();
  $checker->checkPostAuth($user);
  */
}

I need to circumvent the Security model (which is already confusing to me) by:

  • Lookup the user by email
  • If valid assign to session

I am so baffled by all of the dependency injection, I just find myself looking circles so any help is appreciated.

Change the roles

Hi man,

I have a question, this is not an issue, but I didn't find where to put this doubt.

I'm using google login and every user that logs in the system, is recieving a ROLE_USER role.... Is there a way where I can check if an especific user is trying to login and change his role to ROLE_ADMIN or something?

The other question I have. Is there a way to block a especific google email to login?
Like @exemple.com only users with this email can login through google.

Sorry to bother you with this silly questions.
Appreciate the help.

Unserialize error with disabled users

Hello, I experience this error right after the Twitter redirection:

unserialize(): Error at offset 14 of 982 bytes 

This happens only if my User object's isEnabled() method returns false for whatever reason.

You can reproduce it easily modifying StubUser like this:

public function isEnabled()
    {
        return false;
    }

Keep up the good work.

Storage\SymfonySession is not aware of AttributeBag

from #19 (comment)

I am registering an AttributeBag

$app['session']->registerBag(
    new \Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag('fooBar')
);

This leads to storage/retrieval fails when it comes to use the SymfonySession. To fix it i was in need to use $this->session->getBag('attributes')->doo() for every call to the session in https://github.com/gigablah/silex-oauth/blob/master/src/Gigablah/Silex/OAuth/Storage/SymfonySession.php#L43-43

IMO the underlying storage should be aware of the attribute bag but unfortunately it is not.

Do i use it wrong or do i need to fix the SymfonySession that way ?

Adding Tumblr?

I can't seem to add a Tumblr option. I put in the key and secret and get redirected to the authentication page but I get an error on /auth/tumblr/check for:

InvalidArgumentException: $user must be an instanceof UserInterface, an object implementing a __toString method, or a primitive string.

not compatible Silex 2.x

hello,
your demo is not compatible Silex 2.x,
it would be good to mention it in the readme.txt,
or do the updates,
thank you

Silex 1.x
use Silex\ServiceProviderInterface;
Silex 2.x
use Pimple\ServiceProviderInterface;

Not retrieving FB email with silex-oauth-demo

Hi, not sure if it's a bug or was made on purpose but I'm unable to get the user email when requesting a Facebook login via the demo.

Here's the token retrieved within the OAuthInMemoryUserProvider::loadUserByOAuthCredentials method:

OAuthToken {#299 โ–ผ
  #service: "Facebook"
  #uid: ... // ok
  #email: null // returned email is null
  #accessToken: StdOAuth2Token {#265 โ–ผ
    #accessToken: ... // ok
    #refreshToken: null
    #endOfLife: 1481901530
    #extraParams: []
  }
  #providerKey: "default"
  -user: "Karen Alacibeeeecii Yangman"
  -roles: []
  -authenticated: false
  -attributes: []
}

As far as I know, there is nothing special to do on Facebook side but the basic API configuration.

Did I miss something?

Generated urls are relatives since symfony/routing 3.0

In the OAuthServiceRegistry true as value for the referenceType parameter to the url generator method generate() which is not supported in version 3.0 of symfony/routing (cf UrlGeneratorInterface for symfony 3.0)

You should add this in the composer.json

"symfony/routing": "~2.4,<3.0"

Btw, the readme tell us to add symfony/form, symfony/security dependencies to our composer.json, but why ?
the package dependencies should be in the gigablah/silex-oauth composer.json itself.

Extend StabUser

Hi there!
I want to store authenticated facebook user info to database but StubUser Class hasn't all the fields that facebook return. How can I extend StubUser to add extra fields such as facebook User id?
Hope you will help me :)

LinkedIn

Has this been tested with LinkedIn?

It seems there's an issue with the LinkedIn provider. Per the documentation (https://developer.linkedin.com/documents/authentication) there needs to be a "state" parameter that get's passed into the $oauthService->getAuthorizationUri() method and there's nothing that gets set in the OAuthAuthenticationListener class.

I've added a manual workaround but now I'm receiving an Error in retrieving token: "invalid_request" error.

Any thoughts?

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.