Skip to content

Commit

Permalink
feat: Add JWT::fromJwtUnsafe for decode JWT without check signature
Browse files Browse the repository at this point in the history
  • Loading branch information
vincent4vx committed Aug 3, 2023
1 parent ef2bada commit 60f6a0a
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 0 deletions.
13 changes: 13 additions & 0 deletions src/Claims.php
Expand Up @@ -84,4 +84,17 @@ final public function offsetUnset($offset): void
{
unset($this->claims[$offset]);
}

/**
* Get a claim value
*
* @param string $name The claim name
* @param mixed $default The default value to use when the claim is not defined
*
* @return mixed The claim value
*/
final public function claim(string $name, $default = null)
{
return $this->claims[$name] ?? $default;
}
}
36 changes: 36 additions & 0 deletions src/JWT.php
Expand Up @@ -2,6 +2,14 @@

namespace B2pweb\Jwt;

use Base64Url\Base64Url;
use InvalidArgumentException;

use function count;
use function explode;
use function is_array;
use function json_decode;

/**
* Store the parsed JWT data
*/
Expand Down Expand Up @@ -65,4 +73,32 @@ public function payload(): array
{
return $this->toArray();
}

/**
* Raw decode a JWT token, without any validation
* This method is unsafe, on should be used only if key cannot be resolved yet
*
* @param string $jwt The JWT string
*
* @return JWT The decoded JWT
*
* @see JwtDecoder::decode() For a safe decoding
*/
public static function fromJwtUnsafe(string $jwt): JWT
{
$parts = explode('.', $jwt);

if (count($parts) !== 3) {
throw new InvalidArgumentException('Invalid JWT');
}

$headers = json_decode(Base64Url::decode($parts[0]), true);
$payload = json_decode(Base64Url::decode($parts[1]), true);

if (!is_array($headers) || !is_array($payload)) {
throw new InvalidArgumentException('Invalid JWT');
}

return new JWT($jwt, $headers, $payload);
}
}
4 changes: 4 additions & 0 deletions tests/ClaimsTest.php
Expand Up @@ -14,6 +14,10 @@ public function test_array_access()

$this->assertTrue(isset($claims['foo']));
$this->assertSame('bar', $claims['foo']);
$this->assertSame('bar', $claims->claim('foo'));
$this->assertSame('bar', $claims->claim('foo', 'zzz'));
$this->assertNull($claims->claim('a'));
$this->assertSame('zzz', $claims->claim('a', 'zzz'));

$claims['foo'] = 'baz';
$this->assertSame('baz', $claims['foo']);
Expand Down
59 changes: 59 additions & 0 deletions tests/JWTTest.php
@@ -0,0 +1,59 @@
<?php

namespace B2pweb\Jwt\Tests;

use B2pweb\Jwt\JWT;
use Base64Url\Base64Url;
use PHPUnit\Framework\TestCase;

class JWTTest extends TestCase
{
public function test_fromJwtUnsafe_success()
{
$jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';

$decoded = JWT::fromJwtUnsafe($jwt);

$this->assertSame(['alg' => 'HS256', 'typ' => 'JWT'], $decoded->headers());
$this->assertSame(['sub' => '1234567890', 'name' => 'John Doe', 'iat' => 1516239022], $decoded->payload());
$this->assertSame($jwt, $decoded->encoded());
}

public function test_fromJwtUnsafe_missing_parts()
{
$this->expectException(\InvalidArgumentException::class);
JWT::fromJwtUnsafe('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ');
}

public function test_fromJwtUnsafe_header_not_base64()
{
$this->expectException(\InvalidArgumentException::class);
$jwt = '###.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';

JWT::fromJwtUnsafe($jwt);
}

public function test_fromJwtUnsafe_payload_not_base64()
{
$this->expectException(\InvalidArgumentException::class);
$jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.###.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';

JWT::fromJwtUnsafe($jwt);
}

public function test_fromJwtUnsafe_header_not_valid_json()
{
$this->expectException(\InvalidArgumentException::class);
$jwt = 'MTIz.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';

JWT::fromJwtUnsafe($jwt);
}

public function test_fromJwtUnsafe_payload_not_valid_json()
{
$this->expectException(\InvalidArgumentException::class);
$jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.MTIz.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';

JWT::fromJwtUnsafe($jwt);
}
}

0 comments on commit 60f6a0a

Please sign in to comment.