Skip to content

Commit

Permalink
Support additional tokens through ClaimsInterface
Browse files Browse the repository at this point in the history
  • Loading branch information
Henrik Bjornskov committed Sep 6, 2015
1 parent a9e786f commit ba80954
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 13 deletions.
11 changes: 11 additions & 0 deletions src/ClaimsAwareInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Antenna;

interface ClaimsAwareInterface
{
/**
* @return [string]mixed a key/value array of additional claims
*/
public function getClaims();
}
10 changes: 7 additions & 3 deletions src/Coder.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public function __construct($secret)

public function encode(WebToken $webToken)
{
$payload = [
$payload = $webToken->all() + [
'sub' => $webToken->getSubject(),
'iat' => $webToken->getIssuedAt()->getTimestamp(),
'exp' => $webToken->getExpireAt()->getTimestamp(),
Expand All @@ -33,15 +33,19 @@ public function encode(WebToken $webToken)
*/
public function decode($encoded)
{
$payload = (array) JWT::decode($encoded, $this->secret, [$this->algoritm]) + [
$defaults = [
'sub' => null,
'iat' => null,
'exp' => null,
];

$payload = (array) JWT::decode($encoded, $this->secret, [$this->algoritm]) + $defaults;

$claims = array_diff_key($payload, $defaults);

$expireAt = new DateTimeImmutable('@'.$payload['exp']);
$issuedAt = new DateTimeImmutable('@'.$payload['iat']);

return new WebToken($payload['sub'], $issuedAt, $expireAt);
return new WebToken($payload['sub'], $issuedAt, $expireAt, $claims);
}
}
18 changes: 14 additions & 4 deletions src/Security/UsernamePasswordAuthenticator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Antenna\Coder;
use Antenna\WebToken;
use Antenna\ClaimsAwareInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
Expand Down Expand Up @@ -37,9 +38,9 @@ class UsernamePasswordAuthenticator implements
private $coder;

/**
* @param UserCheckerInterface $userChecker
* @param UserCheckerInterface $userChecker
* @param UserPasswordEncoderInterface $encoder
* @param Coder $coder
* @param Coder $coder
*/
public function __construct(
UserCheckerInterface $userChecker,
Expand Down Expand Up @@ -105,10 +106,19 @@ public function onAuthenticationFailure(Request $request, AuthenticationExceptio

public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$webToken = new WebToken($token->getUsername(), date_create_immutable(), date_create_immutable('+7 days'));
$claims = [];
$user = $token->getUser();

if ($user instanceof ClaimsAwareInterface) {
$claims = $user->getClaims();
}

$webToken = new WebToken(
$token->getUsername(), date_create_immutable(), date_create_immutable('+7 days'), $claims
);

return new JsonResponse([
'token' => $this->coder->encode($webToken)
'token' => $this->coder->encode($webToken),
]);
}
}
38 changes: 37 additions & 1 deletion src/WebToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,33 @@

use DateTimeInterface;

/**
* Represents part of a Json Web Token.
*
* Its main relation is "claims" and as such, the methods for interacting
* with these follow the Symfony Convention http://symfony.com/doc/current/contributing/code/conventions.html#method-names
*
* Not this is an immutable class.
*/
class WebToken
{
private $subject;
private $issuedAt;
private $expireAt;
private $claims = [];

public function __construct($subject, DateTimeInterface $issuedAt, DateTimeInterface $expireAt)
/**
* @param string $subject The sub (subject) claim identifies the principal that is the subject of the JWT.
* @param DateTimeInterface $issuedAt The iat (issued at) claim identifies the time at which the JWT was issued.
* @param DateTimeInterface $expireAt The exp (expiration time) claim identifies the expiration time.
* @param [string]mixed Additional claims that is not required.
*/
public function __construct($subject, DateTimeInterface $issuedAt, DateTimeInterface $expireAt, $claims = [])
{
$this->subject = $subject;
$this->issuedAt = $issuedAt;
$this->expireAt = $expireAt;
$this->claims = $claims;
}

public function getSubject()
Expand All @@ -31,4 +47,24 @@ public function getExpireAt()
{
return $this->expireAt;
}

public function all()
{
return $this->claims;
}

public function keys()
{
return array_keys($this->claims);
}

public function has($claim)
{
return array_key_exists($claim, $this->claims);
}

public function get($claim, $default = null)
{
return $this->has($claim) ? $this->claims[$claim] : $default;
}
}
31 changes: 26 additions & 5 deletions tests/CoderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,41 @@

class CoderTest extends \PHPUnit_Framework_TestCase
{
public function testCoding()
public function setUp()
{
$coder = new Coder('shared_secret');
$this->coder = new Coder('shared_secret');
}

public function testCoding()
{
$utc = new \DateTimeZone('UTC');
$issuedAt = date_create_immutable('now');
$expireAt = date_create_immutable('+1 year');

$webToken = $coder->decode($coder->encode(
new WebToken('my_subject', $issuedAt, $expireAt)
));
$webToken = $this->coder->decode(
$this->coder->encode(
new WebToken('my_subject', $issuedAt, $expireAt)
)
);

$this->assertEquals('my_subject', $webToken->getSubject());
$this->assertEquals($issuedAt->getTimestamp(), $webToken->getIssuedAt()->getTimestamp());
$this->assertEquals($expireAt->getTimestamp(), $webToken->getExpireAt()->getTimestamp());
}

public function testClaims()
{
$claims = [
'administrator' => 1,
'roles' => ['ROLE_USER', 'ROLE_SUPER_ADMIN'],
];

$webToken = $this->coder->decode(
$this->coder->encode(
new WebToken('my_subject', date_create(), date_create('+1 year'), $claims)
)
);

$this->assertEquals($claims, $webToken->all());
}
}

0 comments on commit ba80954

Please sign in to comment.