Completed
Push — master ( fc6f01...c001b0 )
by Alexandre
01:58
created

getClientAuthenticationMethod()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: GCC-MED
5
 * Date: 09/03/2018
6
 * Time: 17:00
7
 */
8
9
namespace OAuth2\ClientAuthentication;
10
11
12
use OAuth2\Exceptions\OAuthException;
13
use OAuth2\Roles\ClientTypes\RegisteredClient;
14
use OAuth2\Storages\ClientStorageInterface;
15
use Psr\Http\Message\ServerRequestInterface;
16
17
/**
18
 * Class ClientAuthenticationMethodManager
19
 * @package OAuth2\ClientAuthentication
20
 *
21
 * @see https://tools.ietf.org/html/rfc6749#section-2.3
22
 * If the client type is confidential, the client and authorization
23
 * server establish a client authentication method suitable for the
24
 * security requirements of the authorization server.  The authorization
25
 * server MAY accept any form of client authentication meeting its
26
 * security requirements.
27
 *
28
 * Confidential clients are typically issued (or establish) a set of
29
 * client credentials used for authenticating with the authorization
30
 * server (e.g., password, public/private key pair).
31
 *
32
 * The authorization server MAY establish a client authentication method
33
 * with public clients.  However, the authorization server MUST NOT rely
34
 * on public client authentication for the purpose of identifying the
35
 * client.
36
 *
37
 * The client MUST NOT use more than one authentication method in each
38
 * request.
39
 *
40
 *
41
 * @see https://tools.ietf.org/html/rfc6749#section-3.2.1
42
 * Confidential clients or other clients issued client credentials MUST
43
 * authenticate with the authorization server as described in
44
 * Section 2.3 when making requests to the token endpoint.  Client
45
 * authentication is used for:
46
 *
47
 * o  Enforcing the binding of refresh tokens and authorization codes to
48
 * the client they were issued to.  Client authentication is critical
49
 * when an authorization code is transmitted to the redirection
50
 * endpoint over an insecure channel or when the redirection URI has
51
 * not been registered in full.
52
 *
53
 * o  Recovering from a compromised client by disabling the client or
54
 * changing its credentials, thus preventing an attacker from abusing
55
 * stolen refresh tokens.  Changing a single set of client
56
 * credentials is significantly faster than revoking an entire set of
57
 * refresh tokens.
58
 *
59
 * o  Implementing authentication management best practices, which
60
 * require periodic credential rotation.  Rotation of an entire set
61
 * of refresh tokens can be challenging, while rotation of a single
62
 * set of client credentials is significantly easier.
63
 *
64
 * A client MAY use the "client_id" request parameter to identify itself
65
 * when sending requests to the token endpoint.  In the
66
 * "authorization_code" "grant_type" request to the token endpoint, an
67
 * unauthenticated client MUST send its "client_id" to prevent itself
68
 * from inadvertently accepting a code intended for a client with a
69
 * different "client_id".  This protects the client from substitution of
70
 * the authentication code.  (It provides no additional security for the
71
 * protected resource.)
72
 */
