From 222075e38e56ac074a09958a4a7d287f15263e5e Mon Sep 17 00:00:00 2001
From: Olav Morken <olav.morken@uninett.no>
Date: Wed, 9 Feb 2011 11:22:27 +0000
Subject: [PATCH] New authentication source: authwindowslive

Thanks to Brook Schofield for implementing this.

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@2737 44740490-163a-0410-bde0-09ae8108e29a
---
 attributemap/windowslive2name.php             |  16 ++
 modules/authwindowslive/default-disable       |   3 +
 .../authwindowslive/docs/windowsliveid.txt    |  20 +++
 .../lib/Auth/Source/LiveID.php                | 140 ++++++++++++++++++
 modules/authwindowslive/www/linkback.php      |  47 ++++++
 5 files changed, 226 insertions(+)
 create mode 100644 attributemap/windowslive2name.php
 create mode 100644 modules/authwindowslive/default-disable
 create mode 100644 modules/authwindowslive/docs/windowsliveid.txt
 create mode 100644 modules/authwindowslive/lib/Auth/Source/LiveID.php
 create mode 100644 modules/authwindowslive/www/linkback.php

diff --git a/attributemap/windowslive2name.php b/attributemap/windowslive2name.php
new file mode 100644
index 000000000..f43d0f99a
--- /dev/null
+++ b/attributemap/windowslive2name.php
@@ -0,0 +1,16 @@
+<?php
+$attributemap = array(
+
+	// Generated Windows Live ID Attributes
+	'windowslive_user'		=>	'eduPersonPrincipalName', // uid @ windowslive.com
+	'windowslive_targetedID'	=>	'eduPersonTargetedID', // http://windowslive.com!uid
+	'windowslive_uid'		=>	'uid', // windows live id
+	'windowslive_mail'		=>	'mail',
+
+	// Attributes Returned by Windows Live ID
+	'windowslive.FirstName'		=>	'givenName',
+	'windowslive.LastName'		=>	'sn',
+	'windowslive.Location'		=>	'l',
+	//'windowslive.ThumbnailImageLink'=>	'jpegPhoto', // URL not image data
+
+);
diff --git a/modules/authwindowslive/default-disable b/modules/authwindowslive/default-disable
new file mode 100644
index 000000000..fa0bd82e2
--- /dev/null
+++ b/modules/authwindowslive/default-disable
@@ -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.
diff --git a/modules/authwindowslive/docs/windowsliveid.txt b/modules/authwindowslive/docs/windowsliveid.txt
new file mode 100644
index 000000000..014427189
--- /dev/null
+++ b/modules/authwindowslive/docs/windowsliveid.txt
@@ -0,0 +1,20 @@
+Using the Windows Live ID authentication source with simpleSAMLphp
+==================================================================
+
+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:
+
+ * <http://msdn.microsoft.com/en-us/library/ff751474.aspx>
+ * <https://manage.dev.live.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.
+
diff --git a/modules/authwindowslive/lib/Auth/Source/LiveID.php b/modules/authwindowslive/lib/Auth/Source/LiveID.php
new file mode 100644
index 000000000..2dcd532bf
--- /dev/null
+++ b/modules/authwindowslive/lib/Auth/Source/LiveID.php
@@ -0,0 +1,140 @@
+<?php
+
+/**
+ * Authenticate using LiveID.
+ *
+ * @author Brook Schofield, TERENA.
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class sspmod_authwindowslive_Auth_Source_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';
+
+	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('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: 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_Utilities::redirect($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_Utilities::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_Utilities::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;
+	}
+
+}
diff --git a/modules/authwindowslive/www/linkback.php b/modules/authwindowslive/www/linkback.php
new file mode 100644
index 000000000..a8fe872dc
--- /dev/null
+++ b/modules/authwindowslive/www/linkback.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * Handle linkback() response from Windows Live ID.
+ */
+
+if (array_key_exists('wrap_client_state', $_REQUEST)) {
+	$stateId = $_REQUEST['wrap_client_state'];
+	$state = SimpleSAML_Auth_State::loadState($stateId, sspmod_authwindowslive_Auth_Source_LiveID::STAGE_INIT);
+} else {
+	throw new Exception('Lost OAuth-WRAP Client State');
+}
+
+// http://msdn.microsoft.com/en-us/library/ff749771.aspx
+if (array_key_exists('wrap_verification_code', $_REQUEST)) {
+
+	// Good
+	$state['authwindowslive:wrap_verification_code'] = $_REQUEST['wrap_verification_code'];
+
+	if (array_key_exists('exp', $_REQUEST))
+		$state['authwindowslive:wrap_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('User aborted authentication.');
+		SimpleSAML_Auth_State::throwException($state, $e);
+	}
+
+	// Error
+	throw new Exception('Authentication failed: [' . $_REQUEST['error_code'] . '] ' . $_REQUEST['wrap_error_reason']);
+}
+
+/* Find authentication source. */
+assert('array_key_exists(sspmod_authwindowslive_Auth_Source_LiveID::AUTHID, $state)');
+$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);
+}
+
+$source->finalStep($state);
+
+SimpleSAML_Auth_Source::completeAuth($state);
+
-- 
GitLab