Skip to content

Instantly share code, notes, and snippets.

@markguinn
Created December 21, 2015 11:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save markguinn/203557ab0e5895b0f098 to your computer and use it in GitHub Desktop.
Save markguinn/203557ab0e5895b0f098 to your computer and use it in GitHub Desktop.
How we're handling OAuth+REST
<?php
/**
* @author Mark Guinn <mark@adaircreative.com>
* @date 12.18.2015
* @package simple-giving
* @subpackage auth
*/
class FacebookApi extends Object implements ISocialApi
{
/** @var string */
private static $app_id = '';
/** @var string */
private static $app_secret = '';
/**
* Checks with the site to confirm that the given token is indeed valid
* and corresponds with the userID we were given. It can do anything else
* it needs as well (e.g. facebook provides a debug_token endpoint)
*
* @param string $token
* @param string $userID
* @return boolean
*/
public function validateToken($token, $userID) {
$fb = new Facebook\Facebook([
'app_id' => self::config()->app_id,
'app_secret' => self::config()->app_secret,
'default_graph_version' => 'v2.2',
]);
try {
// Returns a `Facebook\FacebookResponse` object
$response = $fb->get('/me?fields=id,name', $token);
} catch(Facebook\Exceptions\FacebookResponseException $e) {
SS_Log::log("Graph returned an error: " . $e->getMessage(), SS_Log::ERR);
return false;
} catch(Facebook\Exceptions\FacebookSDKException $e) {
SS_Log::log('Facebook SDK returned an error: ' . $e->getMessage(), SS_Log::ERR);
return false;
}
$user = $response->getGraphUser();
return $user->getId() == $userID;
}
}
<?php
/**
*
*
* @author Mark Guinn <mark@adaircreative.com>
* @date 12.18.2015
* @package simple-giving
* @subpackage
*/
class GoogleApi implements ISocialApi
{
/**
* Checks with the site to confirm that the given token is indeed valid
* and corresponds with the userID we were given. It can do anything else
* it needs as well (e.g. facebook provides a debug_token endpoint)
*
* @param string $token
* @param string $userID
* @return boolean
*/
public function validateToken($token, $userID) {
throw new RestSystemException('Google authentication has not been implemented yet. Coming soon!', 500);
}
}
<?php
/**
* I'm choosing not to use OPauth or any of the other social API abstractions out there because
* 1) they are all very complicated and 2) the ones that worked didn't have all the services we
* need and 3) all of these social sites now provide really nice API's themselves so creating our
* own strategies/drivers is very simple given the very simple task we need to do and that we
* already have an access token.
*
* @author Mark Guinn <mark@adaircreative.com>
* @date 12.18.2015
* @package simple-giving
* @subpackage auth
*/
interface ISocialApi
{
/**
* Checks with the site to confirm that the given token is indeed valid
* and corresponds with the userID we were given. It can do anything else
* it needs as well (e.g. facebook provides a debug_token endpoint)
*
* @param string $token
* @param string $userID
* @return boolean
*/
public function validateToken($token, $userID);
}
<?php
/**
* Replaces the default REST api validator to allow social tokens or email/password
*
* @author Mark Guinn <mark@adaircreative.com>
* @date 12.18.2015
* @package simple-giving
* @subpackage auth
*/
class SessionValidatorWithSocial implements IRestValidator
{
/**
* Validates the given data and returns a mapped version back to the caller.
*
* @param array $data
* @return array
* @throws ValidationException
*/
public static function validate($data) {
// allow either email or Email
if (isset($data['email'])) $data['Email'] = $data['email'];
if (isset($data['password'])) $data['Password'] = $data['password'];
if (!empty($data['Token'])) {
return [
'Token' => RestValidatorHelper::validate_string($data, 'Token'),
'AuthService' => RestValidatorHelper::validate_string($data, 'AuthService'),
'UserID' => RestValidatorHelper::validate_string($data, 'UserID'),
];
} else {
return [
'Email' => RestValidatorHelper::validate_email($data, 'Email'),
'Password' => RestValidatorHelper::validate_string($data, 'Password', ['min' => 3]),
];
}
}
}
<?php
/**
* Overrides the default authenticator to allow either Email and Password OR Token, AuthService, and UserID.
*
* @author Mark Guinn <mark@adaircreative.com>
* @date 12.18.2015
* @package simple-giving
* @subpackage auth
*/
class SocialMemberAuthenticator extends MemberAuthenticator
{
private static $social_services = [
'facebook' => 'FacebookApi',
'google' => 'GoogleApi',
];
/**
* @param string $token
* @param string $service
* @param string $userID
* @return bool
*/
public static function validate_token($token, $service, $userID) {
$serviceMap = self::config()->social_services ?: [];
if (empty($service) || empty($serviceMap[$service])) return false;
/** @var ISocialApi $serviceApi */
$serviceApi = Injector::inst()->get($serviceMap[$service]);
return $serviceApi->validateToken($token, $userID);
}
/**
* Attempt to find and authenticate member if possible from the given data
*
* @param array $data
* @param Form $form
* @param bool &$success Success flag
* @return Member Found member, regardless of successful login
*/
protected static function authenticate_member($data, $form, &$success) {
if (!empty($data['Token'])) {
$success = false;
$result = new ValidationResult();
/** @var Member $member */
$member = null;
// First check that the token is valid
if (self::validate_token($data['Token'], $data['AuthService'], $data['UserID'])) {
// Second, check that the Member exists
$identity = SocialIdentity::get()->filter([
'AuthService' => $data['AuthService'],
'UserID' => $data['UserID'],
])->first();
if ($identity) {
$member = $identity->Member();
}
if ($member) {
$success = true;
} else {
$result->error("User not found");
}
} else {
$result->error("Invalid access token");
}
// Emit failure to member and form (if available)
if(!$success) {
if ($member) $member->registerFailedLogin();
if ($form) $form->sessionMessage($result->message(), 'bad');
} else {
if ($member) $member->registerSuccessfulLogin();
}
return $member;
} else {
return parent::authenticate_member($data, $form, $success);
}
}
/**
* Get the name of the authentication method
*
* @return string Returns the name of the authentication method.
*/
public static function get_name() {
return _t('SocialMemberAuthenticator.TITLE', "E-mail &amp; Password or Social Network");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment