Skip to content
Snippets Groups Projects
Commit 923ad7d5 authored by Jaime Pérez Crespo's avatar Jaime Pérez Crespo Committed by GitHub
Browse files

Merge pull request #398 from ghalse/update-authwindowslive

Update the authwindowslive:LiveID module to use OAuth 2.0 authorization
parents b2bfd47d f7cdf2f0
No related branches found
No related tags found
No related merge requests found
......@@ -10,5 +10,13 @@ $attributemap = array(
'windowslive.FirstName' => 'givenName',
'windowslive.LastName' => 'sn',
'windowslive.Location' => 'l',
// Attributes returned by Microsoft Graph - http://graph.microsoft.io/en-us/docs/api-reference/v1.0/resources/user
'windowslive.givenName' => 'givenName',
'windowslive.surname' => 'sn',
'windowslive.displayName' => 'displayName',
'windowslive.id' => 'uid',
'windowslive.userPrincipalName' => 'eduPersonPrincipalName',
'windowslive.mail' => 'mail',
'windowslive.preferredLanguage' => 'preferredLanguage',
);
......@@ -242,9 +242,9 @@ $config = array(
*/
/*
// Windows Live ID Authentication API.
// Microsoft Account (Windows Live ID) Authentication API.
// Register your application to get an API key here:
// https://manage.dev.live.com
// https://apps.dev.microsoft.com/
'windowslive' => array(
'authwindowslive:LiveID',
'key' => 'xxxxxxxxxxxxxxxx',
......
......@@ -8,132 +8,135 @@
*/
class sspmod_authwindowslive_Auth_Source_LiveID extends SimpleSAML_Auth_Source {
/**
* The string used to identify our states.
*/
const STAGE_INIT = 'authwindowslive:init';
/**
* The string used to identify our states.
*/
const STAGE_INIT = 'authwindowslive:init';
/**
* The key of the AuthId field in the state.
*/
const AUTHID = 'authwindowslive:AuthId';
/**
* The key of the AuthId field in the state.
*/
const AUTHID = 'authwindowslive:AuthId';
private $key;
private $secret;
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)');
/**
* 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('LiveID authentication source is not properly configured: missing [key]');
}
$this->key = $config['key'];
if (!array_key_exists('secret', $config)) {
throw new Exception('LiveID authentication source is not properly configured: missing [secret]');
}
$this->secret = $config['secret'];
}
/**
* Log-in using LiveID platform
*
* @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::saveState($state, self::STAGE_INIT);
SimpleSAML\Logger::debug('authwindowslive auth state id = ' . $stateID);
// Authenticate the user
// Documentation at: https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-protocols-oauth-code/
$authorizeURL = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize'
. '?client_id=' . $this->key
. '&response_type=code'
. '&response_mode=query'
. '&redirect_uri=' . urlencode(SimpleSAML_Module::getModuleUrl('authwindowslive') . '/linkback.php')
. '&state=' . urlencode($stateID)
. '&scope=' . urlencode('openid https://graph.microsoft.com/user.read')
;
\SimpleSAML\Utils\HTTP::redirectTrustedURL($authorizeURL);
}
public function finalStep(&$state) {
SimpleSAML\Logger::debug("authwindowslive oauth: Using this verification code [" .
$state['authwindowslive:verification_code'] . "]");
// Retrieve Access Token
// Documentation at: https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-protocols-oauth-code/#request-an-access-token
$postData = 'client_id=' . urlencode($this->key)
. '&client_secret=' . urlencode($this->secret)
. '&scope=' . urlencode('https://graph.microsoft.com/user.read')
. '&grant_type=authorization_code'
. '&redirect_uri=' . urlencode(SimpleSAML_Module::getModuleUrl('authwindowslive') . '/linkback.php')
. '&code=' . urlencode($state['authwindowslive:verification_code']);
$context = array(
'http' => array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postData,
),
);
$result = \SimpleSAML\Utils\HTTP::fetch('https://login.microsoftonline.com/common/oauth2/v2.0/token', $context);
$response = json_decode($result,true);
// error checking of $response to make sure we can proceed
if (!array_key_exists('access_token',$response)) {
throw new Exception('[' . $response['error'] . '] ' . $response['error_description'] .
"\r\nNo access_token returned - cannot proceed\r\n" . implode(', ', $response['error_codes']));
}
// Call the parent constructor first, as required by the interface
parent::__construct($info, $config);
SimpleSAML\Logger::debug("authwindowslive: Got an access token from the OAuth service provider [" .
$response['access_token'] . "]");
// Documentation at: http://graph.microsoft.io/en-us/docs/overview/call_api
$opts = array('http' => array('header' => "Accept: application/json\r\nAuthorization: Bearer " .
$response['access_token'] . "\r\n"));
$data = \SimpleSAML\Utils\HTTP::fetch('https://graph.microsoft.com/v1.0/me',$opts);
$userdata = json_decode($data, TRUE);
if (!array_key_exists('key', $config))
throw new Exception('LiveID authentication source is not properly configured: missing [key]');
// This is the simplest case
if(!array_key_exists('@odata.context',$userdata) || array_key_exists('error',$userdata)) {
throw new Exception('Unable to retrieve userdata from Microsoft Graph [' . $userdata['error']['code'] . '] ' . $userdata['error']['message']);
}
$attributes = array();
$attributes['windowslive_targetedID'] = array('https://graph.microsoft.com!' . (!empty($userdata['id']) ? $userdata['id'] : 'unknown'));
foreach($userdata as $key => $value) {
if (is_string($value)) {
$attributes['windowslive.' . $key] = array((string)$value);
}
}
$this->key = $config['key'];
if (!array_key_exists('secret', $config))
throw new Exception('LiveID authentication source is not properly configured: missing [secret]');
SimpleSAML\Logger::debug('LiveID Returned Attributes: '. implode(", ",array_keys($attributes)));
$this->secret = $config['secret'];
}
/**
* Log-in using LiveID platform
*
* @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::saveState($state, self::STAGE_INIT);
SimpleSAML\Logger::debug('authwindowslive auth state id = ' . $stateID);
// Authenticate the user
// Documentation at: http://msdn.microsoft.com/en-us/library/ff749771.aspx
$authorizeURL = 'https://consent.live.com/Connect.aspx'
. '?wrap_client_id=' . $this->key
. '&wrap_callback=' . urlencode(SimpleSAML\Module::getModuleUrl('authwindowslive') . '/linkback.php')
. '&wrap_client_state=' . urlencode($stateID)
. '&wrap_scope=WL_Profiles.View,Messenger.SignIn'
;
\SimpleSAML\Utils\HTTP::redirectTrustedURL($authorizeURL);
}
public function finalStep(&$state) {
SimpleSAML\Logger::debug("oauth wrap: Using this verification code [" .
$state['authwindowslive:wrap_verification_code'] . "]");
// Retrieve Access Token
// Documentation at: http://msdn.microsoft.com/en-us/library/ff749686.aspx
$postData = 'wrap_client_id=' . urlencode($this->key)
. '&wrap_client_secret=' . urlencode($this->secret)
. '&wrap_callback=' . urlencode(SimpleSAML\Module::getModuleUrl('authwindowslive') . '/linkback.php')
. '&wrap_verification_code=' . urlencode($state['authwindowslive:wrap_verification_code']);
$context = array(
'http' => array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postData,
),
);
$result = \SimpleSAML\Utils\HTTP::fetch('https://consent.live.com/AccessToken.aspx', $context);
parse_str($result, $response);
// error checking of $response to make sure we can proceed
if (!array_key_exists('wrap_access_token',$response))
throw new Exception('[' . $response['error_code'] . '] ' . $response['wrap_error_reason'] .
"\r\nNo wrap_access_token returned - cannot proceed\r\n" . $response['internal_info']);
SimpleSAML\Logger::debug("Got an access token from the OAuth WRAP service provider [" .
$response['wrap_access_token'] . "] for user [" . $response['uid'] . "]");
// Documentation at: http://msdn.microsoft.com/en-us/library/ff751708.aspx
$opts = array('http' => array('header' => "Accept: application/json\r\nAuthorization: WRAP access_token=" .
$response['wrap_access_token'] . "\r\n"));
$data = \SimpleSAML\Utils\HTTP::fetch('https://apis.live.net/V4.1/cid-'. $response['uid'] . '/Profiles',$opts);
$userdata = json_decode($data, TRUE);
$attributes = array();
$attributes['windowslive_uid'] = array($response['uid']);
$attributes['windowslive_targetedID'] = array('http://windowslive.com!' . $response['uid']);
$attributes['windowslive_user'] = array($response['uid'] . '@windowslive.com');
if (array_key_exists('Entries',$userdata)) {
foreach($userdata['Entries'][0] AS $key => $value) {
if (is_string($value))
$attributes['windowslive.' . $key] = array((string)$value);
}
if (array_key_exists('Emails', $userdata['Entries'][0]))
$attributes['windowslive_mail'] = array($userdata['Entries'][0]['Emails'][0]['Address']);
}
SimpleSAML\Logger::debug('LiveID Returned Attributes: '. implode(", ",array_keys($attributes)));
$state['Attributes'] = $attributes;
}
$state['Attributes'] = $attributes;
}
}
......@@ -4,30 +4,34 @@
* Handle linkback() response from Windows Live ID.
*/
if (!array_key_exists('wrap_client_state', $_REQUEST)) {
throw new Exception('Lost OAuth-WRAP Client State');
if (!array_key_exists('state', $_REQUEST)) {
throw new Exception('Lost OAuth Client State');
}
$state = SimpleSAML_Auth_State::loadState($_REQUEST['wrap_client_state'], sspmod_authwindowslive_Auth_Source_LiveID::STAGE_INIT);
$state = SimpleSAML_Auth_State::loadState($_REQUEST['state'], sspmod_authwindowslive_Auth_Source_LiveID::STAGE_INIT);
// http://msdn.microsoft.com/en-us/library/ff749771.aspx
if (array_key_exists('wrap_verification_code', $_REQUEST)) {
if (array_key_exists('code', $_REQUEST)) {
// Good
$state['authwindowslive:wrap_verification_code'] = $_REQUEST['wrap_verification_code'];
// Good
$state['authwindowslive:verification_code'] = $_REQUEST['code'];
if (array_key_exists('exp', $_REQUEST))
$state['authwindowslive:wrap_exp'] = $_REQUEST['exp'];
if (array_key_exists('exp', $_REQUEST)) {
$state['authwindowslive:exp'] = $_REQUEST['exp'];
}
} else {
// wrap_error_reason = 'user_denied' means user chose not to login with LiveID
// redirect them to their original page so they can choose another auth mechanism
if ($_REQUEST['wrap_error_reason'] === 'user_denied') {
$e = new SimpleSAML_Error_UserAborted();
SimpleSAML_Auth_State::throwException($state, $e);
}
// Error
throw new Exception('Authentication failed: [' . $_REQUEST['error_code'] . '] ' . $_REQUEST['wrap_error_reason']);
// In the OAuth WRAP service, error_reason = 'user_denied' means user chose
// not to login with LiveID. It isn't clear that this is still true in the
// newer API, but the parameter name has changed to error. It doesn't hurt
// to preserve support for this, so this is left in as a placeholder.
// redirect them to their original page so they can choose another auth mechanism
if ($_REQUEST['error'] === 'user_denied') {
$e = new SimpleSAML_Error_UserAborted();
SimpleSAML_Auth_State::throwException($state, $e);
}
// Error
throw new Exception('Authentication failed: [' . $_REQUEST['error'] . '] ' . $_REQUEST['error_description']);
}
// Find authentication source
......@@ -36,7 +40,7 @@ $sourceId = $state[sspmod_authwindowslive_Auth_Source_LiveID::AUTHID];
$source = SimpleSAML_Auth_Source::getById($sourceId);
if ($source === NULL) {
throw new Exception('Could not find authentication source with id ' . $sourceId);
throw new Exception('Could not find authentication source with id ' . $sourceId);
}
$source->finalStep($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