Skip to content
Snippets Groups Projects
Unverified Commit 5582e70e authored by Tim van Dijen's avatar Tim van Dijen Committed by GitHub
Browse files

Externalize authlinkedin (#1065)

parent eebf4de7
No related branches found
No related tags found
No related merge requests found
......@@ -48,6 +48,7 @@ php "$TARGET/composer.phar" install --no-dev --prefer-dist -o -d "$TARGET"
# Install external modules
php "$TARGET/composer.phar" require --update-no-dev simplesamlphp/simplesamlphp-module-authfacebook
php "$TARGET/composer.phar" require --update-no-dev simplesamlphp/simplesamlphp-module-authlinkedin
php "$TARGET/composer.phar" require --update-no-dev simplesamlphp/simplesamlphp-module-authtwitter
php "$TARGET/composer.phar" require --update-no-dev simplesamlphp/simplesamlphp-module-authwindowslive
php "$TARGET/composer.phar" require --update-no-dev simplesamlphp/simplesamlphp-module-cdc
......
This file indicates that the default state of this module
is disabled. To enable, create a file named enable in the
same directory as this file.
Using the LinkedIn authentication source with SimpleSAMLphp
===========================================================
Remember to configure `authsources.php`, with both Consumer key and secret. Optionally you can set which attributes are requested from LinkedIn, the list of available attributes can be found here <https://developer.linkedin.com/docs/fields>.
To get an API key and a secret, register the application at:
* <https://www.linkedin.com/secure/developer>
Set the callback URL to be:
* `http://sp.example.org/simplesaml/module.php/authlinkedin/linkback.php`
Replace `sp.example.org` with your hostname.
## Testing authentication
On the SimpleSAMLphp frontpage, go to the *Authentication* tab, and use the link:
* *Test configured authentication sources*
Then choose the *linkedin* authentication source.
Expected behaviour would then be that you are sent to LinkedIn and asked to login.
There is no consent screen for attribute release.
<?php
namespace SimpleSAML\Module\authlinkedin\Auth\Source;
require_once(dirname(dirname(dirname(dirname(dirname(__FILE__))))).'/oauth/libextinc/OAuth.php');
/**
* Authenticate using LinkedIn.
*
* @author Brook Schofield, TERENA.
* @package SimpleSAMLphp
*/
class LinkedIn extends \SimpleSAML\Auth\Source
{
/**
* The string used to identify our states.
*/
const STAGE_INIT = 'authlinkedin:init';
/**
* The key of the AuthId field in the state.
*/
const AUTHID = 'authlinkedin:AuthId';
/** @var string */
private $key;
/** @var string */
private $secret;
/** @var string */
private $attributes;
/**
* Constructor for this authentication source.
*
* @param array $info Information about this authentication source.
* @param array $config Configuration.
* @throws \Exception
*/
public function __construct($info, $config)
{
assert(is_array($info));
assert(is_array($config));
// Call the parent constructor first, as required by the interface
parent::__construct($info, $config);
if (!array_key_exists('key', $config)) {
throw new \Exception('LinkedIn authentication source is not properly configured: missing [key]');
}
$this->key = $config['key'];
if (!array_key_exists('secret', $config)) {
throw new \Exception('LinkedIn authentication source is not properly configured: missing [secret]');
}
$this->secret = $config['secret'];
if (array_key_exists('attributes', $config)) {
$this->attributes = $config['attributes'];
} else {
// Default values if the attributes are not set in config (ref https://developer.linkedin.com/docs/fields)
$this->attributes = 'id,first-name,last-name,headline,summary,specialties,picture-url,email-address';
}
}
/**
* Log-in using LinkedIn platform
* Documentation at: http://developer.linkedin.com/docs/DOC-1008
*
* @param array &$state Information about the current authentication.
* @return void
*/
public function authenticate(&$state)
{
assert(is_array($state));
// We are going to need the authId in order to retrieve this authentication source later
$state[self::AUTHID] = $this->authId;
$stateID = \SimpleSAML\Auth\State::getStateId($state);
\SimpleSAML\Logger::debug('authlinkedin auth state id = '.$stateID);
$consumer = new \SimpleSAML\Module\oauth\Consumer($this->key, $this->secret);
// Get the request token
$requestToken = $consumer->getRequestToken(
'https://api.linkedin.com/uas/oauth/requestToken',
[
'oauth_callback' => \SimpleSAML\Module::getModuleUrl('authlinkedin').'/linkback.php?stateid='.$stateID
]
);
\SimpleSAML\Logger::debug(
"Got a request token from the OAuth service provider [".
$requestToken->key."] with the secret [".$requestToken->secret."]"
);
$state['authlinkedin:requestToken'] = $requestToken;
// Update the state
\SimpleSAML\Auth\State::saveState($state, self::STAGE_INIT);
// Authorize the request token
$consumer->getAuthorizeRequest('https://www.linkedin.com/uas/oauth/authenticate', $requestToken);
}
/**
* @param array &$state
* @return void
*/
public function finalStep(&$state)
{
$requestToken = $state['authlinkedin:requestToken'];
$consumer = new \SimpleSAML\Module\oauth\Consumer($this->key, $this->secret);
\SimpleSAML\Logger::debug(
"oauth: Using this request token [".
$requestToken->key."] with the secret [".$requestToken->secret."]"
);
// Replace the request token with an access token (via GET method)
$accessToken = $consumer->getAccessToken(
'https://api.linkedin.com/uas/oauth/accessToken',
$requestToken,
['oauth_verifier' => $state['authlinkedin:oauth_verifier']]
);
\SimpleSAML\Logger::debug(
"Got an access token from the OAuth service provider [".
$accessToken->key."] with the secret [".$accessToken->secret."]"
);
$userdata = $consumer->getUserInfo(
'https://api.linkedin.com/v1/people/~:('.$this->attributes.')',
$accessToken,
['http' => ['header' => 'x-li-format: json']]
);
$attributes = $this->flatten($userdata, 'linkedin.');
// TODO: pass accessToken: key, secret + expiry as attributes?
if (array_key_exists('id', $userdata)) {
$attributes['linkedin_targetedID'] = ['http://linkedin.com!'.$userdata['id']];
$attributes['linkedin_user'] = [$userdata['id'].'@linkedin.com'];
}
\SimpleSAML\Logger::debug('LinkedIn Returned Attributes: '.implode(", ", array_keys($attributes)));
$state['Attributes'] = $attributes;
}
/**
* takes an associative array, traverses it and returns the keys concatenated with a dot
*
* e.g.:
*
* [
* 'linkedin' => [
* 'location' => [
* 'id' => '123456'
* 'country' => [
* 'code' => 'de'
* ]
* ]
* ]
*
* become:
*
* [
* 'linkedin.location.id' => [0 => '123456'],
* 'linkedin.location.country.code' => [0 => 'de']
* ]
*
* @param array $array
* @param string $prefix
*
* @return array the array with the new concatenated keys
*/
protected function flatten($array, $prefix = '')
{
$result = [];
foreach ($array as $key => $value) {
if (is_array($value)) {
$result = $result + $this->flatten($value, $prefix.$key.'.');
} else {
$result[$prefix.$key] = [$value];
}
}
return $result;
}
}
<?php
/**
* Handle linkback() response from LinkedIn.
*/
if (!array_key_exists('stateid', $_REQUEST)) {
throw new \Exception('Lost OAuth Client State');
}
$state = \SimpleSAML\Auth\State::loadState(
$_REQUEST['stateid'],
\SimpleSAML\Module\authlinkedin\Auth\Source\LinkedIn::STAGE_INIT
);
// http://developer.linkedin.com/docs/DOC-1008#2_Redirect_the_User_to_our_Authorization_Server
if (array_key_exists('oauth_verifier', $_REQUEST)) {
$state['authlinkedin:oauth_verifier'] = $_REQUEST['oauth_verifier'];
} else {
throw new Exception('OAuth verifier not returned.');
}
// Find authentication source
assert(array_key_exists(\SimpleSAML\Module\authlinkedin\Auth\Source\LinkedIn::AUTHID, $state));
$sourceId = $state[\SimpleSAML\Module\authlinkedin\Auth\Source\LinkedIn::AUTHID];
/** @var \SimpleSAML\Module\authlinkedin\Auth\Source\LinkedIn|null $source */
$source = \SimpleSAML\Auth\Source::getById($sourceId);
if ($source === null) {
throw new \Exception('Could not find authentication source with id '.$sourceId);
}
$source->finalStep($state);
\SimpleSAML\Auth\Source::completeAuth($state);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment