Commit 9d4ad05d authored by Sonia Zorba's avatar Sonia Zorba
Browse files

Changes for JWT tokens

parent 47a4929d
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -6,3 +6,5 @@ vendor/
client-icons/
/nbproject/
*.pem
/build/
.phpunit.result.cache
+6 −0
Original line number Diff line number Diff line
@@ -115,6 +115,12 @@ Create the logs directory and assign ownership to the Apache user (usually www-d
    mkdir logs
    sudo chown www-data logs

### Run Unit Tests and build code coverage report

(XDebug or another code coverage driver needs to be installed; e.g. `sudo apt install php-xdebug`)

    ./vendor/bin/phpunit --bootstrap vendor/autoload.php --coverage-html build/coverage-report tests/

## Additional information and developer guide

See the wiki: https://www.ict.inaf.it/gitlab/zorba/rap-ia2/wikis/home
+6 −13
Original line number Diff line number Diff line
@@ -12,22 +12,13 @@ class IdTokenBuilder {
        $this->locator = $locator;
    }

    public function getIdToken(AccessToken $accessToken, string $alg): string {

        $head = array("alg" => $alg, "typ" => "JWT");

        $header = base64_encode(json_encode($head));
    public function getIdToken(AccessToken $accessToken): string {

        $keyPair = $this->locator->getJWKSDAO()->getNewestKeyPair();

        $payloadArr = $this->createPayloadArray($accessToken);
        $payloadArr['kid'] = $keyPair->keyId;

        $payload = base64_encode(json_encode($payloadArr));
        $payload = $this->createPayloadArray($accessToken);

        $token_value = $header . "." . $payload;

        return JWT::encode($token_value, $keyPair->privateKey, $alg);
        return JWT::encode($payload, $keyPair->privateKey, $keyPair->alg, $keyPair->keyId);
    }

    private function createPayloadArray(AccessToken $accessToken) {
@@ -48,8 +39,10 @@ class IdTokenBuilder {
        if (in_array("profile", $accessToken->scope)) {
            $payloadArr['given_name'] = $user->getName();
            $payloadArr['family_name'] = $user->getSurname();
            if ($user->getInstitution() !== null) {
                $payloadArr['org'] = $user->getInstitution();
            }
        }

        return $payloadArr;
    }
+23 −11
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@ class JWKSHandler {
        $rsa = new RSA();

        $rsa->setPrivateKeyFormat(RSA::PRIVATE_FORMAT_PKCS1);
        $rsa->setPublicKeyFormat(RSA::PUBLIC_FORMAT_PKCS1);
        $rsa->setPublicKeyFormat(RSA::PUBLIC_FORMAT_PKCS8);
        $result = $rsa->createKey();

        $keyPair = new RSAKeyPair();
@@ -41,25 +41,37 @@ class JWKSHandler {

        $keyPairs = $dao->getRSAKeyPairs();

        $jwks = [];
        $keys = [];
        foreach ($keyPairs as $keyPair) {

            $publicKey = str_replace("\n", "", $keyPair->publicKey);
            $publicKey = str_replace("\r", "", $publicKey);
            $publicKey = str_replace('-----BEGIN RSA PUBLIC KEY-----', '', $publicKey);
            $publicKey = str_replace('-----END RSA PUBLIC KEY-----', '', $publicKey);
            $rsa = new RSA();
            $rsa->loadKey($keyPair->publicKey);
            $rsa->setPublicKey();
            $publicKeyXML = $rsa->getPublicKey(RSA::PUBLIC_FORMAT_XML);

            $rsaModulus = $this->getTagContent($publicKeyXML, "Modulus");
            $rsaExponent = $this->getTagContent($publicKeyXML, "Exponent");

            $jwk = [];
            $jwk['kty'] = "RSA";
            $jwk['kid'] = $keyPair->id;
            $jwk['kid'] = $keyPair->keyId;
            $jwk['use'] = "sig";
            $jwk['n'] = $publicKey;
            $jwk['e'] = "AQAB";
            $jwk['n'] = $rsaModulus;
            $jwk['e'] = $rsaExponent;

            array_push($keys, $jwk);
        }

            array_push($jwks, $jwk);
        return [
            "keys" => $keys
        ];
    }

        return $jwks;
    private function getTagContent(string $publicKeyXML, string $tagname): string {
        $matches = [];
        $pattern = "#<\s*?$tagname\b[^>]*>(.*?)</$tagname\b[^>]*>#s";
        preg_match($pattern, $publicKeyXML, $matches);
        return $matches[1];
    }

}
+57 −58
Original line number Diff line number Diff line
@@ -10,44 +10,31 @@ class OAuth2RequestHandler {
        $this->locator = $locator;
    }

    public function handleAuthorizeRequest() {
    public function handleAuthorizeRequest($params) {

        if (!isset($_REQUEST['client_id'])) {
        if ($params['client_id'] === null) {
            throw new BadRequestException("Client id is required");
        }

        if (!isset($_REQUEST['redirect_uri'])) {
        if ($params['redirect_uri'] === null) {
            throw new BadRequestException("Redirect URI is required");
        }

        $clientId = $_REQUEST['client_id'];
        $redirectUrl = $_REQUEST['redirect_uri'];

        $client = $this->locator->getDAO()->getOAuth2ClientByClientId($clientId);
        $client = $this->locator->getDAO()->getOAuth2ClientByClientId($params['client_id']);
        if ($client === null) {
            throw new BadRequestException("Invalid client id: " . $clientId);
            throw new BadRequestException("Invalid client id: " . $params['client_id']);
        }
        if ($client->redirectUrl !== $redirectUrl) {
            throw new BadRequestException("Invalid client redirect URI: " . $redirectUrl);
        if ($client->redirectUrl !== $params['redirect_uri']) {
            throw new BadRequestException("Invalid client redirect URI: " . $params['redirect_uri']);
        }

        $alg;
        if (isset($_REQUEST['alg'])) {
            $alg = $_REQUEST['alg'];
        } else {
        $alg = $params['alg'];
        if ($alg === null) {
            $alg = "RS256";
        }

        if (isset($_GET['code'])) {
            
        } else {
            $this->executeStateFlow($client);
        }
    }

    private function executeStateFlow(OAuth2Client $client) {

        if (!isset($_REQUEST['state'])) {
        $state = $params['state'];
        if ($state === null) {
            throw new BadRequestException("State is required");
        }

@@ -55,7 +42,12 @@ class OAuth2RequestHandler {
        $oauth2Data = new \RAP\OAuth2Data();
        $oauth2Data->clientId = $client->client;
        $oauth2Data->redirectUrl = $client->redirectUrl;
        $oauth2Data->state = $_REQUEST['state'];
        $oauth2Data->state = $state;

        $scope = $params['scope'];
        if ($scope !== null) {
            $oauth2Data->scope = explode(' ', $scope);
        }

        $session = $this->locator->getSession();
        $session->setOAuth2Data($oauth2Data);
@@ -71,7 +63,7 @@ class OAuth2RequestHandler {
        $accessToken->userId = $session->user->id;
        $accessToken->clientId = $session->getOAuth2Data()->clientId;
        $accessToken->redirectUri = $session->getOAuth2Data()->redirectUrl;
        //$accessToken->scope = 
        $accessToken->scope = $session->getOAuth2Data()->scope;

        $this->locator->getAccessTokenDAO()->createAccessToken($accessToken);

@@ -83,46 +75,48 @@ class OAuth2RequestHandler {
        return $redirectUrl;
    }

    public function handleAccessTokenRequest(): array {
    public function handleAccessTokenRequest($params): array {

        $this->validateAccessTokenRequest();
        $this->validateAccessTokenRequest($params);

        $code = filter_input(INPUT_POST, 'code', FILTER_SANITIZE_STRING);
        $accessToken = $this->locator->getAccessTokenDAO()->retrieveAccessTokenFromCode($code);
        $accessToken = $this->locator->getAccessTokenDAO()->retrieveAccessTokenFromCode($params['code']);

        if ($accessToken === null) {
            throw new BadRequestException("No token for given code");
        }

        $this->validateParametersMatching();
        if ($accessToken->redirectUri !== $params['redirect_uri']) {
            throw new BadRequestException("Invalid redirect URI: " . $params['redirect_uri']);
        }

        $token = [];
        $token['access_token'] = $accessToken->token;
        //$token['access_token'] = $accessToken->token;
        $token['token_type'] = 'bearer';
        $token['expires_in'] = 300;
        error_log($accessToken->creationTime);
        error_log($accessToken->expirationTime);
        $token['expires_in'] = $this->getExpiresIn($accessToken);

        if ($accessToken->scope !== null) {
            $token['id_token'] = $this->locator->getIdTokenBuilder()->getIdToken($accessToken->userId, 'RS256');
            $token['access_token'] = $this->locator->getIdTokenBuilder()->getIdToken($accessToken);
            //$token['id_token'] = $this->locator->getIdTokenBuilder()->getIdToken($accessToken);
        } else {
            $token['access_token'] = $accessToken->token;
        }

        return $token;
    }

    private function validateAccessTokenRequest() {
    private function validateAccessTokenRequest($params) {

        if (!isset($_POST['grant_type'])) {
            throw new BadRequestException("Client id is required");
        } else if ($_POST['grant_type'] !== 'authorization_code') {
        if ($params['grant_type'] === null) {
            throw new BadRequestException("grant_type is required");
        } else if ($params['grant_type'] !== 'authorization_code') {
            throw new BadRequestException("grant_type must be authorization_code");
        }

        if (!isset($_POST['code'])) {
            throw new BadRequestException("Client id is required");
        if ($params['code'] === null) {
            throw new BadRequestException("code id is required");
        }

        if (!isset($_POST['redirect_uri'])) {
        if ($params['redirect_uri'] === null) {
            throw new BadRequestException("Redirect URI is required");
        }

@@ -130,27 +124,32 @@ class OAuth2RequestHandler {
        // however some clients don't send it
    }

    private function validateParametersMatching() {
        
    }

    public function handleCheckTokenRequest(): array {
    public function handleCheckTokenRequest($token): array {

        if (!isset($_POST['token'])) {
            throw new BadRequestException("Access token id is required");
        }

        $accessToken = filter_input(INPUT_POST, 'token', FILTER_SANITIZE_STRING);

        //if($accessToken)
        $accessToken = $this->locator->getAccessTokenDAO()->getAccessToken($token);
        $user = $this->locator->getDAO()->findUserById($accessToken->userId);

        $result = [];
        $result['exp'] = 3600;
        $result['user_name'] = "test";
        $result['client_id'] = "gms";
        $result['scope'] = "profile";
        $result['exp'] = $this->getExpiresIn($accessToken);
        $result['user_name'] = $user->id;
        $result['client_id'] = $accessToken->clientId;

        if ($accessToken->scope !== null) {
            $result['scope'] = $accessToken->scope;
            $result['id_token'] = $this->locator->getIdTokenBuilder()->getIdToken($accessToken);
        }

        return $result;
    }

    private function getExpiresIn(AccessToken $accessToken) {
        $expTime = strtotime($accessToken->expirationTime);
        $now = time();
        return $expTime - $now;
    }

}
Loading