From 0fd902eb01582bc9c19fd2903ba60307d86fb72a Mon Sep 17 00:00:00 2001 From: Sonia Zorba Date: Mon, 8 Jul 2019 17:18:53 +0200 Subject: [PATCH] Added ORCID login --- README.md | 2 +- classes/login/FacebookLogin.php | 6 +- classes/login/LinkedInLogin.php | 14 +-- classes/login/OrcidLogin.php | 127 ++++++++++++++++++++++++ classes/model/AuthPageModel.php | 2 +- classes/model/AuthenticationMethods.php | 2 +- classes/model/Identity.php | 3 +- config-example.json | 5 + img/eduGain-200.png | Bin 64295 -> 0 bytes img/edugain-100.png | Bin 0 -> 24247 bytes img/orcid-100.png | Bin 0 -> 28665 bytes include/front-controller.php | 23 ++++- index.php | 7 +- views/confirm-join.php | 49 --------- views/join-success.php | 15 --- views/main-page.php | 17 +++- 16 files changed, 183 insertions(+), 89 deletions(-) create mode 100644 classes/login/OrcidLogin.php delete mode 100755 img/eduGain-200.png create mode 100644 img/edugain-100.png create mode 100644 img/orcid-100.png delete mode 100644 views/confirm-join.php delete mode 100644 views/join-success.php diff --git a/README.md b/README.md index 2c54c98..e5664c0 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Requirements: On Ubuntu: - sudo apt install apache2 mariadb-server libapache2-mod-php mariadb-server php7.2-xml php7.2-mbstring php-mysql + sudo apt install apache2 mariadb-server libapache2-mod-php mariadb-server php7.2-xml php7.2-mbstring php-mysql php-curl ### PHP diff --git a/classes/login/FacebookLogin.php b/classes/login/FacebookLogin.php index 3b3b9e6..f8b8559 100644 --- a/classes/login/FacebookLogin.php +++ b/classes/login/FacebookLogin.php @@ -8,7 +8,7 @@ class FacebookLogin extends LoginHandler { parent::__construct($locator, Identity::FACEBOOK); } - public function login() { + public function login(): string { // Retrieve Facebook configuration $Facebook = $this->locator->config->authenticationMethods->Facebook; @@ -25,10 +25,10 @@ class FacebookLogin extends LoginHandler { $loginUrl = $helper->getLoginUrl($Facebook->callback, $permissions); - header("Location: $loginUrl"); + return $loginUrl; } - public function retrieveToken() { + public function retrieveToken(): string { // Retrieve Facebook configuration $Facebook = $this->locator->config->authenticationMethods->Facebook; diff --git a/classes/login/LinkedInLogin.php b/classes/login/LinkedInLogin.php index 7f686ed..93d3c55 100644 --- a/classes/login/LinkedInLogin.php +++ b/classes/login/LinkedInLogin.php @@ -8,7 +8,7 @@ class LinkedInLogin extends LoginHandler { parent::__construct($locator, Identity::FACEBOOK); } - public function login() { + public function login(): string { // Retrieve LinkedIn configuration $LinkedIn = $this->locator->config->authenticationMethods->LinkedIn; @@ -18,10 +18,10 @@ class LinkedInLogin extends LoginHandler { $url .= "&state=789654123"; $url .= "&scope=r_basicprofile r_emailaddress"; - header("Location: $url"); + return $url; } - public function retrieveToken() { + public function retrieveToken(): string { // Retrieve LinkedIn configuration $LinkedIn = $this->locator->config->authenticationMethods->LinkedIn; @@ -100,10 +100,10 @@ class LinkedInLogin extends LoginHandler { $typedId = $data['id']; return $this->onIdentityDataReceived($typedId, function($identity) use($data) { - $identity->email = $data['emailAddress']; - $identity->name = $data['firstName']; - $identity->surname = $data['lastName']; - }); + $identity->email = $data['emailAddress']; + $identity->name = $data['firstName']; + $identity->surname = $data['lastName']; + }); } else { //show information regarding the error $errorMessage = "Error: LinkedIn server response code: " . $info2['http_code'] . " - "; diff --git a/classes/login/OrcidLogin.php b/classes/login/OrcidLogin.php new file mode 100644 index 0000000..8568a73 --- /dev/null +++ b/classes/login/OrcidLogin.php @@ -0,0 +1,127 @@ +locator->config->authenticationMethods->OrcID; + + $url = "https://orcid.org/oauth/authorize"; + $url = $url . "?client_id=" . $ORCID->id; + $url = $url . "&response_type=code"; + $url = $url . "&scope=/authenticate"; + $url = $url . "&redirect_uri=" . $this->locator->getBasePath() . $ORCID->callback; + + return $url; + } + + public function retrieveToken($code) { + + if ($code === null) { + throw new BadRequestException("Unable to get ORCID client code"); + } + + $token = $this->getAccessTokenFromCode($code); + + $access_token = $token['access_token']; + $expires_in = $token['expires_in']; + $orcid_id = $username = $token['orcid']; + + $orcid_data = $this->getOrcidDataUsingAccessToken($orcid_id, $access_token); + + if (!isset($orcid_data['person']['emails']['email'][0]['email'])) { + throw new \Exception("ORCID didn't return the email"); + } + + return $this->onIdentityDataReceived($orcid_id, function($identity) use($orcid_data) { + $identity->email = $email = $orcid_data['person']['emails']['email'][0]['email']; + $identity->name = $orcid_data['person']['name']['given-names']['value']; + $identity->surname = $orcid_data['person']['name']['family-name']['value']; + $employmentSummary = $orcid_data['activities-summary']['employments']['employment-summary']; + if (count($employmentSummary) > 0) { + $identity->institution = $employmentSummary[0]['organization']['name']; + } + }); + } + + private function getAccessTokenFromCode($code): array { + + $ORCID = $this->locator->config->authenticationMethods->OrcID; + + //create array of data to be posted to get AccessToken + $post_data = array( + 'grant_type' => "authorization_code", + 'code' => $code, + 'redirect_uri' => $this->locator->getBasePath() . $ORCID->callback, + 'client_id' => $ORCID->id, + 'client_secret' => $ORCID->secret + ); + + //traverse array and prepare data for posting (key1=value1) + foreach ($post_data as $key => $value) { + $post_items[] = $key . '=' . $value; + } + + //create the final string to be posted + $post_string = implode('&', $post_items); + + //create cURL connection + $conn = curl_init('https://orcid.org/oauth/token'); + + //set options + curl_setopt($conn, CURLOPT_CONNECTTIMEOUT, 30); + curl_setopt($conn, CURLOPT_RETURNTRANSFER, true); + curl_setopt($conn, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($conn, CURLOPT_FOLLOWLOCATION, 1); + + //set data to be posted + curl_setopt($conn, CURLOPT_POSTFIELDS, $post_string); + + //perform our request + $result = curl_exec($conn); + + if ($result) { + $token = json_decode($result, TRUE); + curl_close($conn); + return $token; + } else { + //show information regarding the error + $errorMessage = curl_errno($conn) . "-"; + $errorMessage = $errorMessage . curl_error($conn); + curl_close($conn); + throw new \Exception($errorMessage); + } + } + + private function getOrcidDataUsingAccessToken(string $orcid_id, string $access_token) { + + // API call + $orcid_url = "https://pub.orcid.org/v2.1/" . $orcid_id . "/record"; + $conn = curl_init(); + curl_setopt($conn, CURLOPT_URL, $orcid_url); + curl_setopt($conn, CURLOPT_HTTPHEADER, array( + 'Authorization: Bearer ' . $access_token, + 'Accept: application/json')); + + curl_setopt($conn, CURLOPT_RETURNTRANSFER, true); + $result = curl_exec($conn); + + if ($result) { + $orcid_data = json_decode($result, TRUE); + curl_close($conn); + return $orcid_data; + } else { + $errorMessage = curl_errno($conn) . "-"; + $errorMessage = $errorMessage . curl_error($conn); + curl_close($conn); + throw new \Exception($errorMessage); + } + } + +} diff --git a/classes/model/AuthPageModel.php b/classes/model/AuthPageModel.php index 22756fa..bf90c89 100644 --- a/classes/model/AuthPageModel.php +++ b/classes/model/AuthPageModel.php @@ -41,7 +41,7 @@ class AuthPageModel { $this->eduGAIN = isset($config->authenticationMethods->eduGAIN) && in_array(AuthenticationMethods::EDU_GAIN, $client->authMethods); - $this->orcid = isset($config->authenticationMethods->Orcid) && + $this->orcid = isset($config->authenticationMethods->OrcID) && in_array(AuthenticationMethods::ORCID, $client->authMethods); $this->x509 = isset($config->authenticationMethods->X509) && diff --git a/classes/model/AuthenticationMethods.php b/classes/model/AuthenticationMethods.php index 9933d59..8b3f29d 100644 --- a/classes/model/AuthenticationMethods.php +++ b/classes/model/AuthenticationMethods.php @@ -5,7 +5,7 @@ namespace RAP; abstract class AuthenticationMethods { const EDU_GAIN = "eduGAIN"; - const ORCID = "Orcid"; + const ORCID = "OrcID"; const X509 = "X.509"; const GOOGLE = "Google"; const LINKED_IN = "LinkedIn"; diff --git a/classes/model/Identity.php b/classes/model/Identity.php index 7710595..e0ba592 100644 --- a/classes/model/Identity.php +++ b/classes/model/Identity.php @@ -34,8 +34,9 @@ class Identity { const GOOGLE = "Google"; const FACEBOOK = "Facebook"; const LINKEDIN = "LinkedIn"; + const ORCID = "OrcID"; - private static $ALLOWED_TYPES = [Identity::EDU_GAIN, Identity::X509, Identity::GOOGLE, Identity::FACEBOOK, Identity::LINKEDIN]; + private static $ALLOWED_TYPES = [Identity::EDU_GAIN, Identity::X509, Identity::GOOGLE, Identity::FACEBOOK, Identity::LINKEDIN, Identity::ORCID]; /** * Identity id in the database. Mandatory field. diff --git a/config-example.json b/config-example.json index 92f9edd..873732f 100644 --- a/config-example.json +++ b/config-example.json @@ -37,6 +37,11 @@ "logo": "img/ia2-logo-60x60.png", "logo_alt": "IA2 logo", "description": "Use the IA2 Logo to Login if you have an account provided by IA2 or self registered" + }, + "OrcID": { + "id": "", + "callback": "/auth/orcid", + "secret": "" } } } \ No newline at end of file diff --git a/img/eduGain-200.png b/img/eduGain-200.png deleted file mode 100755 index 5235aa8fcd435d54d4e4bec1374f6aa151a0c3d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64295 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 E08Jqhc>n+a literal 0 HcmV?d00001 diff --git a/include/front-controller.php b/include/front-controller.php index bd63fbf..2b2ab02 100644 --- a/include/front-controller.php +++ b/include/front-controller.php @@ -154,28 +154,43 @@ Flight::route('/auth/social/facebook', function() { session_start(); global $locator; $facebookLogin = new \RAP\FacebookLogin($locator); - $facebookLogin->login(); + Flight::redirect($facebookLogin->login()); }); Flight::route('/auth/social/facebook/token', function() { session_start(); global $locator; $facebookLogin = new \RAP\FacebookLogin($locator); - $facebookLogin->retrieveToken(); + Flight::redirect($facebookLogin->retrieveToken()); }); Flight::route('/auth/social/linkedIn', function() { session_start(); global $locator; $linkedInLogin = new \RAP\LinkedInLogin($locator); - $linkedInLogin->login(); + Flight::redirect($linkedInLogin->login()); }); Flight::route('/auth/social/linkedIn/token', function() { session_start(); global $locator; $linkedInLogin = new \RAP\LinkedInLogin($locator); - $linkedInLogin->retrieveToken(); + Flight::redirect($linkedInLogin->retrieveToken()); +}); + +Flight::route('/auth/orcid', function() { + session_start(); + global $locator; + $orcidLogin = new \RAP\OrcidLogin($locator); + Flight::redirect($orcidLogin->login()); +}); + +Flight::route('/auth/orcid/token', function() { + session_start(); + global $locator; + $code = filter_input(INPUT_GET, 'code', FILTER_SANITIZE_STRING); + $orcidLogin = new \RAP\OrcidLogin($locator); + Flight::redirect($orcidLogin->retrieveToken($code)); }); Flight::route('/auth/eduGAIN', function() { diff --git a/index.php b/index.php index 485f948..7984b1c 100644 --- a/index.php +++ b/index.php @@ -29,16 +29,19 @@ include './include/gui-backend.php'; include './include/rest-web-service.php'; // Error handling -Flight::map('error', function(\Exception $ex) { +Flight::map('error', function($ex) { if ($ex instanceof \RAP\BadRequestException) { http_response_code(400); echo "Bad request: " . $ex->message; - } else { + } else if ($ex instanceof \Exception) { if ($ex->getMessage() !== null) { echo $ex->getMessage(); } else { echo $ex->getTraceAsString(); } + } else { + error_log('Error'); + throw $ex; } }); diff --git a/views/confirm-join.php b/views/confirm-join.php deleted file mode 100644 index 1c22d6a..0000000 --- a/views/confirm-join.php +++ /dev/null @@ -1,49 +0,0 @@ - - -

Confirm join request

- -
-
-
-
-
-
- -
-
-
-
- -
-
-
-
- -
-
-
-
-
- -
-
-

Pressing the following button the identities listed above will be joined.

-
- - -
-
-
- - - -

Success

- -
-
-

Your identities have been joined!

-
-

Return to index

-
- -
- eduGAIN) { ?> + eduGAIN || $model->orcid) { ?>
- - eduGAIN Logo - + eduGAIN) { ?> + + eduGAIN Logo + + + orcid) { ?> + + ORCID Logo + +
- Use the eduGAIN Logo to Login or Register to the RAP facility if you belong to an eduGAIN IdP. + Use the eduGAIN or OrcID Logo to Login or Register to RAP facility with your Institutional account.
google || $model->facebook || $model->linkedIn) { ?> -- GitLab