73
class ClientAuthenticationMethodManager
74
{
75
    protected $clientAuthenticationMethods = [];
76
    /**
77
     * @var ClientStorageInterface
78
     */
79
    private $clientStorage;
80
81
    public function __construct(ClientStorageInterface $clientStorage)
82
    {
83
        $this->clientStorage = $clientStorage;
84
    }
85
86
    public function setClientAuthenticationMethod(string $identifier,
87
                                                  ClientAuthenticationMethodInterface $clientAuthenticationMethod): self
88
    {
89
        $this->clientAuthenticationMethods[$identifier] = $clientAuthenticationMethod;
90
        return $this;
91
    }
92
93
    public function getClientAuthenticationMethod(string $identifier): ?ClientAuthenticationMethodInterface
94
    {
95
        return $this->clientAuthenticationMethods[$identifier] ?? null;
96
    }
97
98
    /**
99
     * @param ServerRequestInterface $request
100
     * @param array $requestData
101
     * @return RegisteredClient
102
     * @throws OAuthException
103
     */
104
    public function authenticate(ServerRequestInterface $request, array $requestData): RegisteredClient
105
    {
106
        /**
107
         * @var ClientAuthenticationMethodInterface $clientAuthenticationMethod
108
         */
109
        $clientAuthenticationMethodUsedIdentifier = null;
110
        $clientAuthenticationMethodUsed = null;
111
112
        foreach ($this->clientAuthenticationMethods as $identifier => $clientAuthenticationMethod) {
113
            if ($clientAuthenticationMethod->support($request, $requestData)) {
114
                /**
115
                 * @see https://tools.ietf.org/html/rfc6749#section-2.3
116
                 * The client MUST NOT use more than one authentication method in each
117
                 * request.
118
                 */
119
                if ($clientAuthenticationMethodUsedIdentifier) {
120
                    throw new OAuthException('invalid_request',
121
                        'The request utilizes more than one mechanism for authenticating the client.',
122
                        'https://tools.ietf.org/html/rfc6749#section-3.2.1');
123
                }
124
125
                $clientAuthenticationMethodUsedIdentifier = $identifier;
126
                $clientAuthenticationMethodUsed = $clientAuthenticationMethod;
127
            }
128
        }
129
130
        /**
131
         * @see https://tools.ietf.org/html/rfc6749#section-3.2.1
132
         * Confidential clients or other clients issued client credentials MUST
133
         * authenticate with the authorization server as described in
134
         * Section 2.3 when making requests to the token endpoint.
135
         */
136
        if ($clientAuthenticationMethodUsed) {
137
            if (!$client = $clientAuthenticationMethodUsed->authenticate($request, $requestData)) {
138
                throw new OAuthException('invalid_client',
139
                    'Client authentication failed. Unknown client.',
140
                    'https://tools.ietf.org/html/rfc6749#section-3.2.1');
141
            }
142
        } else {
143
            /**
144
             * @see https://tools.ietf.org/html/rfc6749#section-3.2.1
145
             * A client MAY use the "client_id" request parameter to identify itself
146
             * when sending requests to the token endpoint.  In the
147
             * "authorization_code" "grant_type" request to the token endpoint, an
148
             * unauthenticated client MUST send its "client_id" to prevent itself
149
             * from inadvertently accepting a code intended for a client with a
150
             * different "client_id".  This protects the client from substitution of
151
             * the authentication code.  (It provides no additional security for the
152
             * protected resource.)
153
             */
154
            if (empty($requestData['client_id'])) {
155
                throw new OAuthException('invalid_request', 'The request is missing the required parameter client_id.',
156
                    'https://tools.ietf.org/html/rfc6749#section-4.1');
157
            }
158
159
            if (!$client = $this->clientStorage->get($requestData['client_id'])) {
160
                throw new OAuthException('invalid_request', 'The request includes the invalid parameter client_id.',
161
                    'https://tools.ietf.org/html/rfc6749#section-4.1');
162
            }
163
164
            /**
165
             * @see https://tools.ietf.org/html/rfc6749#section-3.2.1
166
             * Confidential clients or other clients issued client credentials MUST
167
             * authenticate with the authorization server as described in
168
             * Section 2.3 when making requests to the token endpoint.
169
             */
170
            if ($client->hasCredentials()) {
171
                throw new OAuthException('invalid_client', 'Client authentication failed. No client authentication included',
172
                    'https://tools.ietf.org/html/rfc6749#section-3.2.1');
173
            }
174
175
            $clientAuthenticationMethodUsedIdentifier = 'none';
176
        }
177
178
        $tokenEndpointAuthMethod = $client->getMetadata()->getTokenEndpointAuthMethod() ?: 'client_secret_basic';
179
        if ($tokenEndpointAuthMethod !== $clientAuthenticationMethodUsedIdentifier) {
180
            throw new OAuthException('invalid_client',
181
                'Client authentication failed. Unsupported authentication method.',
182
                'https://tools.ietf.org/html/rfc6749#section-3.2.1');
183
        }
184
185
        return $client;
186
    }
187
}