diff --git a/login-google.php b/login-google.php index c2a36ea..01f785b 100644 --- a/login-google.php +++ b/login-google.php @@ -12,10 +12,14 @@ define('CLIENT_ID', get_option('wpoa_google_api_id')); define('CLIENT_SECRET', get_option('wpoa_google_api_secret')); define('REDIRECT_URI', rtrim(site_url(), '/') . '/'); -define('SCOPE', 'profile'); // PROVIDER SPECIFIC: 'profile' is the minimum scope required to get the user's id from Google define('URL_AUTH', "https://accounts.google.com/o/oauth2/auth?"); -define('URL_TOKEN', "https://accounts.google.com/o/oauth2/token?"); +define('URL_TOKEN', "https://accounts.google.com/o/oauth2/token"); define('URL_USER', "https://www.googleapis.com/plus/v1/people/me?"); +// PROVIDER SPECIFIC: profile minimum and emails for matching users +if(get_option('wpoa_email_linking')) + define('SCOPE', 'https://www.googleapis.com/auth/plus.profile.emails.read'); +else + define('SCOPE', 'profile'); # END OF DEFINE THE OAUTH PROVIDER AND SETTINGS TO USE # // remember the user's last url so we can redirect them back to there after the login ends: @@ -75,6 +79,12 @@ # END OF AUTHENTICATION FLOW # # AUTHENTICATION FLOW HELPER FUNCTIONS # + +/** + * Gets the oauth code. + * + * @param WPOA $wpoa + */ function get_oauth_code($wpoa) { $params = array( 'response_type' => 'code', @@ -89,6 +99,12 @@ function get_oauth_code($wpoa) { exit; } +/** + * Gets the oauth token. + * + * @param WPOA $wpoa + * @return boolean + */ function get_oauth_token($wpoa) { $params = array( 'grant_type' => 'authorization_code', @@ -100,7 +116,7 @@ function get_oauth_token($wpoa) { $url_params = http_build_query($params); switch (strtolower(HTTP_UTIL)) { case 'curl': - $url = URL_TOKEN . $url_params; + $url = URL_TOKEN; $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); @@ -146,6 +162,12 @@ function get_oauth_token($wpoa) { } } +/** + * Gets the oauth identity. + * + * @param WPOA $wpoa + * @return array + */ function get_oauth_identity($wpoa) { // here we exchange the access token for the user info... // set the access token param: @@ -156,12 +178,14 @@ function get_oauth_identity($wpoa) { // perform the http request: switch (strtolower(HTTP_UTIL)) { case 'curl': - $url = URL_USER . $url_params; // TODO: we probably want to send this using a curl_setopt... + $url = URL_USER . $url_params; $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); - // PROVIDER NORMALIZATION: Github/Reddit require a User-Agent here... - // PROVIDER NORMALIZATION: PayPal/Reddit require that we send the access token via a bearer header, PayPal also requires a Content-Type: application/json header, LinkedIn requires an x-li-format: json header... curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + // curl_setopt($curl, CURLOPT_POST, 1); + // curl_setopt($curl, CURLOPT_POSTFIELDS, $params); + // curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, (get_option('wpoa_http_util_verify_ssl') == 1 ? 1 : 0)); + // curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, (get_option('wpoa_http_util_verify_ssl') == 1 ? 2 : 0)); $result = curl_exec($curl); $result_obj = json_decode($result, true); break; @@ -185,8 +209,10 @@ function get_oauth_identity($wpoa) { // parse and return the user's oauth identity: $oauth_identity = array(); $oauth_identity['provider'] = $_SESSION['WPOA']['PROVIDER']; - $oauth_identity['id'] = $result_obj['id']; // PROVIDER SPECIFIC: Google returns the user's OAuth identity as id - //$oauth_identity['email'] = $result_obj['emails'][0]['value']; // PROVIDER SPECIFIC: Google returns an array of email addresses. To respect privacy we currently don't collect the user's email address. + // PROVIDER SPECIFIC: Google returns the user's OAuth identity as id + $oauth_identity['id'] = $result_obj['id']; + // PROVIDER SPECIFIC: Google returns an array of email addresses. To respect privacy we currently only collect if the registration setting is enabled. + $oauth_identity['email'] = (isset($result_obj['emails'])) ? $result_obj['emails'][0]['value'] : ''; if (!$oauth_identity['id']) { $wpoa->wpoa_end_login("Sorry, we couldn't log you in. User identity was not found. Please notify the admin or try again later."); } diff --git a/login-office365.php b/login-office365.php new file mode 100644 index 0000000..ad93490 --- /dev/null +++ b/login-office365.php @@ -0,0 +1,218 @@ +wpoa_end_login("This third-party authentication provider has not been enabled. Please notify the admin or try again later."); +} +/* Do not proceed if id or secret is null */ +elseif (!CLIENT_ID || !CLIENT_SECRET) { + $this->wpoa_end_login("This third-party authentication provider has not been configured with an API key/secret. Please notify the admin or try again later."); +} +/* Do not proceed if an error was detected */ +elseif (isset($_GET['error_description'])) { + $this->wpoa_end_login($_GET['error_description']); +} +/* Do not proceed if an error was detected */ +elseif (isset($_GET['error_message'])) { + $this->wpoa_end_login($_GET['error_message']); +} +/* POST-auth phase, verify the state */ +elseif (isset($_GET['code'])) { + if ($_SESSION['WPOA']['STATE'] == $_GET['state']) { + // get an access token from the third party provider: + get_oauth_token($this); + // get the user's third-party identity and attempt to login/register a matching wordpress user account: + $oauth_identity = get_oauth_identity($this); + $this->wpoa_login_user($oauth_identity); + } + /* Possible CSRF attack, end the login with a generic message + to the user and a detailed message to the admin/logs in case of abuse */ + else { + // TODO: report detailed message to admin/logs here... + $this->wpoa_end_login("Sorry, we couldn't log you in. Please notify the admin or try again later."); + } +} +/* PRE-auth, start the auth process */ +else { + if ((empty($_SESSION['WPOA']['EXPIRES_AT'])) || (time() > $_SESSION['WPOA']['EXPIRES_AT'])) { + // expired token; clear the state: + $this->wpoa_clear_login_state(); + } + get_oauth_code($this); +} + +/* NOTE: If we reach here something went wrong and was not accounted for */ +$this->wpoa_end_login("Sorry, we couldn't log you in. The authentication flow terminated in an unexpected way. Please notify the admin or try again later."); +### END OF AUTHENTICATION FLOW ### + +### AUTHENTICATION FLOW HELPER FUNCTIONS ### +function get_oauth_code($wpoa) { + $params = array( + 'client_id' => CLIENT_ID, + 'redirect_uri' => REDIRECT_URI, + 'scope' => SCOPE, + 'response_type' => 'code', + 'response_mode' => 'query', + 'state' => uniqid('', true), + ); + $_SESSION['WPOA']['STATE'] = $params['state']; + $url = URL_AUTH . http_build_query($params); + header("Location: $url"); + exit; + #TODO: Add Error handling - https://azure.microsoft.com/en-us/documentation/articles/active-directory-protocols-oauth-code/#_error-response +} + +/** + * Gets the oauth token. + * + * @param WPOA $wpoa + * @return array + */ +function get_oauth_token($wpoa) { + $params = array( + 'client_id' => CLIENT_ID, + 'client_secret' => CLIENT_SECRET, + 'code' => $_GET['code'], + 'redirect_uri' => REDIRECT_URI, + 'grant_type' => 'authorization_code', + ); + $url_params = http_build_query($params); + switch (strtolower(HTTP_UTIL)) { + case 'curl': + $url = URL_TOKEN; + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl, CURLOPT_POST, 1); + curl_setopt($curl, CURLOPT_POSTFIELDS, $url_params); // TODO: for Google we use $params... + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, (get_option('wpoa_http_util_verify_ssl') == 1 ? 1 : 0)); + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, (get_option('wpoa_http_util_verify_ssl') == 1 ? 2 : 0)); + $result = curl_exec($curl); + break; + case 'stream-context': + $url = rtrim(URL_TOKEN, "?"); + $opts = array('http' => + array( + 'method' => 'POST', + 'header' => 'Content-type: application/x-www-form-urlencoded', + 'content' => $url_params, + ) + ); + $context = $context = stream_context_create($opts); + $result = @file_get_contents($url, false, $context); + if ($result === false) { + $wpoa->wpoa_end_login("Sorry, we couldn't log you in. Could not retrieve access token via stream context. Please notify the admin or try again later."); + } + break; + } + # PROVIDER SPECIFIC: Outlook REST encodes the access token result as json by default + $result_obj = json_decode($result, true); + # PROVIDER SPECIFIC: this is how Outlook REST returns the access token KEEP THIS PROTECTED! + $access_token = isset($result_obj['access_token']) ? $result_obj['access_token'] : ''; + # PROVIDER SPECIFIC: this is how Outlook REST returns the access token's expiration + $expires_in = isset($result_obj['expires_in']) ? $result_obj['expires_in'] : ''; + $expires_at = time() + $expires_in; + # PROVIDER SPECIFIC: this is how Outlook REST returns the users data (keep for email matching) + $id_token = isset($result_obj['id_token']) ? $result_obj['id_token'] : ''; + # Handle the result: + if (!$access_token || !$expires_in) { + // malformed access token result detected: + $wpoa->wpoa_end_login("Sorry, we couldn't log you in. Malformed access token result detected. Please notify the admin or try again later."); + } else { + $_SESSION['WPOA']['ACCESS_TOKEN'] = $access_token; + $_SESSION['WPOA']['EXPIRES_IN'] = $expires_in; + $_SESSION['WPOA']['EXPIRES_AT'] = $expires_at; + $_SESSION['WPOA']['ID_TOKEN'] = $id_token; + return true; + } +} + +/** + * Gets the oauth identity. + * + * @param WPOA $wpoa + * @return array + */ +function get_oauth_identity($wpoa) { + // here we exchange the access token for the user info... + // set the access token param: + $params = array( + 'access_token' => $_SESSION['WPOA']['ACCESS_TOKEN'], // PROVIDER SPECIFIC: the access token is passed to Outlook REST using this key name + ); + $url_params = http_build_query($params); + // perform the http request: + switch (strtolower(HTTP_UTIL)) { + case 'curl': + $url = rtrim(URL_USER, "?"); + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // TODO: does Outlook REST require this? + curl_setopt($curl, CURLOPT_HTTPHEADER, array("Authorization: bearer " . $_SESSION['WPOA']['ACCESS_TOKEN'])); // PROVIDER SPECIFIC: do we have to do this for Outlook REST? + //curl_setopt($curl, CURLOPT_HTTPHEADER, array('x-li-format: json')); // PROVIDER SPECIFIC: I think this is only for LinkedIn... + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + $result = curl_exec($curl); + $result_obj = json_decode($result, true); + break; + case 'stream-context': + $url = rtrim(URL_USER, "?"); + $opts = array('http' => + array( + 'method' => 'GET', + // PROVIDER NORMALIZATION: Reddit/Github requires User-Agent here... + 'header' => "Authorization: Bearer " . $_SESSION['WPOA']['ACCESS_TOKEN'] . "\r\n" . "x-li-format: json\r\n", // PROVIDER SPECIFIC: i think only LinkedIn uses x-li-format... + ) + ); + $context = $context = stream_context_create($opts); + $result = @file_get_contents($url, false, $context); + if ($result === false) { + $wpoa->wpoa_end_login("Sorry, we couldn't log you in. Could not retrieve user identity via stream context. Please notify the admin or try again later."); + } + $result_obj = json_decode($result, true); + break; + } + // parse and return the user's oauth identity: + $oauth_identity = array(); + $oauth_identity['provider'] = (isset($_SESSION['WPOA']['PROVIDER'])) ? $_SESSION['WPOA']['PROVIDER'] : ''; + // PROVIDER SPECIFIC: Outlook REST returns the user's unique id + $oauth_identity['id'] = (isset($result_obj['Id'])) ? $result_obj['Id'] : ''; + // PROVIDER SPECIFIC: Outlook REST returns the user's email address + $oauth_identity['email'] = (isset($result_obj['EmailAddress'])) ? $result_obj['EmailAddress'] : ''; + if (!$oauth_identity['id']) { + $wpoa->wpoa_end_login("Sorry, we couldn't log you in. User identity was not found. Please notify the admin or try again later."); + } + return $oauth_identity; +} +### END OF AUTHENTICATION FLOW HELPER FUNCTIONS ### + +?> \ No newline at end of file diff --git a/wp-oauth-settings.php b/wp-oauth-settings.php index 0e34fa3..b58a470 100644 --- a/wp-oauth-settings.php +++ b/wp-oauth-settings.php @@ -473,6 +473,14 @@ function wpoa_cc_ux() {
+ +Enabled: | ++ /> + | +
---|---|
Client ID: | ++ ' /> + | +
Client Secret: | ++ ' /> + | +
Tenant: | ++ ' /> + | + +
+ Instructions: +