key = $key; if ($token) { $this->parseToken($token); } else { $this->header = new PropertyBag([ 'alg' => "HS256", 'typ' => "JWT", ]); $this->claims = new PropertyBag(); $this->valid = true; $this->generated = true; } } private function parseToken(string $token) { $this->generated = false; [ $header, $payload, $signature ] = explode(".", trim($token), 3); $hash = JWTUtil::encode(hash_hmac("sha256", $header.".".$payload, $this->key->getBinaryKey(), true)); if ($signature == $hash) { $this->valid = true; } $this->header = new PropertyBag(json_decode(JWTUtil::decode($header), true)); $this->claims = new PropertyBag(json_decode(JWTUtil::decode($payload), true)); if ($this->header->has('exp')) { $exp = intval($this->header->get('exp')); if ($exp <= time()) { // Invalid if expired $this->valid = false; } } } /** * Returns true if the expiry is not in the past. * * NOTE: This function will return true if the expiry header is missing, and * it will not validate any claims. For actual verification of a token matching * issuers, audience or other claims, see Validator\JWTValidator. * * @return bool True if the token expiry timestamp is missing or in the future */ public function isValid(): bool { return $this->valid; } /** * Returns true if the token was generated as opposed to parsed. * * @return bool */ public function isGenerated(): bool { return $this->generated; } public function __get(string $key) { switch ($key) { case 'header': return $this->header; case 'claims': return $this->claims; } } /** * Add a claim to a token. Throws an exception if the claim already exists. * * @param string $name The name of the claim * @param mixed $value Claim value * @throws \NoccyLabs\SimpleJWT\Collection\PropertyException if the claim already exists. */ public function addClaim(string $name, $value) { $this->claims->add($name, $value); } /** * Add a claim to a token. If the claim already exists it will be updated with * the provided value. * * @param string $name The name of the claim * @param mixed $value Claim value */ public function setClaim(string $name, $value) { $this->claims->set($name, $value); } /** * Set the time of expiry for the token. * * The expiry can be supplied as: * - \DateTime instance * - Unixtime as an integer * - A string represening the expiry time * - A period followed by a letter (m,h,d,w) * - null, to unset the expiry * * @param string|int|\DateTime $expiry * @return void */ public function setExpiry($expiry) { if ($expiry instanceof \DateTime) { $this->header->set('exp', $expiry->format("U")); } elseif ($expiry === null) { $this->header->delete('exp'); } elseif (is_numeric($expiry)) { if ($expiry < time()) { $this->header->set('exp', time() + $expiry); } else { $this->header->set('exp', intval($expiry)); } } elseif ($t = strtotime($expiry)) { $this->header->set('exp', $t); } elseif (preg_match('/([0-9]+)([wdhm])/', $expiry, $match)) { switch ($match[2]) { case 'w': $fact = 60 * 60 * 24 * 7; break; case 'd': $fact = 60 * 60 * 24; break; case 'h': $fact = 60 * 60; break; case 'm': $fact = 60; break; default: throw new \InvalidArgumentException(); } $this->header->set('exp', time() + (intval($match[1]) * $fact)); } else { throw new \InvalidArgumentException(); } } public function getSignedToken(): string { $header = JWTUtil::encode($this->header->getJson()); $payload = JWTUtil::encode($this->claims->getJson()); $hash = JWTUtil::encode(hash_hmac("sha256", $header.".".$payload, $this->key->getBinaryKey(), true)); return $header.".".$payload.".".$hash; } }