vendor/lcobucci/jwt/src/Builder.php line 203

Open in your IDE?
  1. <?php
  2. /**
  3.  * This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
  4.  *
  5.  * @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
  6.  */
  7. namespace Lcobucci\JWT;
  8. use DateTimeImmutable;
  9. use Lcobucci\JWT\Claim\Factory as ClaimFactory;
  10. use Lcobucci\JWT\Parsing\Encoder;
  11. use Lcobucci\JWT\Signer\Key;
  12. use Lcobucci\JWT\Token\DataSet;
  13. use Lcobucci\JWT\Token\RegisteredClaimGiven;
  14. use Lcobucci\JWT\Token\RegisteredClaims;
  15. use function array_diff;
  16. use function array_filter;
  17. use function array_key_exists;
  18. use function array_merge;
  19. use function array_shift;
  20. use function count;
  21. use function current;
  22. use function in_array;
  23. use function is_array;
  24. use function is_bool;
  25. use function trigger_error;
  26. use const E_USER_DEPRECATED;
  27. /**
  28.  * This class makes easier the token creation process
  29.  *
  30.  * @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
  31.  * @since 0.1.0
  32.  */
  33. class Builder
  34. {
  35.     /**
  36.      * The token header
  37.      *
  38.      * @var array
  39.      */
  40.     private $headers = ['typ'=> 'JWT''alg' => 'none'];
  41.     /**
  42.      * The token claim set
  43.      *
  44.      * @var array
  45.      */
  46.     private $claims = [];
  47.     /**
  48.      * The data encoder
  49.      *
  50.      * @var Encoder
  51.      */
  52.     private $encoder;
  53.     /**
  54.      * The factory of claims
  55.      *
  56.      * @var ClaimFactory
  57.      */
  58.     private $claimFactory;
  59.     /**
  60.      * @var Signer|null
  61.      */
  62.     private $signer;
  63.     /**
  64.      * @var Key|null
  65.      */
  66.     private $key;
  67.     /**
  68.      * Initializes a new builder
  69.      *
  70.      * @param Encoder $encoder
  71.      * @param ClaimFactory $claimFactory
  72.      */
  73.     public function __construct(
  74.         Encoder $encoder null,
  75.         ClaimFactory $claimFactory null
  76.     ) {
  77.         $this->encoder $encoder ?: new Encoder();
  78.         $this->claimFactory $claimFactory ?: new ClaimFactory();
  79.     }
  80.     /**
  81.      * Configures the audience
  82.      *
  83.      * @deprecated This method has been wrongly added and doesn't exist on v4
  84.      * @see Builder::permittedFor()
  85.      *
  86.      * @param string $audience
  87.      * @param bool $replicateAsHeader
  88.      *
  89.      * @return Builder
  90.      */
  91.     public function canOnlyBeUsedBy($audience$replicateAsHeader false)
  92.     {
  93.         return $this->permittedFor($audience$replicateAsHeader);
  94.     }
  95.     /**
  96.      * Configures the audience
  97.      *
  98.      * @param list<string|bool> $audiences A list of audiences and, optionally, the instruction to replicate as header
  99.      *
  100.      * @return Builder
  101.      */
  102.     public function permittedFor(...$audiences)
  103.     {
  104.         $claim RegisteredClaims::AUDIENCE;
  105.         $replicateAsHeader false;
  106.         if ($audiences !== [] && is_bool($audiences[count($audiences) - 1])) {
  107.             $replicateAsHeader array_pop($audiences);
  108.         }
  109.         $audiences array_filter($audiences'is_string');
  110.         $configured array_key_exists($claim$this->claims) ? $this->claims[$claim] : [];
  111.         $toAppend   array_diff($audiences$configured);
  112.         return $this->setRegisteredClaim($claimarray_merge($configured$toAppend), $replicateAsHeader);
  113.     }
  114.     /**
  115.      * Configures the audience
  116.      *
  117.      * @deprecated This method will be removed on v4
  118.      * @see Builder::permittedFor()
  119.      *
  120.      * @param string $audience
  121.      * @param boolean $replicateAsHeader
  122.      *
  123.      * @return Builder
  124.      */
  125.     public function setAudience($audience$replicateAsHeader false)
  126.     {
  127.         return $this->permittedFor($audience$replicateAsHeader);
  128.     }
  129.     /**
  130.      * Configures the expiration time
  131.      *
  132.      * @param int|DateTimeImmutable $expiration
  133.      * @param boolean $replicateAsHeader
  134.      *
  135.      * @return Builder
  136.      */
  137.     public function expiresAt($expiration$replicateAsHeader false)
  138.     {
  139.         return $this->setRegisteredClaim('exp'$this->convertToDate($expiration), $replicateAsHeader);
  140.     }
  141.     /**
  142.      * @param int|DateTimeImmutable $value
  143.      *
  144.      * @return DateTimeImmutable
  145.      */
  146.     private function convertToDate($value)
  147.     {
  148.         if (! $value instanceof DateTimeImmutable) {
  149.             trigger_error('Using integers for registered date claims is deprecated, please use DateTimeImmutable objects instead.'E_USER_DEPRECATED);
  150.             return new DateTimeImmutable('@' $value);
  151.         }
  152.         return $value;
  153.     }
  154.     /**
  155.      * Configures the expiration time
  156.      *
  157.      * @deprecated This method will be removed on v4
  158.      * @see Builder::expiresAt()
  159.      *
  160.      * @param int|DateTimeImmutable $expiration
  161.      * @param boolean $replicateAsHeader
  162.      *
  163.      * @return Builder
  164.      */
  165.     public function setExpiration($expiration$replicateAsHeader false)
  166.     {
  167.         return $this->expiresAt($expiration$replicateAsHeader);
  168.     }
  169.     /**
  170.      * Configures the token id
  171.      *
  172.      * @param string $id
  173.      * @param boolean $replicateAsHeader
  174.      *
  175.      * @return Builder
  176.      */
  177.     public function identifiedBy($id$replicateAsHeader false)
  178.     {
  179.         return $this->setRegisteredClaim('jti', (string) $id$replicateAsHeader);
  180.     }
  181.     /**
  182.      * Configures the token id
  183.      *
  184.      * @deprecated This method will be removed on v4
  185.      * @see Builder::identifiedBy()
  186.      *
  187.      * @param string $id
  188.      * @param boolean $replicateAsHeader
  189.      *
  190.      * @return Builder
  191.      */
  192.     public function setId($id$replicateAsHeader false)
  193.     {
  194.         return $this->identifiedBy($id$replicateAsHeader);
  195.     }
  196.     /**
  197.      * Configures the time that the token was issued
  198.      *
  199.      * @param int|DateTimeImmutable $issuedAt
  200.      * @param boolean $replicateAsHeader
  201.      *
  202.      * @return Builder
  203.      */
  204.     public function issuedAt($issuedAt$replicateAsHeader false)
  205.     {
  206.         return $this->setRegisteredClaim('iat'$this->convertToDate($issuedAt), $replicateAsHeader);
  207.     }
  208.     /**
  209.      * Configures the time that the token was issued
  210.      *
  211.      * @deprecated This method will be removed on v4
  212.      * @see Builder::issuedAt()
  213.      *
  214.      * @param int|DateTimeImmutable $issuedAt
  215.      * @param boolean $replicateAsHeader
  216.      *
  217.      * @return Builder
  218.      */
  219.     public function setIssuedAt($issuedAt$replicateAsHeader false)
  220.     {
  221.         return $this->issuedAt($issuedAt$replicateAsHeader);
  222.     }
  223.     /**
  224.      * Configures the issuer
  225.      *
  226.      * @param string $issuer
  227.      * @param boolean $replicateAsHeader
  228.      *
  229.      * @return Builder
  230.      */
  231.     public function issuedBy($issuer$replicateAsHeader false)
  232.     {
  233.         return $this->setRegisteredClaim('iss', (string) $issuer$replicateAsHeader);
  234.     }
  235.     /**
  236.      * Configures the issuer
  237.      *
  238.      * @deprecated This method will be removed on v4
  239.      * @see Builder::issuedBy()
  240.      *
  241.      * @param string $issuer
  242.      * @param boolean $replicateAsHeader
  243.      *
  244.      * @return Builder
  245.      */
  246.     public function setIssuer($issuer$replicateAsHeader false)
  247.     {
  248.         return $this->issuedBy($issuer$replicateAsHeader);
  249.     }
  250.     /**
  251.      * Configures the time before which the token cannot be accepted
  252.      *
  253.      * @param int|DateTimeImmutable $notBefore
  254.      * @param boolean $replicateAsHeader
  255.      *
  256.      * @return Builder
  257.      */
  258.     public function canOnlyBeUsedAfter($notBefore$replicateAsHeader false)
  259.     {
  260.         return $this->setRegisteredClaim('nbf'$this->convertToDate($notBefore), $replicateAsHeader);
  261.     }
  262.     /**
  263.      * Configures the time before which the token cannot be accepted
  264.      *
  265.      * @deprecated This method will be removed on v4
  266.      * @see Builder::canOnlyBeUsedAfter()
  267.      *
  268.      * @param int|DateTimeImmutable $notBefore
  269.      * @param boolean $replicateAsHeader
  270.      *
  271.      * @return Builder
  272.      */
  273.     public function setNotBefore($notBefore$replicateAsHeader false)
  274.     {
  275.         return $this->canOnlyBeUsedAfter($notBefore$replicateAsHeader);
  276.     }
  277.     /**
  278.      * Configures the subject
  279.      *
  280.      * @param string $subject
  281.      * @param boolean $replicateAsHeader
  282.      *
  283.      * @return Builder
  284.      */
  285.     public function relatedTo($subject$replicateAsHeader false)
  286.     {
  287.         return $this->setRegisteredClaim('sub', (string) $subject$replicateAsHeader);
  288.     }
  289.     /**
  290.      * Configures the subject
  291.      *
  292.      * @deprecated This method will be removed on v4
  293.      * @see Builder::relatedTo()
  294.      *
  295.      * @param string $subject
  296.      * @param boolean $replicateAsHeader
  297.      *
  298.      * @return Builder
  299.      */
  300.     public function setSubject($subject$replicateAsHeader false)
  301.     {
  302.         return $this->relatedTo($subject$replicateAsHeader);
  303.     }
  304.     /**
  305.      * Configures a registered claim
  306.      *
  307.      * @param string $name
  308.      * @param mixed $value
  309.      * @param boolean $replicate
  310.      *
  311.      * @return Builder
  312.      */
  313.     protected function setRegisteredClaim($name$value$replicate)
  314.     {
  315.         $this->configureClaim($name$value);
  316.         if ($replicate) {
  317.             trigger_error('Replicating claims as headers is deprecated and will removed from v4.0. Please manually set the header if you need it replicated.'E_USER_DEPRECATED);
  318.             $this->headers[$name] = $value;
  319.         }
  320.         return $this;
  321.     }
  322.     /**
  323.      * Configures a header item
  324.      *
  325.      * @param string $name
  326.      * @param mixed $value
  327.      *
  328.      * @return Builder
  329.      */
  330.     public function withHeader($name$value)
  331.     {
  332.         $this->headers[(string) $name] = $value;
  333.         return $this;
  334.     }
  335.     /**
  336.      * Configures a header item
  337.      *
  338.      * @deprecated This method will be removed on v4
  339.      * @see Builder::withHeader()
  340.      *
  341.      * @param string $name
  342.      * @param mixed $value
  343.      *
  344.      * @return Builder
  345.      */
  346.     public function setHeader($name$value)
  347.     {
  348.         return $this->withHeader($name$value);
  349.     }
  350.     /**
  351.      * Configures a claim item
  352.      *
  353.      * @deprecated This method has been wrongly added and doesn't exist on v4
  354.      * @see Builder::withClaim()
  355.      *
  356.      * @param string $name
  357.      * @param mixed $value
  358.      *
  359.      * @return Builder
  360.      */
  361.     public function with($name$value)
  362.     {
  363.         return $this->withClaim($name$value);
  364.     }
  365.     /**
  366.      * @param string $name
  367.      * @param mixed $value
  368.      *
  369.      * @return Builder
  370.      */
  371.     private function configureClaim($name$value)
  372.     {
  373.         $this->claims[(string) $name] = $value;
  374.         return $this;
  375.     }
  376.     /**
  377.      * Configures a claim item
  378.      *
  379.      * @param string $name
  380.      * @param mixed $value
  381.      *
  382.      * @return Builder
  383.      *
  384.      * @throws RegisteredClaimGiven
  385.      */
  386.     public function withClaim($name$value)
  387.     {
  388.         if (in_array($nameRegisteredClaims::ALLtrue)) {
  389.             trigger_error('The use of the method "withClaim" is deprecated for registered claims. Please use dedicated method instead.'E_USER_DEPRECATED);
  390.         }
  391.         return $this->forwardCallToCorrectClaimMethod($name$value);
  392.     }
  393.     private function forwardCallToCorrectClaimMethod($name$value)
  394.     {
  395.         switch ($name) {
  396.             case RegisteredClaims::ID:
  397.                 return $this->identifiedBy($value);
  398.             case RegisteredClaims::EXPIRATION_TIME:
  399.                 return $this->expiresAt($value);
  400.             case RegisteredClaims::NOT_BEFORE:
  401.                 return $this->canOnlyBeUsedAfter($value);
  402.             case RegisteredClaims::ISSUED_AT:
  403.                 return $this->issuedAt($value);
  404.             case RegisteredClaims::ISSUER:
  405.                 return $this->issuedBy($value);
  406.             case RegisteredClaims::AUDIENCE:
  407.                 return $this->permittedFor($value);
  408.             default:
  409.                 return $this->configureClaim($name$value);
  410.         }
  411.     }
  412.     /**
  413.      * Configures a claim item
  414.      *
  415.      * @deprecated This method will be removed on v4
  416.      * @see Builder::withClaim()
  417.      *
  418.      * @param string $name
  419.      * @param mixed $value
  420.      *
  421.      * @return Builder
  422.      */
  423.     public function set($name$value)
  424.     {
  425.         return $this->forwardCallToCorrectClaimMethod($name$value);
  426.     }
  427.     /**
  428.      * Signs the data
  429.      *
  430.      * @deprecated This method will be removed on v4
  431.      * @see Builder::getToken()
  432.      *
  433.      * @param Signer $signer
  434.      * @param Key|string $key
  435.      *
  436.      * @return Builder
  437.      */
  438.     public function sign(Signer $signer$key)
  439.     {
  440.         if (! $key instanceof Key) {
  441.             trigger_error('Implicit conversion of keys from strings is deprecated. Please use InMemory or LocalFileReference classes.'E_USER_DEPRECATED);
  442.             $key = new Key($key);
  443.         }
  444.         $this->signer $signer;
  445.         $this->key $key;
  446.         return $this;
  447.     }
  448.     /**
  449.      * Removes the signature from the builder
  450.      *
  451.      * @deprecated This method will be removed on v4
  452.      * @see Builder::getToken()
  453.      *
  454.      * @return Builder
  455.      */
  456.     public function unsign()
  457.     {
  458.         $this->signer null;
  459.         $this->key null;
  460.         return $this;
  461.     }
  462.     /**
  463.      * Returns the resultant token
  464.      *
  465.      * @return Token
  466.      */
  467.     public function getToken(Signer $signer nullKey $key null)
  468.     {
  469.         if ($signer === null || $key === null) {
  470.             trigger_error('Not specifying the signer and key to Builder#getToken() is deprecated. Please move the arguments from Builder#sign() to Builder#getToken().'E_USER_DEPRECATED);
  471.         }
  472.         $signer $signer ?: $this->signer;
  473.         $key $key ?: $this->key;
  474.         if ($signer instanceof Signer) {
  475.             $signer->modifyHeader($this->headers);
  476.         }
  477.         $headers = new DataSet(
  478.             $this->headers,
  479.             $this->encoder->base64UrlEncode($this->encoder->jsonEncode($this->convertItems($this->headers)))
  480.         );
  481.         $claims = new DataSet(
  482.             $this->claims,
  483.             $this->encoder->base64UrlEncode($this->encoder->jsonEncode($this->convertItems($this->claims)))
  484.         );
  485.         return new Token(
  486.             $headers,
  487.             $claims,
  488.             $this->createSignature($headers->toString() . '.' $claims->toString(), $signer$key),
  489.             [''''],
  490.             $this->claimFactory
  491.         );
  492.     }
  493.     /**
  494.      * @param array<string, mixed> $items
  495.      *
  496.      * @return array<string, mixed>
  497.      */
  498.     private function convertItems(array $items)
  499.     {
  500.         foreach (RegisteredClaims::DATE_CLAIMS as $name) {
  501.             if (! array_key_exists($name$items) || ! $items[$name] instanceof DateTimeImmutable) {
  502.                 continue;
  503.             }
  504.             $items[$name] = $items[$name]->getTimestamp();
  505.         }
  506.         $audience RegisteredClaims::AUDIENCE;
  507.         if (array_key_exists($audience$items) && is_array($items[$audience]) && count($items[$audience]) === 1) {
  508.             $items[$audience] = current($items[$audience]);
  509.         }
  510.         return $items;
  511.     }
  512.     /**
  513.      * @param string $payload
  514.      *
  515.      * @return Signature
  516.      */
  517.     private function createSignature($payloadSigner $signer nullKey $key null)
  518.     {
  519.         if ($signer === null || $key === null) {
  520.             return Signature::fromEmptyData();
  521.         }
  522.         $hash $signer->sign($payload$key)->hash();
  523.         return new Signature($hash$this->encoder->base64UrlEncode($hash));
  524.     }
  525. }