From 358ff07d6fe2d718df79aa41d5c294ac7351d24f Mon Sep 17 00:00:00 2001
From: Sonia Zorba <sonia.zorba@inaf.it>
Date: Tue, 3 Sep 2019 17:24:23 +0200
Subject: [PATCH] Implemented implicit grant flow (tested with Guacamole auth)

---
 classes/IdTokenBuilder.php       | 13 +++++++++----
 classes/JWKSHandler.php          |  3 ++-
 classes/OAuth2RequestHandler.php | 21 ++++++++++++++++-----
 classes/login/LoginHandler.php   |  2 +-
 classes/model/OAuth2Data.php     |  1 +
 include/front-controller.php     |  3 ++-
 6 files changed, 31 insertions(+), 12 deletions(-)

diff --git a/classes/IdTokenBuilder.php b/classes/IdTokenBuilder.php
index 1ea499d..a198208 100644
--- a/classes/IdTokenBuilder.php
+++ b/classes/IdTokenBuilder.php
@@ -12,16 +12,16 @@ class IdTokenBuilder {
         $this->locator = $locator;
     }
 
-    public function getIdToken(AccessToken $accessToken): string {
+    public function getIdToken(AccessToken $accessToken, $nonce = null): string {
 
         $keyPair = $this->locator->getJWKSDAO()->getNewestKeyPair();
 
-        $payload = $this->createPayloadArray($accessToken);
+        $payload = $this->createPayloadArray($accessToken, $nonce);
 
         return JWT::encode($payload, $keyPair->privateKey, $keyPair->alg, $keyPair->keyId);
     }
 
-    private function createPayloadArray(AccessToken $accessToken) {
+    private function createPayloadArray(AccessToken $accessToken, $nonce = null) {
 
         $user = $this->locator->getUserDAO()->findUserById($accessToken->userId);
 
@@ -30,9 +30,14 @@ class IdTokenBuilder {
             'sub' => $user->id,
             'iat' => time(),
             'exp' => time() + 3600,
-            'name' => $user->getCompleteName()
+            'name' => $user->getCompleteName(),
+            'aud' => $accessToken->clientId
         );
 
+        if ($nonce !== null) {
+            $payloadArr['nonce'] = $nonce;
+        }
+
         if (in_array("email", $accessToken->scope)) {
             $payloadArr['email'] = $user->getPrimaryEmail();
         }
diff --git a/classes/JWKSHandler.php b/classes/JWKSHandler.php
index 48b5822..4c2ca9b 100644
--- a/classes/JWKSHandler.php
+++ b/classes/JWKSHandler.php
@@ -21,7 +21,8 @@ class JWKSHandler {
 
         $rsa->setPrivateKeyFormat(RSA::PRIVATE_FORMAT_PKCS1);
         $rsa->setPublicKeyFormat(RSA::PUBLIC_FORMAT_PKCS8);
-        $result = $rsa->createKey();
+        // Guacamole needs a key of at least 2048
+        $result = $rsa->createKey(2048);
 
         $keyPair = new RSAKeyPair();
         $keyPair->alg = 'RS256';
diff --git a/classes/OAuth2RequestHandler.php b/classes/OAuth2RequestHandler.php
index 08c9967..44175c4 100644
--- a/classes/OAuth2RequestHandler.php
+++ b/classes/OAuth2RequestHandler.php
@@ -36,8 +36,10 @@ class OAuth2RequestHandler {
         }
 
         $state = $params['state'];
-        if ($state === null) {
-            throw new BadRequestException("State is required");
+        $nonce = $params['nonce'];
+
+        if ($state === null && $nonce === null) {
+            throw new BadRequestException("State or nonce is required");
         }
 
         // Storing OAuth2 data in session
@@ -45,6 +47,7 @@ class OAuth2RequestHandler {
         $oauth2Data->clientId = $client->client;
         $oauth2Data->redirectUrl = $client->redirectUrl;
         $oauth2Data->state = $state;
+        $oauth2Data->nonce = $nonce;
 
         $scope = $params['scope'];
         if ($scope !== null) {
@@ -55,7 +58,7 @@ class OAuth2RequestHandler {
         $session->setOAuth2Data($oauth2Data);
     }
 
-    public function getCodeResponseUrl(): string {
+    public function getRedirectResponseUrl(): string {
 
         $session = $this->locator->getSession();
 
@@ -70,9 +73,17 @@ class OAuth2RequestHandler {
         $this->locator->getAccessTokenDAO()->createAccessToken($accessToken);
 
         $state = $session->getOAuth2Data()->state;
+        $nonce = $session->getOAuth2Data()->nonce;
 
-        $redirectUrl = $session->getOAuth2Data()->redirectUrl
-                . '?code=' . $accessToken->code . '&scope=profile&state=' . $state;
+        if ($state !== null) {
+            // Authorization code grant flow
+            $redirectUrl = $session->getOAuth2Data()->redirectUrl
+                    . '?code=' . $accessToken->code . '&scope=profile&state=' . $state;
+        } else {
+            // Implicit grant flow
+            $idToken = $this->locator->getIdTokenBuilder()->getIdToken($accessToken, $nonce);
+            $redirectUrl = $session->getOAuth2Data()->redirectUrl . "#id_token=" . $idToken;
+        }
 
         return $redirectUrl;
     }
diff --git a/classes/login/LoginHandler.php b/classes/login/LoginHandler.php
index 0bb85fd..198e140 100644
--- a/classes/login/LoginHandler.php
+++ b/classes/login/LoginHandler.php
@@ -75,7 +75,7 @@ class LoginHandler {
 
         if ($session->getOAuth2Data() !== null) {
             $session->setUser($user);
-            $redirectUrl = $this->locator->getOAuth2RequestHandler()->getCodeResponseUrl();
+            $redirectUrl = $this->locator->getOAuth2RequestHandler()->getRedirectResponseUrl();
             session_destroy();
             return $redirectUrl;
         }
diff --git a/classes/model/OAuth2Data.php b/classes/model/OAuth2Data.php
index f4ae310..9716301 100644
--- a/classes/model/OAuth2Data.php
+++ b/classes/model/OAuth2Data.php
@@ -8,5 +8,6 @@ class OAuth2Data {
     public $redirectUrl;
     public $state;
     public $scope;
+    public $nonce;
 
 }
diff --git a/include/front-controller.php b/include/front-controller.php
index d47c05b..90a9da3 100644
--- a/include/front-controller.php
+++ b/include/front-controller.php
@@ -79,7 +79,8 @@ Flight::route('GET /auth/oauth2/authorize', function() {
         "redirect_uri" => filter_input(INPUT_GET, 'redirect_uri', FILTER_SANITIZE_STRING),
         "alg" => filter_input(INPUT_GET, 'alg', FILTER_SANITIZE_STRING),
         "state" => filter_input(INPUT_GET, 'state', FILTER_SANITIZE_STRING),
-        "scope" => filter_input(INPUT_GET, 'scope', FILTER_SANITIZE_STRING)
+        "scope" => filter_input(INPUT_GET, 'scope', FILTER_SANITIZE_STRING),
+        "nonce" => filter_input(INPUT_GET, 'nonce', FILTER_SANITIZE_STRING)
     ];
 
     $requestHandler = new \RAP\OAuth2RequestHandler($locator);
-- 
GitLab