Jwt::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Jasny\Auth\Session;
6
7
use DateInterval;
8
use DateTime;
9
use DateTimeImmutable;
10
use DateTimeInterface;
11
use DateTimeZone;
12
use Jasny\Auth\Session\Jwt\Cookie;
13
use Jasny\Auth\Session\Jwt\CookieInterface;
14
use Jasny\Immutable;
15
use Lcobucci\JWT\Configuration;
16
use Lcobucci\JWT\Token;
17
use Lcobucci\JWT\UnencryptedToken;
18
19
/**
20
 * Use JSON Web Token and JSON Web Signature (RFC 7519) to store auth session info.
21
 *
22
 * @see https://github.com/lcobucci/jwt
23
 */
24
class Jwt implements SessionInterface
25
{
26
    use Immutable\With;
27
28
    protected Configuration $jwtConfig;
29
30
    protected int $ttl = 24 * 3600;
31
    protected CookieInterface $cookie;
32
33
    /**
34
     * JWT constructor.
35
     */
36 7
    public function __construct(Configuration $jwtConfig)
37
    {
38 7
        $this->jwtConfig = $jwtConfig;
39 7
        $this->cookie = new Cookie('jwt');
40
    }
41
42
    /**
43
     * Get a copy with a different TTL for expiry.
44
     */
45 1
    public function withTtl(int $seconds): self
46
    {
47 1
        return $this->withProperty('ttl', $seconds);
48
    }
49
50
    /**
51
     * Get a copy with custom cookie handler.
52
     */
53 7
    public function withCookie(CookieInterface $cookie): self
54
    {
55 7
        return $this->withProperty('cookie', $cookie);
56
    }
57
58
    /**
59
     * @inheritDoc
60
     */
61 4
    public function getInfo(): array
62
    {
63 4
        $jwt = $this->cookie->get();
64
65
        /** @var (Token&UnencryptedToken)|null $token */
66 4
        $token = $jwt !== null && $jwt !== ''
67 3
            ? $this->jwtConfig->parser()->parse($jwt)
68 1
            : null;
69
70 4
        $constraints = $this->jwtConfig->validationConstraints();
71
72 4
        if ($token === null ||
73 4
            ($constraints !== [] && !$this->jwtConfig->validator()->validate($token, ...$constraints))
74
        ) {
75 2
            return ['user' => null, 'context' => null, 'checksum' => null, 'timestamp' => null];
76
        }
77
78 2
        $timestamp = $token->headers()->get('iat');
79 2
        if (is_array($timestamp)) {
80 1
            $timestamp = new DateTimeImmutable($timestamp['date'], new DateTimeZone($timestamp['timezone']));
81
        }
82
83 2
        return [
84 2
            'user' => $token->claims()->get('user'),
85 2
            'context' => $token->claims()->get('context'),
86 2
            'checksum' => $token->claims()->get('checksum'),
87 2
            'timestamp' => $timestamp,
88 2
        ];
89
    }
90
91
    /**
92
     * @inheritDoc
93
     */
94 2
    public function persist(mixed $userId, mixed $contextId, ?string $checksum, ?DateTimeInterface $timestamp): void
95
    {
96 2
        if ($timestamp instanceof DateTime) {
97 2
            $timestamp = DateTimeImmutable::createFromMutable($timestamp);
98
        }
99
        /** @var DateTimeImmutable|null $timestamp */
100 2
        $time = $timestamp ?? new DateTimeImmutable();
101 2
        $expire = $time->add(new DateInterval("PT{$this->ttl}S"));
102
103 2
        $builder = clone $this->jwtConfig->builder()
104 2
            ->withClaim('user', $userId)
105 2
            ->withClaim('context', $contextId)
106 2
            ->withClaim('checksum', $checksum)
107 2
            ->issuedAt($time)
108 2
            ->expiresAt($expire);
109
110 2
        if ($timestamp !== null) {
111 2
            $builder = $builder->withHeader('iat', $timestamp);
112
        }
113
114 2
        $this->cookie->set(
115 2
            $builder->getToken($this->jwtConfig->signer(), $this->jwtConfig->signingKey())->toString(),
116 2
            $expire->getTimestamp()
117 2
        );
118
    }
119
120
    /**
121
     * @inheritDoc
122
     */
123 1
    public function clear(): void
124
    {
125 1
        $this->cookie->clear();
126
    }
127
}
128