Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

setPayload flag and customized header properties #21

Merged
merged 5 commits into from
Apr 25, 2015
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
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH I think this will be confusing, as the algorith can either be the algorithm itself or a dictionary or headers. I think we should change the constructor rather than have this as it leads to misunderstanding and legacy

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm okay with that, I was just trying to maintain backwards compatibility to prevent the big version bump.

}

/**
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about assigning directly as before?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is an artifact of the intermediary step where I had added a flag that you passed to turn off automatic claims inclusion which got backed out when switching to the subclass approach suggested by @odino. (That intermediary step has since been squashed+rebased out.) I can back this one out, though some would probably argue it is a good idea to use the public interface to prevent duplicated efforts if these setters ever do something more meaningful. Let me know and I'll fix it up.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brianjmiller if there is no special logic in setters and/or no need to expose them externally I would remove them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wrong, this is to make sure that the method of the subclass is called which does have special logic. For instance in EasyJWS setPayload adds the 'iat' claim.

$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
Loading