Introduction
Sentinel Social makes authenticating your users through social networks & third-party OAuth providers an absolute breeze.
The package follows the FIG standard PSR-4 to ensure a high level of interoperability between shared PHP code and is fully unit-tested.
The package requires PHP 8.0+.
Have a read through the Installation Guide.
Quick Example
Add Connections
Social::addConnection('facebook' => [
'driver' => 'Facebook',
'identifier' => '',
'secret' => '',
'scopes' => ['email'],
]);
Authorize
$callback = 'http://app.dev/callback.php';
$url = Social::getAuthorizationUrl('facebook', $callback);
header('Location: ' . $url);
exit;
Authenticate
$callback = 'http://app.dev/callback.php';
try {
$user = Social::authenticate('facebook', $callback, function(Cartalyst\Sentinel\Addons\Social\Models\LinkInterface $link, $provider, $token, $slug) {
// Retrieve the user in question for modificiation
$user = $link->getUser();
// You could add your custom data
$data = $provider->getUserDetails($token);
$user->foo = $data->foo;
$user->save();
});
} catch (Cartalyst\Sentinel\Addons\Social\AccessMissingException $e) {
var_dump($e); // You may save this to the session, redirect somewhere
die();
header('HTTP/1.0 404 Not Found');
}
Installation
The best and easiest way to install the Sentinel Social package is with Composer.
Preparation
Open your composer.json
and add the following to the require
array:
"cartalyst/sentinel-social": "^7.0"
Add the following lines after the require
array on your composer.json
file:
"repositories": [
{
"type": "composer",
"url": "https://packages.cartalyst.com"
}
],
Note: Make sure that after the required changes your
composer.json
file is valid by runningcomposer validate
.
Install the dependencies
Run Composer to install or update the new requirement.
php composer install
or
php composer update
Now you are able to require the vendor/autoload.php
file to autoload the package.
Integration
Laravel 9
The Sentinel Social package has optional support for Laravel 9 and it comes bundled with a
Service Provider and a Facade for easier integration.
After you have installed the package, just follow the instructions.
Open your Laravel config file config/app.php
and add the following lines.
In the $providers
array add the following service provider for this package.
Cartalyst\Sentinel\Addons\Social\Laravel\SocialServiceProvider::class,
In the $aliases
array add the following facade for this package.
'Social' => Cartalyst\Sentinel\Addons\Social\Laravel\Facades\Social::class,
Assets
Run the following commands to publish the migration and config files.
- Sentinel
php artisan vendor:publish --provider="Cartalyst\Sentinel\Laravel\SentinelServiceProvider"
- Sentinel Social
php artisan vendor:publish --provider="Cartalyst\Sentinel\Addons\Social\Laravel\SocialServiceProvider"
Migrations
Run the following command to migrate Sentinel after publishing the assets.
php artisan migrate
Configuration
After publishing, the sentinel social config file can be found under config/cartalyst.sentinel-addons.social.php
where you can modify the package configuration.
Native
After you have installed the package, just follow the instructions.
Setup your database
Sentinel schema
`vendor/cartalyst/sentinel/schema/mysql.sql`
Sentinel Social schema
`vendor/cartalyst/sentinel-social/schema/mysql.sql`
Configuration
Instantiate Sentinel Social
// Include the composer autoload file
require_once 'vendor/autoload.php';
// Import the necessary classes
use Cartalyst\Sentinel\Addons\Social\Manager;
$manager = new Manager($instanceOfSentinel);
$manager->addConnection('facebook' => [
'driver' => 'Facebook',
'identifier' => '',
'secret' => '',
'scopes' => ['email'],
]);
Authorize
$callback = 'http://app.dev/callback.php';
$url = $manager->getAuthorizationUrl('facebook', $callback);
header('Location: ' . $url);
exit;
Authenticate
$callback = 'http://app.dev/callback.php';
try {
$user = $manager->authenticate('facebook', $callback, function(Cartalyst\Sentinel\Addons\Social\Models\LinkInterface $link, $provider, $token, $slug) {
// Retrieve the user in question for modificiation
$user = $link->getUser();
// You could add your custom data
$data = $provider->getUserDetails($token);
$user->foo = $data->foo;
$user->save();
});
} catch (Cartalyst\Sentinel\Addons\Social\AccessMissingException $e) {
var_dump($e); // You may save this to the session, redirect somewhere
die();
header('HTTP/1.0 404 Not Found');
}
Usage
OAuth Flow
While OAuth1 and OAuth2 are incompatible protocols, they (for the most part) follow the same process:
- A secure connection is established between a your app and a provider.
- A user is redirected to the provider where they may login and approve (or reject) your app to have access.
- Your app receives a token from the service so your app may act on behalf of the person who authenticated. You never find out their password and they have the option to revoke your access at any point.
Sentinel Social abstracts all the differences between OAuth 1 and OAuth 2, so that you can focus on the more interesting parts of your app.
Manager
$manager = new Cartalyst\Sentinel\Addons\Social\Manager($instanceOfSentinel);
Social
is the Laravel alias for the manager and can be directly used without instantiation.
Connections
Single connection
Social::addConnection('facebook', [
'driver' => 'Facebook',
'identifier' => '',
'secret' => '',
'scopes' => ['email'],
]);
Multiple connections
$connections = [
'facebook' => [
'driver' => 'Facebook',
'identifier' => '',
'secret' => '',
'scopes' => ['email'],
],
'github' => [
'driver' => 'GitHub',
'identifier' => '',
'secret' => '',
'scopes' => ['user'],
],
];
Social::addConnections($connections);
Connections on Laravel are stored in
config/cartalyst.sentinel-addons.social.php
Authorization
Authorizing a user (redirecting them to the provider's login/approval screen) is extremely easy.
Once you've configured a provider with Sentinel Social, you simply need to redirect the user to the authorization URL.
Route::get('oauth/authorize', function() {
$callback = URL::to('oauth/callback');
$url = Social::getAuthorizationUrl('facebook', $callback);
return Redirect::to($url);
});
Authentication
Once a user has finished authorizing (or rejecting) your application, they're redirected to the callback URL which you specified.
To handle the authentication process, you will need to respond to the response from the provider on that callback URL.
Route::get('oauth/callback', function() {
// Callback is required for providers such as Facebook and a few others (it's required
// by the spec, but some providers omit this).
$callback = URL::current();
try {
$user = Social::authenticate('facebook', URL::current(), function(Cartalyst\Sentinel\Addons\Social\Models\LinkInterface $link, $provider, $token, $slug) {
// Retrieve the user in question for modificiation
$user = $link->getUser();
// You could add your custom data
$data = $provider->getUserDetails($token);
$user->foo = $data->foo;
$user->save();
});
} catch (Cartalyst\Sentinel\Addons\Social\AccessMissingException $e) {
// Missing OAuth parameters were missing from the query string.
// Either the person rejected the app, or the URL has been manually
// accesed.
if ($error = Input::get('error')) {
return Redirect::to('oauth')->withErrors($error);
}
App::abort(404);
}
});
Note: If you attempt to authenticate a provider when a Sentinel user is already logged in, the authenticated provider account will be linked with that User. For you as a developer, this allows your users to link multiple social accounts easily. If you don't want to allow other accounts to be linked, either don't show the social login links and/or log the user out at the start of the authorization process (in your controller).
Hooks
In addition to providing a hook (callback) for when a user is being linked (the second parameter passed to authenticate()
), we also provide ways to hook into new user registrations as well as existing user linking.
For example, this may be useful to send welcome emails when new users are being registered:
Social::registered(function(Cartalyst\Sentinel\Addons\Social\Models\LinkInterface $link, $provider, $token, $slug) {
$user = $link->getUser();
Mail::later($user->email, 'welcome', compact('user', 'slug'));
});
Social::existing(function(Cartalyst\Sentinel\Addons\Social\Models\LinkInterface $link, $provider, $token, $slug) {
// Callback for existing users
});
// Finally, after hooks are registered, you may authenticate away
$user = Social::authenticate($params);
Extending Sentinel Social
Sentinel Social was designed from the ground up with extendability in mind.
Extending is as simple as 2 steps
- Creating your implementation class.
- Adding the connection to Sentinel Social.
Creating An Implementation Class
To create an implementation class, you firstly need to determine if you're dealing with OAuth 1 or OAuth 2.
OAuth 1
To create an OAuth 1 implementation class for Sentinel Social, simply create a class which extends League\OAuth1\Client\Server\Server
.
Example:
use League\OAuth1\Client\Server\User;
class MyOAuth1Provider extends \League\OAuth1\Client\Server\Server
{
/**
* The response type for data returned from API calls.
*
* @var string
*/
protected $responseType = 'json';
/**
* Get the URL for retrieving temporary credentials.
*
* @return string
*/
public function urlTemporaryCredentials()
{
return 'https://api.myprovider.com/oauth/temporary_credentials';
}
/**
* Get the URL for redirecting the resource owner to authorize the client.
*
* @return string
*/
public function urlAuthorization()
{
return 'https://api.myprovider.com/oauth/authorize';
}
/**
* Get the URL retrieving token credentials.
*
* @return string
*/
public function urlTokenCredentials()
{
return 'https://api.myprovider.com/oauth/token_credentials';
}
/**
* Get the URL for retrieving user details.
*
* @return string
*/
public function urlUserDetails()
{
return 'https://api.myprovider/1.0/user.json';
}
/**
* Take the decoded data from the user details URL and convert
* it to a User object.
*
* @param mixed $data
* @param TokenCredentials $tokenCredentials
* @return User
*/
public function userDetails($data, TokenCredentials $tokenCredentials)
{
$user = new User;
// Take the decoded data (determined by $this->responseType)
// and fill out the user object by abstracting out the API
// properties (this keeps our user object simple and adds
// a layer of protection in-case the API response changes)
$user->first_name = $data['user']['firstname'];
$user->last_name = $data['user']['lastname'];
$user->email = $data['emails']['primary'];
// Etc..
return $user;
}
/**
* Take the decoded data from the user details URL and extract
* the user's UID.
*
* @param mixed $data
* @param TokenCredentials $tokenCredentials
* @return string|int
*/
public function userUid($data, TokenCredentials $tokenCredentials)
{
return $data['unique_id'];
}
/**
* Take the decoded data from the user details URL and extract
* the user's email.
*
* @param mixed $data
* @param TokenCredentials $tokenCredentials
* @return string
*/
public function userEmail($data, TokenCredentials $tokenCredentials)
{
// Optional
if (isset($data['email'])) {
return $data['email'];
}
}
/**
* Take the decoded data from the user details URL and extract
* the user's screen name.
*
* @param mixed $data
* @param TokenCredentials $tokenCredentials
* @return User
*/
public function userScreenName($data, TokenCredentials $tokenCredentials)
{
// Optional
if (isset($data['screen_name'])) {
return $data['screen_name'];
}
}
}
OAuth 2
The underlying OAuth 2 package provides a generic provider which can be used for various services. An example connection looks as follows.
'my_service' => [
'name' => 'My Service',
'driver' => 'GenericProvider',
'identifier' => '',
'secret' => '',
'scopes' => [
'user',
],
'additional_options' => [
'urlAuthorize' => 'https://api.my_service.com/authorize',
'urlAccessToken' => 'https://api.my_service.com/access_token',
'urlResourceOwnerDetails' => 'https://api.my_service.com/user',
],
],