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

Support for a custom json encoder #119

Closed
Closed
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
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,14 @@ vulnerability. More info [here](http://tech.namshi.com/blog/2015/02/19/update-yo
## Using a custom encoder

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.
inject any implementation of `Namshi\JOSE\Encoder\Encoder` in a `JWS` instance.
Likewise, `JWS::load()` accepts such an implementation as a second argument.

### Using a custom json encoder
The token is a base64 encoded json. If you want to control how the json is
encoded, you can inject a custom json encoder using: `JWS::setJsonEncoder()`
which takes an instance of `Namshi\JOSE\Encoder\Encoder`.

## Implementation Specifics

The library provides a base JWT Class that implements what is needed just for JSON Web Tokens. The JWS Class then extends
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Namshi\JOSE\Base64;
namespace Namshi\JOSE\Encoder;

class Base64Encoder implements Encoder
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Namshi\JOSE\Base64;
namespace Namshi\JOSE\Encoder;

class Base64UrlSafeEncoder implements Encoder
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Namshi\JOSE\Base64;
namespace Namshi\JOSE\Encoder;

interface Encoder
{
Expand Down
26 changes: 26 additions & 0 deletions src/Namshi/JOSE/Encoder/JsonEncoder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace Namshi\JOSE\Encoder;

class JsonEncoder implements Encoder
{
/**
* @param string $data
*
* @return string
*/
public function encode($data)
{
return json_encode($data, JSON_UNESCAPED_SLASHES);
}

/**
* @param string $data
*
* @return string
*/
public function decode($data)
{
return json_decode($data, true);
}
}
32 changes: 19 additions & 13 deletions src/Namshi/JOSE/JWS.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
namespace Namshi\JOSE;

use InvalidArgumentException;
use Namshi\JOSE\Base64\Base64Encoder;
use Namshi\JOSE\Base64\Base64UrlSafeEncoder;
use Namshi\JOSE\Base64\Encoder;
use Namshi\JOSE\Encoder\JsonEncoder;
use Namshi\JOSE\Encoder\Base64Encoder;
use Namshi\JOSE\Encoder\Base64UrlSafeEncoder;
use Namshi\JOSE\Encoder\Encoder;
use Namshi\JOSE\Signer\SignerInterface;

/**
Expand All @@ -30,7 +31,7 @@ class JWS extends JWT
* @see https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-4
*
* @param string $encryptionEngine
* }
*
*/
public function __construct($header = array(), $encryptionEngine = 'OpenSSL')
{
Expand Down Expand Up @@ -96,32 +97,36 @@ public function getTokenString()
{
$signinInput = $this->generateSigninInput();

return sprintf('%s.%s', $signinInput, $this->encoder->encode($this->getSignature()));
return sprintf('%s.%s', $signinInput, $this->base64Encoder->encode($this->getSignature()));
}

/**
* Creates an instance of a JWS from a JWT.
*
* @param string $jwsTokenString
* @param bool $allowUnsecure
* @param Encoder $encoder
* @param Encoder $base64Encoder
* @param string $encryptionEngine
*
* @return JWS
*
* @throws \InvalidArgumentException
*/
public static function load($jwsTokenString, $allowUnsecure = false, Encoder $encoder = null, $encryptionEngine = 'OpenSSL')
public static function load($jwsTokenString, $allowUnsecure = false, Encoder $base64Encoder = null, $encryptionEngine = 'OpenSSL', Encoder $jsonEncoder = null)
{
if ($encoder === null) {
$encoder = strpbrk($jwsTokenString, '+/=') ? new Base64Encoder() : new Base64UrlSafeEncoder();
if ($base64Encoder === null) {
$base64Encoder = strpbrk($jwsTokenString, '+/=') ? new Base64Encoder() : new Base64UrlSafeEncoder();
}

if ($jsonEncoder === null) {
$jsonEncoder = new JsonEncoder();
}

$parts = explode('.', $jwsTokenString);

if (count($parts) === 3) {
$header = json_decode($encoder->decode($parts[0]), true);
$payload = json_decode($encoder->decode($parts[1]), true);
$header = $jsonEncoder->decode($base64Encoder->decode($parts[0]));
$payload = $jsonEncoder->decode($base64Encoder->decode($parts[1]));

if (is_array($header) && is_array($payload)) {
if (strtolower($header['alg']) === 'none' && !$allowUnsecure) {
Expand All @@ -130,7 +135,8 @@ public static function load($jwsTokenString, $allowUnsecure = false, Encoder $en

$jws = new static($header, $encryptionEngine);

$jws->setEncoder($encoder)
$jws->setBase64Encoder($base64Encoder)
->setJsonEncoder($jsonEncoder)
->setHeader($header)
->setPayload($payload)
->setOriginalToken($jwsTokenString)
Expand Down Expand Up @@ -158,7 +164,7 @@ public function verify($key, $algo = null)
return false;
}

$decodedSignature = $this->encoder->decode($this->getEncodedSignature());
$decodedSignature = $this->base64Encoder->decode($this->getEncodedSignature());
$signinInput = $this->getSigninInput();

return $this->getSigner()->verify($key, $decodedSignature, $signinInput);
Expand Down
42 changes: 35 additions & 7 deletions src/Namshi/JOSE/JWT.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

namespace Namshi\JOSE;

use Namshi\JOSE\Base64\Base64UrlSafeEncoder;
use Namshi\JOSE\Base64\Encoder;
use Namshi\JOSE\Encoder\Base64UrlSafeEncoder;
use Namshi\JOSE\Encoder\JsonEncoder;
use Namshi\JOSE\Encoder\Encoder;

/**
* Class representing a JSON Web Token.
Expand All @@ -23,7 +24,12 @@ class JWT
/**
* @var Encoder
*/
protected $encoder;
protected $base64Encoder;

/**
* @var Encoder
*/
protected $jsonEncoder;

/**
* Constructor.
Expand All @@ -35,15 +41,35 @@ public function __construct(array $payload, array $header)
{
$this->setPayload($payload);
$this->setHeader($header);
$this->setEncoder(new Base64UrlSafeEncoder());
$this->setBase64Encoder(new Base64UrlSafeEncoder());
$this->setJsonEncoder(new JsonEncoder());
}

/**
* @param Encoder $encoder
*/
public function setBase64Encoder(Encoder $encoder)
{
$this->base64Encoder = $encoder;

return $this;
}

/**
* @param Encoder $encoder
* @deprecated Use setBase64Encoder()
*/
public function setEncoder(Encoder $encoder)
{
$this->encoder = $encoder;
return $this->setBase64Encoder($encoder);
}

/**
* @param Encoder $encoder
*/
public function setJsonEncoder(Encoder $encoder)
{
$this->jsonEncoder = $encoder;

return $this;
}
Expand All @@ -55,8 +81,10 @@ public function setEncoder(Encoder $encoder)
*/
public function generateSigninInput()
{
$base64payload = $this->encoder->encode(json_encode($this->getPayload(), JSON_UNESCAPED_SLASHES));
$base64header = $this->encoder->encode(json_encode($this->getHeader(), JSON_UNESCAPED_SLASHES));
$payload = $this->jsonEncoder->encode($this->getPayload());
$base64payload = $this->base64Encoder->encode($payload);
$header = $this->jsonEncoder->encode($this->getHeader());
$base64header = $this->base64Encoder->encode($header);

return sprintf('%s.%s', $base64header, $base64payload);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Namshi/JOSE/Test/BCJWSTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Namshi\JOSE\Test;

use Namshi\JOSE\Base64\Base64Encoder;
use Namshi\JOSE\Encoder\Base64Encoder;
use Namshi\JOSE\JWS;
use PHPUnit_Framework_TestCase as TestCase;

Expand Down
4 changes: 2 additions & 2 deletions tests/Namshi/JOSE/Test/JWSTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use PHPUnit_Framework_TestCase as TestCase;
use Prophecy\Argument;
use Namshi\JOSE\Signer\OpenSSL\HS256;
use Namshi\JOSE\Base64\Base64UrlSafeEncoder;
use Namshi\JOSE\Encoder\Base64UrlSafeEncoder;

class JWSTest extends TestCase
{
Expand Down Expand Up @@ -135,7 +135,7 @@ public function testVerificationRS256KeyAsString()

public function testUseOfCustomEncoder()
{
$encoder = $this->prophesize('Namshi\JOSE\Base64\Encoder');
$encoder = $this->prophesize('Namshi\JOSE\Encoder\Encoder');
$encoder
->decode(Argument::any())
->willReturn('{"whatever": "the payload should be"}')
Expand Down
15 changes: 9 additions & 6 deletions tests/Namshi/JOSE/Test/JWTTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

namespace Namshi\JOSE\Test;

use Namshi\JOSE\Base64\Base64UrlSafeEncoder;
use Namshi\JOSE\Encoder\Base64UrlSafeEncoder;
use Namshi\JOSE\Encoder\JsonEncoder;
use Namshi\JOSE\JWT;
use PHPUnit_Framework_TestCase as TestCase;

Expand All @@ -13,17 +14,19 @@ public function testGenerationOfTheSigninInput()
$payload = array('b' => 'a', 'iat' => 1421161177);
$header = array('a' => 'b');
$jwt = new JWT($payload, $header);
$encoder = new Base64UrlSafeEncoder();
$base64Encoder = new Base64UrlSafeEncoder();
$jsonEncoder = new JsonEncoder();

$this->assertEquals(sprintf('%s.%s', $encoder->encode(json_encode($header)), $encoder->encode(json_encode($payload))), $jwt->generateSigninInput());
$this->assertEquals(sprintf('%s.%s', $base64Encoder->encode($jsonEncoder->encode($header)), $base64Encoder->encode($jsonEncoder->encode($payload))), $jwt->generateSigninInput());
}

public function testGenerationOfTheSigninInputCanHandleSlashes()
{
$encoder = new Base64UrlSafeEncoder();
$base64Encoder= new Base64UrlSafeEncoder();
$jsonEncoder = new JsonEncoder();
$json_string = '{"a":"/b/"}';
$encoded_json_string = $encoder->encode($json_string);
$jwt = new JWT(json_decode($json_string, true), json_decode($json_string, true));
$encoded_json_string = $base64Encoder->encode($json_string);
$jwt = new JWT($jsonEncoder->decode($json_string), $jsonEncoder->decode($json_string));

$this->assertEquals(sprintf('%s.%s', $encoded_json_string, $encoded_json_string), $jwt->generateSigninInput());
}
Expand Down