Skip to content

Commit

Permalink
Merge pull request #21 from brianjmiller/master
Browse files Browse the repository at this point in the history
setPayload flag and customized header properties
  • Loading branch information
cirpo committed Apr 25, 2015
2 parents 190df49 + 3186960 commit a418f6c
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 99 deletions.
44 changes: 31 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ implementation of the JWS

This library needs PHP 5.4+ and the library OpenSSL.

It has been tested using `PHP5.3` to `PHP5.6` and `HHVM`.
It has been tested using `PHP5.4` to `PHP5.6` and `HHVM`.


## Installation
Expand All @@ -22,7 +22,7 @@ You can install the library directly from
composer / [packagist](https://packagist.org/packages/namshi/jose):

```
"namshi/jose": "2.1.*"
"namshi/jose": "4.0.*"
```

## Usage
Expand All @@ -43,12 +43,14 @@ First, generate the JWS:
``` php
<?php

use Namshi\JOSE\JWS;
use Namshi\JOSE\SimpleJWS;

if ($username == 'correctUsername' && $pass == 'ok') {
$user = Db::loadUserByUsername($username);

$jws = new JWS('RS256');
$jws = new SimpleJWS(array(
'alg' => 'RS256'
));
$jws->setPayload(array(
'uid' => $user->getid(),
));
Expand All @@ -68,9 +70,9 @@ is a valid call:
``` php
<?php

use Namshi\JOSE\JWS;
use Namshi\JOSE\SimpleJWS;

$jws = JWS::load($_COOKIE['identity']);
$jws = SimpleJWS::load($_COOKIE['identity']);
$public_key = openssl_pkey_get_public("/path/to/public.key");

// verify that the token is valid and had the same values
Expand Down Expand Up @@ -98,12 +100,12 @@ In these cases, simply add the optional `'SecLib'` parameter when
constructing a JWS:

```php
$jws = new JWS('RS256', 'JWS', 'SecLib');
$jws = new JWS(array('alg' => 'RS256'), 'SecLib');
```

You can now use the PHPSecLib implmentaiton of RSA signing. If you use
You can now use the PHPSecLib implementation of RSA signing. If you use
a password protected private key, you can still submit the private key
to use for signing as a string, as long as if you pass the password as the
to use for signing as a string, as long as you pass the password as the
second parameter into the `sign` method:

```php
Expand All @@ -118,15 +120,15 @@ $jws = JWS::load($tokenString, false, $encoder, 'SecLib');

## Under the hood

In order to [validate the JWS](https://github.com/namshi/jose/blob/master/src/Namshi/JOSE/JWS.php#L126),
the signature is first [verified](https://github.com/namshi/jose/blob/master/src/Namshi/JOSE/JWS.php#L110)
with a public key and then we will check whether the [token is expired](https://github.com/namshi/jose/blob/master/src/Namshi/JOSE/JWS.php#L172).
In order to [validate the JWS](https://github.com/namshi/jose/blob/master/src/Namshi/JOSE/SimpleJWS.php#L43),
the signature is first [verified](https://github.com/namshi/jose/blob/master/src/Namshi/JOSE/JWS.php#L113)
with a public key and then we will check whether the [token is expired](https://github.com/namshi/jose/blob/master/src/Namshi/JOSE/SimpleJWS.php#L55).

To give a JWS a TTL, just use the standard `exp` value in the payload:

``` php
$date = new DateTime('tomorrow');
$this->jws = new JWS('RS256');
$this->jws = new SimpleJWS(array('alg' => 'RS256'));
$this->jws->setPayload(array(
'exp' => $date->format('U'),
));
Expand Down Expand Up @@ -154,6 +156,22 @@ If, for some reason, you need to encode the token in a different way, you can
inject any implementation of `Namshi\JOSE\Base64\Encoder` in a `JWS` instance.
Likewise, `JWS::load()` accepts such an implementation as a second argument.

## Implementation Specifics

The library provides a base JWT Class that implements what is needed just for JSON Web Tokens. The JWS Class then extends
the JWT class and adds the implementation for signing and verifying using JSON Web Signatures. The SimpleJWS class extends
the base JWS class and adds validation of a TTL and inclusion of automatic claims.

## Major Versions

### 2.x.x to 3.x.x

Introduced the ability to specify an encryption engine. Added support of PHPSecLib to the existing OpenSSL implementation.

### 3.x.x to 4.x.x - Not Backwards Compatible

Added the ability to set custom properties in the header. Moved automatic inclusion of certain claims into an SimpleJWS class from the base JWS class.

## Credits

This library has been inspired by the
Expand Down
50 changes: 11 additions & 39 deletions src/Namshi/JOSE/JWS.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use Namshi\JOSE\Base64\Encoder;

/**
* Class representing a JSOn Web Signature.
* Class representing a JSON Web Signature.
*/
class JWS extends JWT
{
Expand All @@ -22,17 +22,21 @@ class JWS extends JWT
/**
* Constructor
*
* @param string $algorithm
* @param string $type
* @param array $header An associative array of headers. The value can be any type accepted by json_encode or a JSON serializable object
* @see http://php.net/manual/en/function.json-encode.php
* @see http://php.net/manual/en/jsonserializable.jsonserialize.php
* @see https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-4
* @param string $encryptionEngine
* }
*/
public function __construct($algorithm, $type = null, $encryptionEngine = "OpenSSL")
public function __construct($header = array(), $encryptionEngine = "OpenSSL")
{
if (!in_array($encryptionEngine, $this->supportedEncryptionEngines)) {
throw new InvalidArgumentException(sprintf("Encryption engine %s is not supported", $encryptionEngine));
}
$this->encryptionEngine = $encryptionEngine;
parent::__construct(array(), array('alg' => $algorithm, 'typ' => $type ?: "JWS"));

parent::__construct(array(), $header);
}

/**
Expand Down Expand Up @@ -110,8 +114,8 @@ public static function load($jwsTokenString, $allowUnsecure = false, Encoder $en
throw new InvalidArgumentException(sprintf('The token "%s" cannot be validated in a secure context, as it uses the unallowed "none" algorithm', $jwsTokenString));
}

$jws = new self($header['alg'], isset($header['typ']) ? $header['typ'] : null, $encryptionEngine);
$jws = new static($header, $encryptionEngine);

$jws->setEncoder($encoder)
->setHeader($header)
->setPayload($payload)
Expand Down Expand Up @@ -144,20 +148,6 @@ public function verify($key, $algo = null)
return $this->getSigner()->verify($key, $decodedSignature, $signinInput);
}

/**
* Checks that the JWS has been signed with a valid private key by verifying it with a public $key
* and the token is not expired.
*
* @param resource|string $key
* @param string $algo The algorithms this JWS should be signed with. Use it if you want to restrict which algorithms you want to allow to be validated.
*
* @return bool
*/
public function isValid($key, $algo = null)
{
return $this->verify($key, $algo) && ! $this->isExpired();
}

/**
* Returns the base64 encoded signature.
*
Expand Down Expand Up @@ -198,22 +188,4 @@ protected function getSigner()
throw new InvalidArgumentException(
sprintf("The algorithm '%s' is not supported for %s", $this->header['alg'], $this->encryptionEngine));
}

/**
* Checks whether the token is expired.
*
* @return bool
*/
protected function isExpired()
{
$payload = $this->getPayload();

if (isset($payload['exp']) && is_numeric($payload['exp'])) {
$now = new \DateTime('now');

return ($now->format('U') - $payload['exp']) > 0;
}

return false;
}
}
11 changes: 3 additions & 8 deletions src/Namshi/JOSE/JWT.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ class JWT
*/
public function __construct(array $payload, array $header)
{
$this->payload = $payload;
$this->header = $header;
$this->encoder = new Base64UrlSafeEncoder();
$this->setPayload($payload);
$this->setHeader($header);
$this->setEncoder(new Base64UrlSafeEncoder());
}

/**
Expand Down Expand Up @@ -81,11 +81,6 @@ public function setPayload(array $payload)
{
$this->payload = $payload;

if (!isset($this->payload['iat'])) {
$now = new \DateTime('now');
$this->payload['iat'] = $now->format('U');
}

return $this;
}

Expand Down
73 changes: 73 additions & 0 deletions src/Namshi/JOSE/SimpleJWS.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

namespace Namshi\JOSE;

/**
* Class providing an easy to use JWS implementation.
*/
class SimpleJWS extends JWS
{
/**
* Constructor
*
* @param array $header An associative array of headers. The value can be any type accepted by json_encode or a JSON serializable object
* @see http://php.net/manual/en/function.json-encode.php
* @see http://php.net/manual/en/jsonserializable.jsonserialize.php
* @see https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-4
* }
*/
public function __construct($header = array())
{
if (!isset($header['typ'])) {
$header['typ'] = 'JWS';
}
parent::__construct($header);
}

/**
* Sets the payload of the current JWS with an issued at value in the 'iat' property.
*
* @param array $payload
*/
public function setPayload(array $payload)
{
if (!isset($payload['iat'])) {
$now = new \DateTime('now');
$payload['iat'] = $now->format('U');
}

return parent::setPayload($payload);
}

/**
* Checks that the JWS has been signed with a valid private key by verifying it with a public $key
* and the token is not expired.
*
* @param resource|string $key
* @param string $algo The algorithms this JWS should be signed with. Use it if you want to restrict which algorithms you want to allow to be validated.
*
* @return bool
*/
public function isValid($key, $algo = null)
{
return $this->verify($key, $algo) && ! $this->isExpired();
}

/**
* Checks whether the token is expired based on the 'exp' value.
*
* @return bool
*/
protected function isExpired()
{
$payload = $this->getPayload();

if (isset($payload['exp']) && is_numeric($payload['exp'])) {
$now = new \DateTime('now');

return ($now->format('U') - $payload['exp']) > 0;
}

return false;
}
}
2 changes: 1 addition & 1 deletion tests/Namshi/JOSE/Test/BCJWSTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public function testTestBC()
);

foreach ($data as $payload) {
$jwsOld = new JWS("RS256");
$jwsOld = new JWS(array("alg" => "RS256"));
$jwsOld->setEncoder(new Base64Encoder());
$jwsOld->setPayload($payload);
$jwsOld->sign(openssl_pkey_get_private(SSL_KEYS_PATH . "private.key", self::SSL_KEY_PASSPHRASE));
Expand Down

0 comments on commit a418f6c

Please sign in to comment.