diff --git a/htdocs/web_portal/index.php b/htdocs/web_portal/index.php
index 2e21d49f6..3a2a012f3 100644
--- a/htdocs/web_portal/index.php
+++ b/htdocs/web_portal/index.php
@@ -26,6 +26,8 @@
// Require GocContextPath which is used in most of the views scripts
require_once __DIR__.'/GocContextPath.php';
+use org\gocdb\security\authentication\BadCredentialsException;
+
// Set the timezone
date_default_timezone_set("UTC");
@@ -84,9 +86,21 @@ function rejectIfNotAuthenticated($message = null){
try {
Draw_Page($Page_Type);
+} catch (BadCredentialsException $error) {
+ /**
+ * `show_view('error.php', ..., $rawOutput)` is not suitable here.
+ * - setting rawOutput to FALSE triggers another exception because it
+ * tries to render a pretty error in a GOCDB window, which fails because
+ * the user isn't authroised.
+ * - setting rawOutput to TRUE also isn't ideal as it displays html tags
+ * in the otherwise nicely formatted output.
+ * die-ing like this atleast gives the user a somewhat nicely formatted
+ * error.
+ */
+ die($error->getMessage());
} catch (ErrorException $e) {
/* ErrorExceptions may be thrown by an invalid configuration so it is
- not safe to try to give a pretty output. Set 'raw' to true. */
+ not safe to try to give a pretty output. Set 'rawOutput' to true. */
show_view('error.php', $e->getMessage(), NULL, TRUE);
die();
} catch(Exception $e) {
diff --git a/lib/Authentication/AuthTokens/EOSCAAIAuthToken.php b/lib/Authentication/AuthTokens/EOSCAAIAuthToken.php
new file mode 100644
index 000000000..4b6e6080b
--- /dev/null
+++ b/lib/Authentication/AuthTokens/EOSCAAIAuthToken.php
@@ -0,0 +1,31 @@
+acceptedIssuers = array("https://aai-demo.eosc-portal.eu/auth/realms/core");
+ $this->authRealm = "EOSC Proxy IdP";
+ $this->groupHeader = "OIDC_CLAIM_eduperson_entitlement";
+ $this->groupSplitChar = ',';
+ $this->bannedGroups = array();
+ $this->requiredGroups = array("urn:geant:eosc-portal.eu:res:gocdb.eosc-portal.eu");
+ $this->helpString = 'Please seek assistance by opening a ticket against the ' .
+ '"EOSC AAI: Core Infrastructure Proxy" group in ' .
+ 'https://eosc-helpdesk.eosc-portal.eu/';
+
+ if (isset($_SERVER['OIDC_access_token'])) {
+ $this->setTokenFromSession();
+ }
+ }
+}
diff --git a/lib/Authentication/AuthTokens/OIDCAuthToken.php b/lib/Authentication/AuthTokens/OIDCAuthToken.php
new file mode 100644
index 000000000..d14f7e5bb
--- /dev/null
+++ b/lib/Authentication/AuthTokens/OIDCAuthToken.php
@@ -0,0 +1,211 @@
+authorities;
+ }
+
+ /**
+ * {@see IAuthentication::getCredentials()}
+ * @return string An empty string as passwords are not used by this token.
+ */
+ public function getCredentials()
+ {
+ return ""; // none used in this token, handled by IdP
+ }
+
+ /**
+ * A custom object used to store additional user details.
+ * Allows non-security related user information (such as email addresses,
+ * telephone numbers etc) to be stored in a convenient location.
+ * {@see IAuthentication::getDetails()}
+ *
+ * @return Object or null if not used
+ */
+ public function getDetails()
+ {
+ return $this->userDetails;
+ }
+
+ /**
+ * {@see IAuthentication::getPrinciple()}
+ * @return string unique principle string of user
+ */
+ public function getPrinciple()
+ {
+ return $this->principal;
+ }
+
+ /**
+ * {@see IAuthentication::setAuthorities($authorities)}
+ */
+ public function setAuthorities($authorities)
+ {
+ $this->authorities = $authorities;
+ }
+
+ /**
+ * {@see IAuthentication::setDetails($userDetails)}
+ * @param Object $userDetails
+ */
+ public function setDetails($userDetails)
+ {
+ $this->userDetails = $userDetails;
+ }
+
+ /**
+ * {@see IAuthentication::validate()}
+ */
+ public function validate()
+ {
+ }
+
+ /**
+ * {@see IAuthentication::isPreAuthenticating()}
+ */
+ public static function isPreAuthenticating()
+ {
+ return true;
+ }
+
+ /**
+ * Returns true, this token reads the session attributes and so
+ * does not need to be stateful itself.
+ * {@see IAuthentication::isStateless()}
+ */
+ public static function isStateless()
+ {
+ return true;
+ }
+
+ /**
+ * Set principal/User details from the session and check group membership.
+ */
+ protected function setTokenFromSession()
+ {
+ if (in_array($_SERVER['OIDC_CLAIM_iss'], $this->acceptedIssuers, true)) {
+ $this->principal = $_SERVER['REMOTE_USER'];
+ $this->userDetails = array(
+ 'AuthenticationRealm' => array($this->authRealm)
+ );
+
+ // Check group membership is acceptable.
+ $this->checkBannedGroups();
+ $this->checkRequiredGroups();
+ }
+ }
+
+ /**
+ * Check the token lists all the required groups.
+ */
+ protected function checkRequiredGroups()
+ {
+ $groupArray = explode(
+ $this->groupSplitChar,
+ $_SERVER[$this->groupHeader]
+ );
+
+ // Build up a list of missing groups.
+ $missingGoodGroups = [];
+ foreach ($this->requiredGroups as $group) {
+ if (!in_array($group, $groupArray)) {
+ $missingGoodGroups[] = $group;
+ }
+ }
+
+ // If the list of missing groups is not empty, reject the user.
+ if (!empty($missingGoodGroups)) {
+ $this->rejectUser(
+ 'You are missing the following group(s):',
+ $missingGoodGroups
+ );
+ }
+ }
+
+ /**
+ * Check the token lists none of the banned groups.
+ */
+ protected function checkBannedGroups()
+ {
+ $groupArray = explode(
+ $this->groupSplitChar,
+ $_SERVER[$this->groupHeader]
+ );
+
+ $presentBadGroups = [];
+ foreach ($this->bannedGroups as $group) {
+ if (in_array($group, $groupArray)) {
+ $presentBadGroups[] = $group;
+ }
+ }
+
+ // If the list of present bad groups is not empty, reject the user.
+ if (!empty($presentBadGroups)) {
+ $this->rejectUser(
+ 'We do not grant access to GOCDB to members of the following group(s):',
+ $presentBadGroups
+ );
+ }
+ }
+
+ /**
+ * Craft a BadCredentialsException exception.
+ *
+ * Uses the given error message to provide the end user more context.
+ *
+ * @param string $errorContext Context for the error.
+ * @param string[] $groupArray An array of group memberships
+ */
+ protected function rejectUser($errorContext, $groupArray)
+ {
+ // For readability, when listing groups to the user,
+ // start each one on a new line with a '-' character.
+ $prependString = '
- ';
+ $groupString = implode($prependString, $groupArray);
+ throw new BadCredentialsException(
+ null,
+ 'You do not belong to the correct group(s) ' .
+ 'to gain access to this site.
' . $errorContext .
+ $prependString . $groupString . '
' . $this->helpString
+ );
+ }
+}