P4BGroup/ldap-jwt-authentication

View on GitHub
src/Claims.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php declare(strict_types=1);

namespace P4BGroup\Authentication;

use DateTimeInterface;
use JsonSerializable;
use stdClass;

class Claims implements JsonSerializable
{
    /**
     * This claim is application specific.
     * Example of usage: host of the service generating the claim, server ip, etc
     *
     * @var string
     * @see https://tools.ietf.org/html/rfc7519#section-4.1.1
     */
    private $issuer;

    /**
     * This claim is application specific
     * Example of usage: description of the token usage scope (authentication, refresh, acl, etc)
     *
     * @var string
     * @see https://tools.ietf.org/html/rfc7519#section-4.1.2
     */
    private $subject;

    /**
     * This claim is application specific
     * Example of usage: a hwid of the client that generated the token, to be used for theft prevention purposes
     *
     * @var string
     * @see https://tools.ietf.org/html/rfc7519#section-4.1.3
     */
    private $audience;

    /**
     * @var int
     * @see https://tools.ietf.org/html/rfc7519#section-4.1.4
     */
    private $expirationTime;

    /**
     * @var int
     * @see https://tools.ietf.org/html/rfc7519#section-4.1.5
     */
    private $notBefore;

    /**
     * @var int
     * @see https://tools.ietf.org/html/rfc7519#section-4.1.6
     */
    private $issuedAt;

    /**
     * Application specific - unique identifier of a token.
     * Example of usage: An id, a hash, etc
     *
     * @var string
     * @see https://tools.ietf.org/html/rfc7519#section-4.1.7
     */
    private $jwtId;

    /**
     * Custom claims that are not part of the RFC should be grouped and isolated in a single claim.
     * This will keep a fixed structure of the JWT body and will allow us clients better control on de-serialization
     * Example: $data can have multiple structures depending on the "subject" claim
     *
     * @var array
     */
    private $data = [];

    /**
     * Sets the audience to "authentication"
     */
    public const SUBJECT_AUTHENTICATION = 'authentication';

    /**
     * Sets the audience to "refresh"
     */
    public const SUBJECT_REFRESH = 'refresh';

    /**
     * @param stdClass|null $claims
     */
    public function __construct(?stdClass $claims = null)
    {
        if ($claims instanceof stdClass) {
            $data = ($claims->data ?? null) instanceof stdClass ? $claims->data : new stdClass();
            $claims->data = json_decode(json_encode($data), true);

            foreach ($this->claimsMap() as $claim => $property) {
                $this->{$property} = $claims->{$claim} ?? $this->{$property};
            }
        }
    }

    /**
     * @return string[]
     */
    private function claimsMap(): array
    {
        return [
            'iss' => 'issuer',
            'sub' => 'subject',
            'aud' => 'audience',
            'exp' => 'expirationTime',
            'nbf' => 'notBefore',
            'iat' => 'issuedAt',
            'jti' => 'jwtId',
            // claims extra data
            'data' => 'data',
        ];
    }

    /**
     * @return array
     */
    public function toArray(): array
    {
        $claims = [];
        foreach ($this->claimsMap() as $claim => $property) {
            $value = $this->{$property} ?? null;
            // skip empty values
            if (in_array($value, [null, []], true)) {
                continue;
            }

            $claims[$claim] = $value;
        }

        return $claims;
    }

    /**
     * Specify data which should be serialized to JSON
     *
     * @link https://php.net/manual/en/jsonserializable.jsonserialize.php
     * @return mixed data which can be serialized by <b>json_encode</b>,
     * which is a value of any type other than a resource.
     * @since 5.4.0
     */
    #[\ReturnTypeWillChange]
    public function jsonSerialize()
    {
        return $this->toArray();
    }

    /**
     * @return string
     */
    public function getIssuer(): string
    {
        return $this->issuer;
    }

    /**
     * @return string
     */
    public function getSubject(): string
    {
        return $this->subject;
    }

    /**
     * @return string
     */
    public function getAudience(): string
    {
        return $this->audience;
    }

    /**
     * @return int
     */
    public function getExpirationTime(): int
    {
        return $this->expirationTime;
    }

    /**
     * @return int
     */
    public function getNotBefore(): int
    {
        return $this->notBefore;
    }

    /**
     * @return int
     */
    public function getIssuedAt(): int
    {
        return $this->issuedAt;
    }

    /**
     * @return string
     */
    public function getJwtId(): string
    {
        return $this->jwtId;
    }

    /**
     * @return array
     */
    public function getData(): array
    {
        return $this->data;
    }

    /**
     * @param string $issuer
     *
     * @return Claims
     */
    public function setIssuer(string $issuer): Claims
    {
        $this->issuer = $issuer;
        return $this;
    }

    /**
     * @param string $subject
     *
     * @return Claims
     */
    public function setSubject(string $subject): Claims
    {
        $this->subject = $subject;
        return $this;
    }

    /**
     * @param string $audience
     *
     * @return Claims
     */
    public function setAudience(string $audience): Claims
    {
        $this->audience = $audience;
        return $this;
    }

    /**
     * @param DateTimeInterface $dateTime
     *
     * @return Claims
     */
    public function setExpirationTime(DateTimeInterface $dateTime): Claims
    {
        $this->expirationTime = $dateTime->getTimestamp();
        return $this;
    }

    /**
     * @param int $notBefore
     *
     * @return Claims
     */
    public function setNotBefore(int $notBefore): Claims
    {
        $this->notBefore = $notBefore;
        return $this;
    }

    /**
     * @param int $issuedAt
     *
     * @return Claims
     */
    public function setIssuedAt(int $issuedAt): Claims
    {
        $this->issuedAt = $issuedAt;
        return $this;
    }

    /**
     * @param string $jwtId
     *
     * @return Claims
     */
    public function setJwtId(string $jwtId): Claims
    {
        $this->jwtId = $jwtId;
        return $this;
    }

    /**
     * @param array $data
     *
     * @return Claims
     */
    public function setData(array $data): Claims
    {
        $this->data = $data;
        return $this;
    }
}