New authentication source: authlinkedin

Thanks to Brook Schofield for implementing this.

git-svn-id: http://simplesamlphp.googlecode.com/svn/trunk@2736 44740490-163a-0410-bde0-09ae8108e29a
This commit is contained in:
olavmrk 2011-02-08 13:51:32 +00:00
parent e239600e03
commit d642624543
6 changed files with 215 additions and 0 deletions

View File

@ -0,0 +1,19 @@
<?php
$attributemap = array(
// See http://developer.linkedin.com/docs/DOC-1061 for LinkedIn Profile fields.
// NB: JSON response requires the conversion of field names from hyphened to camelCase.
// For instance, first-name becomes firstName.
// Generated LinkedIn Attributes
'linkedin_user' => 'eduPersonPrincipalName', // id @ linkedin.com
'linkedin_targetedID' => 'eduPersonTargetedID', // http://linkedin.com!id
// Attributes Returned by LinkedIn
'linkedin.firstName' => 'givenName',
'linkedin.lastName' => 'sn',
'linkedin.id' => 'uid', // alpha + mixed case user id
//'linkedin.pictureUrl' => 'jpegPhoto', // URL not image data
'linkedin.headline' => 'title',
'linkedin.summary' => 'description',
);

View File

@ -134,6 +134,17 @@ $config = array(
),
*/
/*
// LinkedIn OAuth Authentication API.
// Register your application to get an API key here:
// https://www.linkedin.com/secure/developer
'linkedin' => array(
'authlinkedin:LinkedIn',
'key' => 'xxxxxxxxxxxxxxxx',
'secret' => 'xxxxxxxxxxxxxxxx',
),
*/
/*
// Twitter OAuth Authentication API.
// Register your application to get an API key here:

View File

@ -0,0 +1,3 @@
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.

View File

@ -0,0 +1,26 @@
Using the LinkedIn authentication source with simpleSAMLphp
===========================================================
Remember to configure `authsources.php`, with both Consumer key and secret.
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.

View File

@ -0,0 +1,122 @@
<?php
require_once(dirname(dirname(dirname(dirname(dirname(__FILE__))))) . '/oauth/libextinc/OAuth.php');
/**
* Authenticate using LinkedIn.
*
* @author Brook Schofield, TERENA.
* @package simpleSAMLphp
* @version $Id$
*/
class sspmod_authlinkedin_Auth_Source_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';
private $key;
private $secret;
/**
* Constructor for this authentication source.
*
* @param array $info Information about this authentication source.
* @param array $config Configuration.
*/
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'];
}
/**
* Log-in using LinkedIn platform
* Documentation at: http://developer.linkedin.com/docs/DOC-1008
*
* @param array &$state Information about the current authentication.
*/
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 sspmod_oauth_Consumer($this->key, $this->secret);
// Get the request token
$requestToken = $consumer->getRequestToken('https://api.linkedin.com/uas/oauth/requestToken', array('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);
}
public function finalStep(&$state) {
$requestToken = $state['authlinkedin:requestToken'];
$consumer = new sspmod_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,
array('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 . "]");
// TODO: configure attributes (http://developer.linkedin.com/docs/DOC-1061) from config? Limited options via LinkedIn.
$userdata = $consumer->getUserInfo('https://api.linkedin.com/v1/people/~:(id,first-name,last-name,headline,summary,specialties,picture-url)', $accessToken, array('http' => array('header' => 'x-li-format: json')));
$attributes = array();
foreach($userdata AS $key => $value) {
if (is_string($value))
$attributes['linkedin.' . $key] = array((string)$value);
}
// TODO: pass accessToken: key, secret + expiry as attributes?
if (array_key_exists('id', $userdata) ) {
$attributes['linkedin_targetedID'] = array('http://linkedin.com!' . $userdata['id']);
$attributes['linkedin_user'] = array($userdata['id'] . '@linkedin.com');
}
SimpleSAML_Logger::debug('LinkedIn Returned Attributes: '. implode(", ",array_keys($attributes)));
$state['Attributes'] = $attributes;
}
}

View File

@ -0,0 +1,34 @@
<?php
/**
* Handle linkback() response from LinkedIn.
*/
if (array_key_exists('stateid', $_REQUEST)) {
$stateId = $_REQUEST['stateid'];
} else {
throw new Exception('Lost OAuth Client State');
}
$state = SimpleSAML_Auth_State::loadState($stateId, sspmod_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(sspmod_authlinkedin_Auth_Source_LinkedIn::AUTHID, $state)');
$sourceId = $state[sspmod_authlinkedin_Auth_Source_LinkedIn::AUTHID];
$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);