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

Externalize authwindowslive (#1061)

parent f158e9c5
No related branches found
No related tags found
No related merge requests found
...@@ -47,6 +47,7 @@ php "$TARGET/composer.phar" config version "$VERSION" -d "$TARGET" ...@@ -47,6 +47,7 @@ php "$TARGET/composer.phar" config version "$VERSION" -d "$TARGET"
php "$TARGET/composer.phar" install --no-dev --prefer-dist -o -d "$TARGET" php "$TARGET/composer.phar" install --no-dev --prefer-dist -o -d "$TARGET"
# Install external modules # Install external modules
php "$TARGET/composer.phar" require --update-no-dev simplesamlphp/simplesamlphp-module-authwindowslive
php "$TARGET/composer.phar" require --update-no-dev simplesamlphp/simplesamlphp-module-cdc php "$TARGET/composer.phar" require --update-no-dev simplesamlphp/simplesamlphp-module-cdc
php "$TARGET/composer.phar" require --update-no-dev simplesamlphp/simplesamlphp-module-memcookie php "$TARGET/composer.phar" require --update-no-dev simplesamlphp/simplesamlphp-module-memcookie
php "$TARGET/composer.phar" require --update-no-dev simplesamlphp/simplesamlphp-module-oauth php "$TARGET/composer.phar" require --update-no-dev simplesamlphp/simplesamlphp-module-oauth
......
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 Windows Live ID authentication source with SimpleSAMLphp
==================================================================
This module works around the limitation in Microsoft Online/Azure OIDC implementation of not supplying the OIDC userinfo endpoint.
Microsoft explains the omission by suggesting that the Graph API can produce anything userinfo would have brought, in place.
Remember to configure `authsources.php`, with both your Client ID and Secret key.
To get an API key and a secret, register the application at:
* <https://msdn.microsoft.com/en-us/library/ff751474.aspx>
* <https://apps.dev.microsoft.com/>
## Testing authentication
On the SimpleSAMLphp frontpage, go to the *Authentication* tab, and use the link:
* *Test configured authentication sources*
Then choose the *windowsliveid* authentication source.
Expected behaviour would then be that you are sent to Windows Live ID and asked to login.
<?php
namespace SimpleSAML\Module\authwindowslive\Auth\Source;
/**
* Authenticate using LiveID.
*
* @author Brook Schofield, TERENA.
* @author Guy Halse, TENET.
* @package SimpleSAMLphp
*/
class LiveID extends \SimpleSAML\Auth\Source
{
/**
* 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';
/** @var string */
private $key;
/** @var string */
private $secret;
/**
* Constructor for this authentication source.
*
* @param array $info Information about this authentication source.
* @param array $config Configuration.
*
* @throws \Exception In case of misconfiguration.
*/
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.
* @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::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);
}
/**
* @param array &$state
* @return void
* @throws \Exception
*/
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 = [
'http' => [
'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'])
);
}
\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 = [
'http' => ['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);
// 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 = [];
$attributes['windowslive_targetedID'] = [
'https://graph.microsoft.com!'.(!empty($userdata['id']) ? $userdata['id'] : 'unknown')
];
foreach ($userdata as $key => $value) {
if (is_string($value)) {
$attributes['windowslive.'.$key] = [(string) $value];
}
}
\SimpleSAML\Logger::debug('LiveID Returned Attributes: '.implode(", ", array_keys($attributes)));
$state['Attributes'] = $attributes;
}
}
<?php
/**
* Handle linkback() response from Windows Live ID.
*/
if (!array_key_exists('state', $_REQUEST)) {
throw new \Exception('Lost OAuth Client State');
}
$state = \SimpleSAML\Auth\State::loadState(
$_REQUEST['state'],
\SimpleSAML\Module\authwindowslive\Auth\Source\LiveID::STAGE_INIT
);
// http://msdn.microsoft.com/en-us/library/ff749771.aspx
if (array_key_exists('code', $_REQUEST)) {
// good
$state['authwindowslive:verification_code'] = $_REQUEST['code'];
if (array_key_exists('exp', $_REQUEST)) {
$state['authwindowslive:exp'] = $_REQUEST['exp'];
}
} else {
// 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
assert(array_key_exists(\SimpleSAML\Module\authwindowslive\Auth\Source\LiveID::AUTHID, $state));
$sourceId = $state[\SimpleSAML\Module\authwindowslive\Auth\Source\LiveID::AUTHID];
/** @var \SimpleSAML\Module\authwindowslive\Auth\Source\LiveID|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