AbstractProvider   A
last analyzed

Complexity

Total Complexity 9

Size/Duplication

Total Lines 111
Duplicated Lines 0 %

Test Coverage

Coverage 16.22%

Importance

Changes 7
Bugs 0 Features 0
Metric Value
wmc 9
eloc 34
c 7
b 0
f 0
dl 0
loc 111
ccs 6
cts 37
cp 0.1622
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A getJWKSet() 0 22 3
A getAuthUrlParameters() 0 13 1
A discover() 0 5 1
A parseToken() 0 17 3
A createAccessToken() 0 8 1
1
<?php
2
/**
3
 * SocialConnect project
4
 * @author: Patsura Dmitry https://github.com/ovr <[email protected]>
5
 */
6
declare(strict_types=1);
7
8
namespace SocialConnect\OpenIDConnect;
9
10
use SocialConnect\Common\Entity\User;
11
use SocialConnect\JWX\DecodeOptions;
12
use SocialConnect\JWX\JWKSet;
13
use SocialConnect\JWX\JWT;
14
use SocialConnect\Provider\AccessTokenInterface;
15
use SocialConnect\Provider\Exception\InvalidAccessToken;
16
use SocialConnect\Provider\Exception\InvalidResponse;
17
18
abstract class AbstractProvider extends \SocialConnect\OAuth2\AbstractProvider
19
{
20
    /**
21
     * @return array
22
     * @throws InvalidResponse
23
     * @throws \Psr\Http\Client\ClientExceptionInterface
24
     */
25
    public function discover(): array
26
    {
27
        return $this->hydrateResponse(
28
            $this->executeRequest(
29
                $this->httpStack->createRequest('GET', $this->getOpenIdUrl())
30
            )
31
        );
32
    }
33
34
    /**
35
     * @return JWKSet
36
     * @throws InvalidResponse
37
     * @throws \Psr\Http\Client\ClientExceptionInterface
38
     */
39
    public function getJWKSet(): JWKSet
40
    {
41
        $spec = $this->discover();
42
43
        if (!isset($spec['jwks_uri'])) {
44
            throw new \RuntimeException('Unknown jwks_uri inside OpenIDConnect specification');
45
        }
46
47
        $response = $this->executeRequest(
48
            $this->httpStack->createRequest('GET', $spec['jwks_uri'])
49
        );
50
51
        $result = $this->hydrateResponse($response);
52
53
        if (!isset($result['keys'])) {
54
            throw new InvalidResponse(
55
                'API response without "keys" key inside JSON',
56
                $response
57
            );
58
        }
59
60
        return new JWKSet($result);
61
    }
62
63
    /**
64
     * @return string
65
     */
66
    abstract public function getOpenIdUrl();
67
68
    /**
69
     * {@inheritdoc}
70
     */
71
    public function getAuthUrlParameters(): array
72
    {
73
        $parameters = parent::getAuthUrlParameters();
74
75
        // special parameters only required for OpenIDConnect
76
        $parameters['client_id'] = $this->consumer->getKey();
77
        $parameters['redirect_uri'] = $this->getRedirectUrl();
78
        $parameters['response_type'] = 'code';
79
        // Optional field...
80
        //$parameters['response_mode'] = 'form_post';
81
        $parameters['scope'] = 'openid';
82
83
        return $parameters;
84
    }
85
86
    /**
87
     * {@inheritdoc}
88
     */
89 4
    public function parseToken(string $body)
90
    {
91 4
        if (empty($body)) {
92 2
            throw new InvalidAccessToken('Provider response with empty body');
93
        }
94
95 2
        $result = json_decode($body, true);
96 2
        if ($result) {
97
            $token = new AccessToken($result);
98
            $token->setJwt(
99
                JWT::decode($result['id_token'], $this->getJWKSet(), new DecodeOptions())
100
            );
101
102
            return $token;
103
        }
104
105 2
        throw new InvalidAccessToken('Provider response with not valid JSON');
106
    }
107
108
    /**
109
     * Extract data from JWT->payload and create User
110
     * In some providers it's possible to get email/emailVerified or another data from JWT
111
     * And due this fact it's not needed to do server request to get identity
112
     *
113
     * @param AccessTokenInterface $accessToken
114
     * @return User
115
     */
116
    abstract public function extractIdentity(AccessTokenInterface $accessToken);
117
118
    /**
119
     * {@inheritDoc}
120
     */
121
    public function createAccessToken(array $information)
122
    {
123
        $token = new AccessToken($information);
124
        $token->setJwt(
125
            JWT::decode($information['id_token'], $this->getJWKSet(), new DecodeOptions())
126
        );
127
128
        return $token;
129
    }
130
}
131