AbstractClaims::claimJWT()   B
last analyzed

Complexity

Conditions 8
Paths 8

Size

Total Lines 43
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 8.7515

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 22
nc 8
nop 2
dl 0
loc 43
ccs 17
cts 22
cp 0.7727
crap 8.7515
rs 8.4444
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace TMV\OpenIdClient\Claims;
6
7
use function array_diff_key;
8
use function array_flip;
9
use function array_key_exists;
10
use function count;
11
use function explode;
12
use Jose\Component\Core\AlgorithmManager;
13
use Jose\Component\Signature\Algorithm\RS256;
14
use Jose\Component\Signature\JWSVerifier;
15
use Jose\Component\Signature\Serializer\CompactSerializer;
16
use Jose\Component\Signature\Serializer\JWSSerializer;
17
use function json_decode;
18
use function sprintf;
19
use function TMV\OpenIdClient\base64url_decode;
20
use TMV\OpenIdClient\Client\ClientInterface as OpenIDClient;
21
use TMV\OpenIdClient\Exception\InvalidArgumentException;
22
use TMV\OpenIdClient\Exception\RuntimeException;
23
use TMV\OpenIdClient\Issuer\IssuerFactory;
24
use TMV\OpenIdClient\Issuer\IssuerFactoryInterface;
25
26
abstract class AbstractClaims
27
{
28
    /** @var AlgorithmManager */
29
    protected $algorithmManager;
30
31
    /** @var JWSVerifier */
32
    protected $JWSVerifier;
33
34
    /** @var IssuerFactoryInterface */
35
    protected $issuerFactory;
36
37
    /** @var JWSSerializer */
38
    protected $serializer;
39
40 8
    public function __construct(
41
        ?AlgorithmManager $algorithmManager = null,
42
        ?JWSVerifier $JWSVerifier = null,
43
        ?IssuerFactoryInterface $issuerFactory = null,
44
        ?JWSSerializer $serializer = null
45
    ) {
46 8
        $this->algorithmManager = $algorithmManager ?: new AlgorithmManager([new RS256()]);
47 8
        $this->JWSVerifier = $JWSVerifier ?: new JWSVerifier($this->algorithmManager);
48 8
        $this->issuerFactory = $issuerFactory ?: new IssuerFactory();
49 8
        $this->serializer = $serializer ?: new CompactSerializer();
50 8
    }
51
52 3
    protected function claimJWT(OpenIDClient $client, string $jwt): array
53
    {
54 3
        $issuer = $client->getIssuer();
55
56 3
        $header = json_decode(base64url_decode(explode('.', $jwt)[0] ?? '{}'), true);
57 3
        $payload = json_decode(base64url_decode(explode('.', $jwt)[1] ?? '{}'), true);
58
59
        /** @var null|string $alg */
60 3
        $alg = $header['alg'] ?? null;
61
        /** @var null|string $kid */
62 3
        $kid = $header['kid'] ?? null;
63
64 3
        if (null === $alg) {
65
            throw new InvalidArgumentException('Claim source is missing JWT header alg property');
66
        }
67
68 3
        if ('none' === $alg) {
69 2
            return $payload;
70
        }
71
72
        /** @var null|string $iss */
73 1
        $iss = $payload['iss'] ?? null;
74
75 1
        if (null === $iss || $iss === $issuer->getMetadata()->getIssuer()) {
76 1
            $jwks = $issuer->getJwks();
77
        } else {
78
            $discovered = $this->issuerFactory->fromUri($iss);
79
            $jwks = $discovered->getJwks();
80
        }
81
82 1
        $jws = $this->serializer->unserialize($jwt);
83
84 1
        $jwk = $jwks->selectKey('sig', $this->algorithmManager->get($alg), null !== $kid ? ['kid' => $kid] : []);
85
86 1
        if (null === $jwk) {
87
            throw new RuntimeException('Unable to get a key to verify claim source JWT');
88
        }
89
90 1
        if (false === $this->JWSVerifier->verifyWithKey($jws, $jwk, 0)) {
91
            throw new InvalidArgumentException('Invalid claim source JWT signature');
92
        }
93
94 1
        return $payload;
95
    }
96
97 4
    protected function assignClaims(array $claims, array $sourceNames, array $sources): array
98
    {
99 4
        foreach ($sourceNames as $claim => $inSource) {
100 4
            if (! array_key_exists($inSource, $sources)) {
101 1
                continue;
102
            }
103
104 3
            if (! $sources[$inSource][$claim]) {
105
                throw new RuntimeException(sprintf('Unable to find claim "%s" in source "%s"', $claim, $inSource));
106
            }
107
108 3
            $claims[$claim] = $sources[$inSource][$claim];
109 3
            $claims['_claim_names'] = array_diff_key($claims['_claim_names'] ?? [], array_flip([$claim]));
110
        }
111
112 4
        return $claims;
113
    }
114
115 4
    protected function cleanClaims(array $claims): array
116
    {
117 4
        if (array_key_exists('_claim_names', $claims) && 0 === count($claims['_claim_names'] ?? [])) {
118 3
            $claims = array_diff_key($claims, array_flip(['_claim_names']));
119
        }
120
121 4
        if (array_key_exists('_claim_sources', $claims) && 0 === count($claims['_claim_sources'] ?? [])) {
122 3
            $claims = array_diff_key($claims, array_flip(['_claim_sources']));
123
        }
124
125 4
        return $claims;
126
    }
127
}
128