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

Create new validation API #129

Merged
merged 11 commits into from
Jan 7, 2017
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ vendor
phpunit.xml
composer.lock
humbuglog.txt
coverage
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"autoload-dev": {
"psr-4": {
"Lcobucci\\JWT\\": [
"test/_keys",
"test/unit",
"test/functional"
]
Expand Down
241 changes: 17 additions & 224 deletions src/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,278 +10,71 @@
namespace Lcobucci\JWT;

use BadMethodCallException;
use Lcobucci\Jose\Parsing;
use Lcobucci\JWT\Claim\Factory as ClaimFactory;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Token\Plain;

/**
* This class makes easier the token creation process
*
* @author Luís Otávio Cobucci Oblonczyk <[email protected]>
* @since 0.1.0
* @since 4.0.0
*/
class Builder
interface Builder
{
/**
* The token header
*
* @var array
*/
private $headers;

/**
* The token claim set
*
* @var array
*/
private $claims;

/**
* The token signature
*
* @var Signature
*/
private $signature;

/**
* The data encoder
*
* @var Parsing\Encoder
*/
private $encoder;

/**
* The factory of claims
*
* @var ClaimFactory
*/
private $claimFactory;

/**
* Initializes a new builder
*
* @param Parsing\Encoder $encoder
* @param ClaimFactory $claimFactory
* Appends a new audience
*/
public function __construct(
Parsing\Encoder $encoder,
ClaimFactory $claimFactory
) {
$this->encoder = $encoder;
$this->claimFactory = $claimFactory;
$this->headers = ['typ'=> 'JWT', 'alg' => 'none'];
$this->claims = [];
}

/**
* Configures the audience
*
* @param string|array $audience
* @param bool $addHeader
*
* @return Builder
*/
public function canOnlyBeUsedBy(string $audience, bool $addHeader = false): Builder
{
$audiences = isset($this->claims['aud'])
? array_merge($this->claims['aud']->getValue(), [$audience])
: [$audience];

return $this->setRegisteredClaim(
'aud',
array_values(array_map('strval', $audiences)),
$addHeader
);
}
public function canOnlyBeUsedBy(string $audience, bool $addHeader = false): Builder;

Choose a reason for hiding this comment

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

belongsTo?

Copy link
Owner Author

Choose a reason for hiding this comment

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

Not really, audience is about who can consume the token.
I wouldn't change it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Docblock specifies "appends", method name suggests "only". :confus:

Copy link
Owner Author

Choose a reason for hiding this comment

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

Those names were defined on #113 we're just extracting an interface. Let's define on #150 and change them on a different PR.


/**
* Configures the expiration time
*
* @param int $expiration
* @param bool $addHeader
*
* @return Builder
*/
public function expiresAt(int $expiration, bool $addHeader = false): Builder
{
return $this->setRegisteredClaim('exp', $expiration, $addHeader);
}
public function expiresAt(int $expiration, bool $addHeader = false): Builder;

/**
* Configures the token id
*
* @param string $id
* @param boolean $addHeader
*
* @return Builder
*/
public function identifiedBy(string $id, bool $addHeader = false): Builder
{
return $this->setRegisteredClaim('jti', $id, $addHeader);
}
public function identifiedBy(string $id, bool $addHeader = false): Builder;

/**
* Configures the time that the token was issued
*
* @param int $issuedAt
* @param bool $addHeader
*
* @return Builder
*/
public function issuedAt(int $issuedAt, bool $addHeader = false): Builder
{
return $this->setRegisteredClaim('iat', (int) $issuedAt, $addHeader);
}
public function issuedAt(int $issuedAt, bool $addHeader = false): Builder;

/**
* Configures the issuer
*
* @param string $issuer
* @param bool $addHeader
*
* @return Builder
*/
public function issuedBy(string $issuer, bool $addHeader = false): Builder
{
return $this->setRegisteredClaim('iss', $issuer, $addHeader);
}
public function issuedBy(string $issuer, bool $addHeader = false): Builder;

/**
* Configures the time before which the token cannot be accepted
*
* @param int $notBefore
* @param bool $addHeader
*
* @return Builder
*/
public function canOnlyBeUsedAfter(int $notBefore, bool $addHeader = false): Builder
{
return $this->setRegisteredClaim('nbf', $notBefore, $addHeader);
}
public function canOnlyBeUsedAfter(int $notBefore, bool $addHeader = false): Builder;

/**
* Configures the subject
*
* @param string $subject
* @param bool $addHeader
*
* @return Builder
*/
public function relatedTo(string $subject, bool $addHeader = false): Builder
{
return $this->setRegisteredClaim('sub', $subject, $addHeader);
}

/**
* Configures a registed claim
*
* @param string $name
* @param mixed $value
* @param bool $addHeader
*
* @return Builder
*/
protected function setRegisteredClaim(string $name, $value, bool $addHeader): Builder
{
$this->with($name, $value);

if ($addHeader) {
$this->headers[$name] = $this->claims[$name];
}

return $this;
}
public function relatedTo(string $subject, bool $addHeader = false): Builder;

/**
* Configures a header item
*
* @param string $name
* @param mixed $value
*
* @return Builder
*
* @throws BadMethodCallException When data has been already signed
*/
public function withHeader(string $name, $value): Builder
{
if ($this->signature) {
throw new BadMethodCallException('You must unsign before make changes');
}

$this->headers[$name] = $this->claimFactory->create($name, $value);

return $this;
}
public function withHeader(string $name, $value): Builder;

/**
* Configures a claim item
*
* @param string $name
* @param mixed $value
*
* @return Builder
*
* @throws BadMethodCallException When data has been already signed
*/
public function with(string $name, $value): Builder
{
if ($this->signature) {
throw new BadMethodCallException('You must unsign before making changes');
}

$this->claims[$name] = $this->claimFactory->create($name, $value);

return $this;
}

/**
* Signs the data
*
* @param Signer $signer
* @param Key $key
*
* @return Builder
*/
public function sign(Signer $signer, Key $key): Builder
{
$signer->modifyHeader($this->headers);

$this->signature = $signer->sign(
$this->getToken()->getPayload(),
$key
);

return $this;
}
public function with(string $name, $value): Builder;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Only with() looks a little bit vague to me, wouldn't be better to call it withClaim instead?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Indeed, change needed

Copy link
Owner Author

Choose a reason for hiding this comment

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

Those names were defined on #113 we're just extracting an interface. Let's define on #150 and change them on a different PR.


/**
* Removes the signature from the builder
*
* @return Builder
* Returns a signed token to be used
*/
public function unsign(): Builder
{
$this->signature = null;

return $this;
}
public function getToken(Signer $signer, Key $key): Plain;

/**
* Returns the resultant token
*
* @return Token
* Returns an unsecured token (not recommended)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Wouldn't it make more sense to use the previous API with a no-op signer?

Copy link
Owner Author

Choose a reason for hiding this comment

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

What you mean here?

Copy link
Collaborator

@Ocramius Ocramius Jan 7, 2017

Choose a reason for hiding this comment

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

Have always a token, let a NoneSignerdeal with it (validates only empty signature, generates only empty signature)

Copy link
Owner Author

Choose a reason for hiding this comment

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

That's a good idea, it would simplify things indeed.

Copy link
Owner Author

Choose a reason for hiding this comment

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

But it can be done in a different PR... I really want to have this merged instead of adding things to the stack

Copy link
Owner Author

Choose a reason for hiding this comment

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

#152 is there for that.

*/
public function getToken(): Token
{
$payload = [
$this->encoder->base64UrlEncode($this->encoder->jsonEncode($this->headers)),
$this->encoder->base64UrlEncode($this->encoder->jsonEncode($this->claims))
];

if ($this->signature !== null) {
$payload[] = $this->encoder->base64UrlEncode((string) $this->signature);
}

return new Token($this->headers, $this->claims, $this->signature, $payload);
}
public function getUnsecuredToken(): Plain;
}
42 changes: 0 additions & 42 deletions src/Claim.php

This file was deleted.

Loading