Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion dist/functions.jsconnect.php
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,10 @@ class JsConnect
* @var string
*/
protected $signingAlgorithm;
/**
* @var int
*/
protected $timeout = self::TIMEOUT;
/**
* JsConnect constructor.
*/
Expand Down Expand Up @@ -468,7 +472,7 @@ public function setGuest(bool $isGuest)
*/
public function jwtEncode(array $payload) : string
{
$payload += ['v' => $this->getVersion(), 'iat' => $this->getTimestamp(), 'exp' => $this->getTimestamp() + self::TIMEOUT];
$payload += ['v' => $this->getVersion(), 'iat' => $this->getTimestamp(), 'exp' => $this->getTimestamp() + $this->getTimeout()];
$jwt = JWT::encode($payload, $this->getSigningSecret(), $this->getSigningAlgorithm(), null, [self::FIELD_CLIENT_ID => $this->getSigningClientID()]);
return $jwt;
}
Expand Down Expand Up @@ -591,6 +595,26 @@ public function getVersion() : string
{
return self::VERSION;
}
/**
* Get the JWT expiry timeout.
*
* @return int
*/
public function getTimeout() : int
{
return $this->timeout;
}
/**
* Set the JWT expiry timeout.
*
* @param int $timeout
* @return $this
*/
public function setTimeout(int $timeout)
{
$this->timeout = $timeout;
return $this;
}
}
}

Expand Down
27 changes: 26 additions & 1 deletion src/JsConnect.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ class JsConnect {
*/
protected $signingAlgorithm;

/**
* @var int
*/
protected $timeout = self::TIMEOUT;

/**
* JsConnect constructor.
*/
Expand Down Expand Up @@ -278,7 +283,7 @@ public function jwtEncode(array $payload): string {
$payload += [
'v' => $this->getVersion(),
'iat' => $this->getTimestamp(),
'exp' => $this->getTimestamp() + self::TIMEOUT,
'exp' => $this->getTimestamp() + $this->getTimeout(),
];

$jwt = JWT::encode($payload, $this->getSigningSecret(), $this->getSigningAlgorithm(), null, [
Expand Down Expand Up @@ -405,4 +410,24 @@ final public static function decodeJWTHeader(string $jwt): ?array {
public function getVersion(): string {
return self::VERSION;
}

/**
* Get the JWT expiry timeout.
*
* @return int
*/
public function getTimeout(): int {
return $this->timeout;
}

/**
* Set the JWT expiry timeout.
*
* @param int $timeout
* @return $this
*/
public function setTimeout(int $timeout) {
$this->timeout = $timeout;
return $this;
}
}
5 changes: 5 additions & 0 deletions src/JsConnectServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace Vanilla\JsConnect;

use Firebase\JWT\ExpiredException;
use Firebase\JWT\JWT;
use Vanilla\JsConnect\Exceptions\InvalidValueException;

Expand Down Expand Up @@ -46,13 +47,17 @@ public function generateRequest(array $state = []): array {
// The cookie was already set. Make sure it's one of ours.
try {
$decodedCookie = $this->jwtDecode($state[self::FIELD_COOKIE]);
} catch (ExpiredException $ex) {
// The cookie was valid, but expired so issue a new one.
goto GENERATE_COOKIE;
} catch (\Exception $ex) {
throw new InvalidValueException('Could not use supplied jsConnect SSO token: '.$ex->getMessage());
}
$nonce = self::validateFieldExists(self::FIELD_NONCE, $decodedCookie, 'ssoToken');
$cookie = $state[self::FIELD_COOKIE];
unset($state[self::FIELD_COOKIE]);
} else {
GENERATE_COOKIE:
$nonce = JWT::urlsafeB64Encode(openssl_random_pseudo_bytes(15));
$cookie = $this->jwtEncode([self::FIELD_NONCE => $nonce]);
}
Expand Down
11 changes: 11 additions & 0 deletions tests/JsConnectServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,17 @@ public function testCookieReuse(): void {
$this->assertArrayNotHasKey(JsConnectServer::FIELD_COOKIE, $decoded2, "The cookie should be double encoded.");
}

/**
* If I try to re-use an expired, but valid cookie then a new one should be generated.
*/
public function testCookieReuseTimeout(): void {
$this->jsc->setTimeout(1);
list($_, $cookie) = $this->jsc->generateRequest();
sleep(2);
list($_, $cookie2) = $this->jsc->generateRequest([JsConnectServer::FIELD_COOKIE => $cookie]);
$this->assertNotSame($cookie, $cookie2);
}

/**
* I should not be able to re-use any old cookie value for JsConnect.
*/
Expand Down