Passed
Branch develop (d6f62e)
by Tito
06:29
created
extensions/libraries/redcore/api/oauth2/GrantType/GrantTypeInterface.php 1 patch
Indentation   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -11,10 +11,10 @@
 block discarded – undo
11 11
  */
12 12
 interface GrantTypeInterface
13 13
 {
14
-    public function getQuerystringIdentifier();
15
-    public function validateRequest(RequestInterface $request, ResponseInterface $response);
16
-    public function getClientId();
17
-    public function getUserId();
18
-    public function getScope();
19
-    public function createAccessToken(AccessTokenInterface $accessToken, $client_id, $user_id, $scope);
14
+	public function getQuerystringIdentifier();
15
+	public function validateRequest(RequestInterface $request, ResponseInterface $response);
16
+	public function getClientId();
17
+	public function getUserId();
18
+	public function getScope();
19
+	public function createAccessToken(AccessTokenInterface $accessToken, $client_id, $user_id, $scope);
20 20
 }
Please login to merge, or discard this patch.
extensions/libraries/redcore/api/oauth2/GrantType/ClientCredentials.php 1 patch
Indentation   +41 added lines, -41 removed lines patch added patch discarded remove patch
@@ -13,55 +13,55 @@
 block discarded – undo
13 13
  */
14 14
 class ClientCredentials extends HttpBasic implements GrantTypeInterface
15 15
 {
16
-    private $clientData;
16
+	private $clientData;
17 17
 
18
-    public function __construct(ClientCredentialsInterface $storage, array $config = array())
19
-    {
20
-        /**
21
-         * The client credentials grant type MUST only be used by confidential clients
22
-         *
23
-         * @see http://tools.ietf.org/html/rfc6749#section-4.4
24
-         */
25
-        $config['allow_public_clients'] = false;
18
+	public function __construct(ClientCredentialsInterface $storage, array $config = array())
19
+	{
20
+		/**
21
+		 * The client credentials grant type MUST only be used by confidential clients
22
+		 *
23
+		 * @see http://tools.ietf.org/html/rfc6749#section-4.4
24
+		 */
25
+		$config['allow_public_clients'] = false;
26 26
 
27
-        parent::__construct($storage, $config);
28
-    }
27
+		parent::__construct($storage, $config);
28
+	}
29 29
 
30
-    public function getQuerystringIdentifier()
31
-    {
32
-        return 'client_credentials';
33
-    }
30
+	public function getQuerystringIdentifier()
31
+	{
32
+		return 'client_credentials';
33
+	}
34 34
 
35
-    public function getScope()
36
-    {
37
-        $this->loadClientData();
35
+	public function getScope()
36
+	{
37
+		$this->loadClientData();
38 38
 
39
-        return isset($this->clientData['scope']) ? $this->clientData['scope'] : null;
40
-    }
39
+		return isset($this->clientData['scope']) ? $this->clientData['scope'] : null;
40
+	}
41 41
 
42
-    public function getUserId()
43
-    {
44
-        $this->loadClientData();
42
+	public function getUserId()
43
+	{
44
+		$this->loadClientData();
45 45
 
46
-        return isset($this->clientData['user_id']) ? $this->clientData['user_id'] : null;
47
-    }
46
+		return isset($this->clientData['user_id']) ? $this->clientData['user_id'] : null;
47
+	}
48 48
 
49
-    public function createAccessToken(AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
50
-    {
51
-        /**
52
-         * Client Credentials Grant does NOT include a refresh token
53
-         *
54
-         * @see http://tools.ietf.org/html/rfc6749#section-4.4.3
55
-         */
56
-        $includeRefreshToken = false;
49
+	public function createAccessToken(AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
50
+	{
51
+		/**
52
+		 * Client Credentials Grant does NOT include a refresh token
53
+		 *
54
+		 * @see http://tools.ietf.org/html/rfc6749#section-4.4.3
55
+		 */
56
+		$includeRefreshToken = false;
57 57
 
58
-        return $accessToken->createAccessToken($client_id, $user_id, $scope, $includeRefreshToken);
59
-    }
58
+		return $accessToken->createAccessToken($client_id, $user_id, $scope, $includeRefreshToken);
59
+	}
60 60
 
61
-    private function loadClientData()
62
-    {
63
-        if (!$this->clientData) {
64
-            $this->clientData = $this->storage->getClientDetails($this->getClientId());
65
-        }
66
-    }
61
+	private function loadClientData()
62
+	{
63
+		if (!$this->clientData) {
64
+			$this->clientData = $this->storage->getClientDetails($this->getClientId());
65
+		}
66
+	}
67 67
 }
Please login to merge, or discard this patch.
extensions/libraries/redcore/api/oauth2/GrantType/JwtBearer.php 1 patch
Indentation   +203 added lines, -203 removed lines patch added patch discarded remove patch
@@ -20,207 +20,207 @@
 block discarded – undo
20 20
  */
21 21
 class JwtBearer implements GrantTypeInterface, ClientAssertionTypeInterface
22 22
 {
23
-    private $jwt;
24
-
25
-    protected $storage;
26
-    protected $audience;
27
-    protected $jwtUtil;
28
-    protected $allowedAlgorithms;
29
-
30
-    /**
31
-     * Creates an instance of the JWT bearer grant type.
32
-     *
33
-     * @param OAuth2\Storage\JWTBearerInterface|JwtBearerInterface $storage A valid storage interface that implements storage hooks for the JWT bearer grant type.
34
-     * @param string $audience The audience to validate the token against. This is usually the full URI of the OAuth token requests endpoint.
35
-     * @param EncryptionInterface|OAuth2\Encryption\JWT $jwtUtil OPTONAL The class used to decode, encode and verify JWTs.
36
-     * @param array $config
37
-     */
38
-    public function __construct(JwtBearerInterface $storage, $audience, EncryptionInterface $jwtUtil = null, array $config = array())
39
-    {
40
-        $this->storage = $storage;
41
-        $this->audience = $audience;
42
-
43
-        if (is_null($jwtUtil)) {
44
-            $jwtUtil = new Jwt();
45
-        }
46
-
47
-        $this->config = array_merge(array(
48
-            'allowed_algorithms' => array('RS256', 'RS384', 'RS512')
49
-        ), $config);
50
-
51
-        $this->jwtUtil = $jwtUtil;
52
-
53
-        $this->allowedAlgorithms = $this->config['allowed_algorithms'];
54
-    }
55
-
56
-    /**
57
-     * Returns the grant_type get parameter to identify the grant type request as JWT bearer authorization grant.
58
-     *
59
-     * @return
60
-     * The string identifier for grant_type.
61
-     *
62
-     * @see OAuth2\GrantType\GrantTypeInterface::getQuerystringIdentifier()
63
-     */
64
-    public function getQuerystringIdentifier()
65
-    {
66
-        return 'urn:ietf:params:oauth:grant-type:jwt-bearer';
67
-    }
68
-
69
-    /**
70
-     * Validates the data from the decoded JWT.
71
-     *
72
-     * @return
73
-     * TRUE if the JWT request is valid and can be decoded. Otherwise, FALSE is returned.
74
-     *
75
-     * @see OAuth2\GrantType\GrantTypeInterface::getTokenData()
76
-     */
77
-    public function validateRequest(RequestInterface $request, ResponseInterface $response)
78
-    {
79
-        if (!$request->request("assertion")) {
80
-            $response->setError(400, 'invalid_request', 'Missing parameters: "assertion" required');
81
-
82
-            return null;
83
-        }
84
-
85
-        // Store the undecoded JWT for later use
86
-        $undecodedJWT = $request->request('assertion');
87
-
88
-        // Decode the JWT
89
-        $jwt = $this->jwtUtil->decode($request->request('assertion'), null, false);
90
-
91
-        if (!$jwt) {
92
-            $response->setError(400, 'invalid_request', "JWT is malformed");
93
-
94
-            return null;
95
-        }
96
-
97
-        // ensure these properties contain a value
98
-        // @todo: throw malformed error for missing properties
99
-        $jwt = array_merge(array(
100
-            'scope' => null,
101
-            'iss' => null,
102
-            'sub' => null,
103
-            'aud' => null,
104
-            'exp' => null,
105
-            'nbf' => null,
106
-            'iat' => null,
107
-            'jti' => null,
108
-            'typ' => null,
109
-        ), $jwt);
110
-
111
-        if (!isset($jwt['iss'])) {
112
-            $response->setError(400, 'invalid_grant', "Invalid issuer (iss) provided");
113
-
114
-            return null;
115
-        }
116
-
117
-        if (!isset($jwt['sub'])) {
118
-            $response->setError(400, 'invalid_grant', "Invalid subject (sub) provided");
119
-
120
-            return null;
121
-        }
122
-
123
-        if (!isset($jwt['exp'])) {
124
-            $response->setError(400, 'invalid_grant', "Expiration (exp) time must be present");
125
-
126
-            return null;
127
-        }
128
-
129
-        // Check expiration
130
-        if (ctype_digit($jwt['exp'])) {
131
-            if ($jwt['exp'] <= time()) {
132
-                $response->setError(400, 'invalid_grant', "JWT has expired");
133
-
134
-                return null;
135
-            }
136
-        } else {
137
-            $response->setError(400, 'invalid_grant', "Expiration (exp) time must be a unix time stamp");
138
-
139
-            return null;
140
-        }
141
-
142
-        // Check the not before time
143
-        if ($notBefore = $jwt['nbf']) {
144
-            if (ctype_digit($notBefore)) {
145
-                if ($notBefore > time()) {
146
-                    $response->setError(400, 'invalid_grant', "JWT cannot be used before the Not Before (nbf) time");
147
-
148
-                    return null;
149
-                }
150
-            } else {
151
-                $response->setError(400, 'invalid_grant', "Not Before (nbf) time must be a unix time stamp");
152
-
153
-                return null;
154
-            }
155
-        }
156
-
157
-        // Check the audience if required to match
158
-        if (!isset($jwt['aud']) || ($jwt['aud'] != $this->audience)) {
159
-            $response->setError(400, 'invalid_grant', "Invalid audience (aud)");
160
-
161
-            return null;
162
-        }
163
-
164
-        // Check the jti (nonce)
165
-        // @see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-13#section-4.1.7
166
-        if (isset($jwt['jti'])) {
167
-            $jti = $this->storage->getJti($jwt['iss'], $jwt['sub'], $jwt['aud'], $jwt['exp'], $jwt['jti']);
168
-
169
-            //Reject if jti is used and jwt is still valid (exp parameter has not expired).
170
-            if ($jti && $jti['expires'] > time()) {
171
-                $response->setError(400, 'invalid_grant', "JSON Token Identifier (jti) has already been used");
172
-
173
-                return null;
174
-            } else {
175
-                $this->storage->setJti($jwt['iss'], $jwt['sub'], $jwt['aud'], $jwt['exp'], $jwt['jti']);
176
-            }
177
-        }
178
-
179
-        // Get the iss's public key
180
-        // @see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06#section-4.1.1
181
-        if (!$key = $this->storage->getClientKey($jwt['iss'], $jwt['sub'])) {
182
-            $response->setError(400, 'invalid_grant', "Invalid issuer (iss) or subject (sub) provided");
183
-
184
-            return null;
185
-        }
186
-
187
-        // Verify the JWT
188
-        if (!$this->jwtUtil->decode($undecodedJWT, $key, $this->allowedAlgorithms)) {
189
-            $response->setError(400, 'invalid_grant', "JWT failed signature verification");
190
-
191
-            return null;
192
-        }
193
-
194
-        $this->jwt = $jwt;
195
-
196
-        return true;
197
-    }
198
-
199
-    public function getClientId()
200
-    {
201
-        return $this->jwt['iss'];
202
-    }
203
-
204
-    public function getUserId()
205
-    {
206
-        return $this->jwt['sub'];
207
-    }
208
-
209
-    public function getScope()
210
-    {
211
-        return null;
212
-    }
213
-
214
-    /**
215
-     * Creates an access token that is NOT associated with a refresh token.
216
-     * If a subject (sub) the name of the user/account we are accessing data on behalf of.
217
-     *
218
-     * @see OAuth2\GrantType\GrantTypeInterface::createAccessToken()
219
-     */
220
-    public function createAccessToken(AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
221
-    {
222
-        $includeRefreshToken = false;
223
-
224
-        return $accessToken->createAccessToken($client_id, $user_id, $scope, $includeRefreshToken);
225
-    }
23
+	private $jwt;
24
+
25
+	protected $storage;
26
+	protected $audience;
27
+	protected $jwtUtil;
28
+	protected $allowedAlgorithms;
29
+
30
+	/**
31
+	 * Creates an instance of the JWT bearer grant type.
32
+	 *
33
+	 * @param OAuth2\Storage\JWTBearerInterface|JwtBearerInterface $storage A valid storage interface that implements storage hooks for the JWT bearer grant type.
34
+	 * @param string $audience The audience to validate the token against. This is usually the full URI of the OAuth token requests endpoint.
35
+	 * @param EncryptionInterface|OAuth2\Encryption\JWT $jwtUtil OPTONAL The class used to decode, encode and verify JWTs.
36
+	 * @param array $config
37
+	 */
38
+	public function __construct(JwtBearerInterface $storage, $audience, EncryptionInterface $jwtUtil = null, array $config = array())
39
+	{
40
+		$this->storage = $storage;
41
+		$this->audience = $audience;
42
+
43
+		if (is_null($jwtUtil)) {
44
+			$jwtUtil = new Jwt();
45
+		}
46
+
47
+		$this->config = array_merge(array(
48
+			'allowed_algorithms' => array('RS256', 'RS384', 'RS512')
49
+		), $config);
50
+
51
+		$this->jwtUtil = $jwtUtil;
52
+
53
+		$this->allowedAlgorithms = $this->config['allowed_algorithms'];
54
+	}
55
+
56
+	/**
57
+	 * Returns the grant_type get parameter to identify the grant type request as JWT bearer authorization grant.
58
+	 *
59
+	 * @return
60
+	 * The string identifier for grant_type.
61
+	 *
62
+	 * @see OAuth2\GrantType\GrantTypeInterface::getQuerystringIdentifier()
63
+	 */
64
+	public function getQuerystringIdentifier()
65
+	{
66
+		return 'urn:ietf:params:oauth:grant-type:jwt-bearer';
67
+	}
68
+
69
+	/**
70
+	 * Validates the data from the decoded JWT.
71
+	 *
72
+	 * @return
73
+	 * TRUE if the JWT request is valid and can be decoded. Otherwise, FALSE is returned.
74
+	 *
75
+	 * @see OAuth2\GrantType\GrantTypeInterface::getTokenData()
76
+	 */
77
+	public function validateRequest(RequestInterface $request, ResponseInterface $response)
78
+	{
79
+		if (!$request->request("assertion")) {
80
+			$response->setError(400, 'invalid_request', 'Missing parameters: "assertion" required');
81
+
82
+			return null;
83
+		}
84
+
85
+		// Store the undecoded JWT for later use
86
+		$undecodedJWT = $request->request('assertion');
87
+
88
+		// Decode the JWT
89
+		$jwt = $this->jwtUtil->decode($request->request('assertion'), null, false);
90
+
91
+		if (!$jwt) {
92
+			$response->setError(400, 'invalid_request', "JWT is malformed");
93
+
94
+			return null;
95
+		}
96
+
97
+		// ensure these properties contain a value
98
+		// @todo: throw malformed error for missing properties
99
+		$jwt = array_merge(array(
100
+			'scope' => null,
101
+			'iss' => null,
102
+			'sub' => null,
103
+			'aud' => null,
104
+			'exp' => null,
105
+			'nbf' => null,
106
+			'iat' => null,
107
+			'jti' => null,
108
+			'typ' => null,
109
+		), $jwt);
110
+
111
+		if (!isset($jwt['iss'])) {
112
+			$response->setError(400, 'invalid_grant', "Invalid issuer (iss) provided");
113
+
114
+			return null;
115
+		}
116
+
117
+		if (!isset($jwt['sub'])) {
118
+			$response->setError(400, 'invalid_grant', "Invalid subject (sub) provided");
119
+
120
+			return null;
121
+		}
122
+
123
+		if (!isset($jwt['exp'])) {
124
+			$response->setError(400, 'invalid_grant', "Expiration (exp) time must be present");
125
+
126
+			return null;
127
+		}
128
+
129
+		// Check expiration
130
+		if (ctype_digit($jwt['exp'])) {
131
+			if ($jwt['exp'] <= time()) {
132
+				$response->setError(400, 'invalid_grant', "JWT has expired");
133
+
134
+				return null;
135
+			}
136
+		} else {
137
+			$response->setError(400, 'invalid_grant', "Expiration (exp) time must be a unix time stamp");
138
+
139
+			return null;
140
+		}
141
+
142
+		// Check the not before time
143
+		if ($notBefore = $jwt['nbf']) {
144
+			if (ctype_digit($notBefore)) {
145
+				if ($notBefore > time()) {
146
+					$response->setError(400, 'invalid_grant', "JWT cannot be used before the Not Before (nbf) time");
147
+
148
+					return null;
149
+				}
150
+			} else {
151
+				$response->setError(400, 'invalid_grant', "Not Before (nbf) time must be a unix time stamp");
152
+
153
+				return null;
154
+			}
155
+		}
156
+
157
+		// Check the audience if required to match
158
+		if (!isset($jwt['aud']) || ($jwt['aud'] != $this->audience)) {
159
+			$response->setError(400, 'invalid_grant', "Invalid audience (aud)");
160
+
161
+			return null;
162
+		}
163
+
164
+		// Check the jti (nonce)
165
+		// @see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-13#section-4.1.7
166
+		if (isset($jwt['jti'])) {
167
+			$jti = $this->storage->getJti($jwt['iss'], $jwt['sub'], $jwt['aud'], $jwt['exp'], $jwt['jti']);
168
+
169
+			//Reject if jti is used and jwt is still valid (exp parameter has not expired).
170
+			if ($jti && $jti['expires'] > time()) {
171
+				$response->setError(400, 'invalid_grant', "JSON Token Identifier (jti) has already been used");
172
+
173
+				return null;
174
+			} else {
175
+				$this->storage->setJti($jwt['iss'], $jwt['sub'], $jwt['aud'], $jwt['exp'], $jwt['jti']);
176
+			}
177
+		}
178
+
179
+		// Get the iss's public key
180
+		// @see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06#section-4.1.1
181
+		if (!$key = $this->storage->getClientKey($jwt['iss'], $jwt['sub'])) {
182
+			$response->setError(400, 'invalid_grant', "Invalid issuer (iss) or subject (sub) provided");
183
+
184
+			return null;
185
+		}
186
+
187
+		// Verify the JWT
188
+		if (!$this->jwtUtil->decode($undecodedJWT, $key, $this->allowedAlgorithms)) {
189
+			$response->setError(400, 'invalid_grant', "JWT failed signature verification");
190
+
191
+			return null;
192
+		}
193
+
194
+		$this->jwt = $jwt;
195
+
196
+		return true;
197
+	}
198
+
199
+	public function getClientId()
200
+	{
201
+		return $this->jwt['iss'];
202
+	}
203
+
204
+	public function getUserId()
205
+	{
206
+		return $this->jwt['sub'];
207
+	}
208
+
209
+	public function getScope()
210
+	{
211
+		return null;
212
+	}
213
+
214
+	/**
215
+	 * Creates an access token that is NOT associated with a refresh token.
216
+	 * If a subject (sub) the name of the user/account we are accessing data on behalf of.
217
+	 *
218
+	 * @see OAuth2\GrantType\GrantTypeInterface::createAccessToken()
219
+	 */
220
+	public function createAccessToken(AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
221
+	{
222
+		$includeRefreshToken = false;
223
+
224
+		return $accessToken->createAccessToken($client_id, $user_id, $scope, $includeRefreshToken);
225
+	}
226 226
 }
Please login to merge, or discard this patch.
redcore/api/oauth2/ClientAssertionType/ClientAssertionTypeInterface.php 1 patch
Indentation   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -10,6 +10,6 @@
 block discarded – undo
10 10
  */
11 11
 interface ClientAssertionTypeInterface
12 12
 {
13
-    public function validateRequest(RequestInterface $request, ResponseInterface $response);
14
-    public function getClientId();
13
+	public function validateRequest(RequestInterface $request, ResponseInterface $response);
14
+	public function getClientId();
15 15
 }
Please login to merge, or discard this patch.
extensions/libraries/redcore/api/oauth2/ClientAssertionType/HttpBasic.php 2 patches
Indentation   +107 added lines, -107 removed lines patch added patch discarded remove patch
@@ -13,111 +13,111 @@
 block discarded – undo
13 13
  */
14 14
 class HttpBasic implements ClientAssertionTypeInterface
15 15
 {
16
-    private $clientData;
17
-
18
-    protected $storage;
19
-    protected $config;
20
-
21
-    /**
22
-     * @param OAuth2\Storage\ClientCredentialsInterface $clientStorage REQUIRED Storage class for retrieving client credentials information
23
-     * @param array                                     $config        OPTIONAL Configuration options for the server
24
-     *                                                                 <code>
25
-     *                                                                 $config = array(
26
-     *                                                                 'allow_credentials_in_request_body' => true, // whether to look for credentials in the POST body in addition to the Authorize HTTP Header
27
-     *                                                                 'allow_public_clients'  => true              // if true, "public clients" (clients without a secret) may be authenticated
28
-     *                                                                 );
29
-     *                                                                 </code>
30
-     */
31
-    public function __construct(ClientCredentialsInterface $storage, array $config = array())
32
-    {
33
-        $this->storage = $storage;
34
-        $this->config = array_merge(array(
35
-            'allow_credentials_in_request_body' => true,
36
-            'allow_public_clients' => true,
37
-        ), $config);
38
-    }
39
-
40
-    public function validateRequest(RequestInterface $request, ResponseInterface $response)
41
-    {
42
-        if (!$clientData = $this->getClientCredentials($request, $response)) {
43
-            return false;
44
-        }
45
-
46
-        if (!isset($clientData['client_id'])) {
47
-            throw new \LogicException('the clientData array must have "client_id" set');
48
-        }
49
-
50
-        if (!isset($clientData['client_secret']) || $clientData['client_secret'] == '') {
51
-            if (!$this->config['allow_public_clients']) {
52
-                $response->setError(400, 'invalid_client', 'client credentials are required');
53
-
54
-                return false;
55
-            }
56
-
57
-            if (!$this->storage->isPublicClient($clientData['client_id'])) {
58
-                $response->setError(400, 'invalid_client', 'This client is invalid or must authenticate using a client secret');
59
-
60
-                return false;
61
-            }
62
-        } elseif ($this->storage->checkClientCredentials($clientData['client_id'], $clientData['client_secret']) === false) {
63
-            $response->setError(400, 'invalid_client', 'The client credentials are invalid');
64
-
65
-            return false;
66
-        }
67
-
68
-        $this->clientData = $clientData;
69
-
70
-        return true;
71
-    }
72
-
73
-    public function getClientId()
74
-    {
75
-        return $this->clientData['client_id'];
76
-    }
77
-
78
-    /**
79
-     * Internal function used to get the client credentials from HTTP basic
80
-     * auth or POST data.
81
-     *
82
-     * According to the spec (draft 20), the client_id can be provided in
83
-     * the Basic Authorization header (recommended) or via GET/POST.
84
-     *
85
-     * @return
86
-     * A list containing the client identifier and password, for example
87
-     * @code
88
-     * return array(
89
-     *     "client_id"     => CLIENT_ID,        // REQUIRED the client id
90
-     *     "client_secret" => CLIENT_SECRET,    // OPTIONAL the client secret (may be omitted for public clients)
91
-     * );
92
-     * @endcode
93
-     *
94
-     * @see http://tools.ietf.org/html/rfc6749#section-2.3.1
95
-     *
96
-     * @ingroup oauth2_section_2
97
-     */
98
-    public function getClientCredentials(RequestInterface $request, ResponseInterface $response = null)
99
-    {
100
-        if (!is_null($request->headers('PHP_AUTH_USER')) && !is_null($request->headers('PHP_AUTH_PW'))) {
101
-            return array('client_id' => $request->headers('PHP_AUTH_USER'), 'client_secret' => $request->headers('PHP_AUTH_PW'));
102
-        }
103
-
104
-        if ($this->config['allow_credentials_in_request_body']) {
105
-            // Using POST for HttpBasic authorization is not recommended, but is supported by specification
106
-            if (!is_null($request->request('client_id'))) {
107
-                /**
108
-                 * client_secret can be null if the client's password is an empty string
109
-                 * @see http://tools.ietf.org/html/rfc6749#section-2.3.1
110
-                 */
111
-
112
-                return array('client_id' => $request->request('client_id'), 'client_secret' => $request->request('client_secret'));
113
-            }
114
-        }
115
-
116
-        if ($response) {
117
-            $message = $this->config['allow_credentials_in_request_body'] ? ' or body' : '';
118
-            $response->setError(400, 'invalid_client', 'Client credentials were not found in the headers'.$message);
119
-        }
120
-
121
-        return null;
122
-    }
16
+	private $clientData;
17
+
18
+	protected $storage;
19
+	protected $config;
20
+
21
+	/**
22
+	 * @param OAuth2\Storage\ClientCredentialsInterface $clientStorage REQUIRED Storage class for retrieving client credentials information
23
+	 * @param array                                     $config        OPTIONAL Configuration options for the server
24
+	 *                                                                 <code>
25
+	 *                                                                 $config = array(
26
+	 *                                                                 'allow_credentials_in_request_body' => true, // whether to look for credentials in the POST body in addition to the Authorize HTTP Header
27
+	 *                                                                 'allow_public_clients'  => true              // if true, "public clients" (clients without a secret) may be authenticated
28
+	 *                                                                 );
29
+	 *                                                                 </code>
30
+	 */
31
+	public function __construct(ClientCredentialsInterface $storage, array $config = array())
32
+	{
33
+		$this->storage = $storage;
34
+		$this->config = array_merge(array(
35
+			'allow_credentials_in_request_body' => true,
36
+			'allow_public_clients' => true,
37
+		), $config);
38
+	}
39
+
40
+	public function validateRequest(RequestInterface $request, ResponseInterface $response)
41
+	{
42
+		if (!$clientData = $this->getClientCredentials($request, $response)) {
43
+			return false;
44
+		}
45
+
46
+		if (!isset($clientData['client_id'])) {
47
+			throw new \LogicException('the clientData array must have "client_id" set');
48
+		}
49
+
50
+		if (!isset($clientData['client_secret']) || $clientData['client_secret'] == '') {
51
+			if (!$this->config['allow_public_clients']) {
52
+				$response->setError(400, 'invalid_client', 'client credentials are required');
53
+
54
+				return false;
55
+			}
56
+
57
+			if (!$this->storage->isPublicClient($clientData['client_id'])) {
58
+				$response->setError(400, 'invalid_client', 'This client is invalid or must authenticate using a client secret');
59
+
60
+				return false;
61
+			}
62
+		} elseif ($this->storage->checkClientCredentials($clientData['client_id'], $clientData['client_secret']) === false) {
63
+			$response->setError(400, 'invalid_client', 'The client credentials are invalid');
64
+
65
+			return false;
66
+		}
67
+
68
+		$this->clientData = $clientData;
69
+
70
+		return true;
71
+	}
72
+
73
+	public function getClientId()
74
+	{
75
+		return $this->clientData['client_id'];
76
+	}
77
+
78
+	/**
79
+	 * Internal function used to get the client credentials from HTTP basic
80
+	 * auth or POST data.
81
+	 *
82
+	 * According to the spec (draft 20), the client_id can be provided in
83
+	 * the Basic Authorization header (recommended) or via GET/POST.
84
+	 *
85
+	 * @return
86
+	 * A list containing the client identifier and password, for example
87
+	 * @code
88
+	 * return array(
89
+	 *     "client_id"     => CLIENT_ID,        // REQUIRED the client id
90
+	 *     "client_secret" => CLIENT_SECRET,    // OPTIONAL the client secret (may be omitted for public clients)
91
+	 * );
92
+	 * @endcode
93
+	 *
94
+	 * @see http://tools.ietf.org/html/rfc6749#section-2.3.1
95
+	 *
96
+	 * @ingroup oauth2_section_2
97
+	 */
98
+	public function getClientCredentials(RequestInterface $request, ResponseInterface $response = null)
99
+	{
100
+		if (!is_null($request->headers('PHP_AUTH_USER')) && !is_null($request->headers('PHP_AUTH_PW'))) {
101
+			return array('client_id' => $request->headers('PHP_AUTH_USER'), 'client_secret' => $request->headers('PHP_AUTH_PW'));
102
+		}
103
+
104
+		if ($this->config['allow_credentials_in_request_body']) {
105
+			// Using POST for HttpBasic authorization is not recommended, but is supported by specification
106
+			if (!is_null($request->request('client_id'))) {
107
+				/**
108
+				 * client_secret can be null if the client's password is an empty string
109
+				 * @see http://tools.ietf.org/html/rfc6749#section-2.3.1
110
+				 */
111
+
112
+				return array('client_id' => $request->request('client_id'), 'client_secret' => $request->request('client_secret'));
113
+			}
114
+		}
115
+
116
+		if ($response) {
117
+			$message = $this->config['allow_credentials_in_request_body'] ? ' or body' : '';
118
+			$response->setError(400, 'invalid_client', 'Client credentials were not found in the headers'.$message);
119
+		}
120
+
121
+		return null;
122
+	}
123 123
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -115,7 +115,7 @@
 block discarded – undo
115 115
 
116 116
         if ($response) {
117 117
             $message = $this->config['allow_credentials_in_request_body'] ? ' or body' : '';
118
-            $response->setError(400, 'invalid_client', 'Client credentials were not found in the headers'.$message);
118
+            $response->setError(400, 'invalid_client', 'Client credentials were not found in the headers' . $message);
119 119
         }
120 120
 
121 121
         return null;
Please login to merge, or discard this patch.
extensions/libraries/redcore/api/oauth2/ResponseInterface.php 1 patch
Indentation   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -10,15 +10,15 @@
 block discarded – undo
10 10
  */
11 11
 interface ResponseInterface
12 12
 {
13
-    public function addParameters(array $parameters);
13
+	public function addParameters(array $parameters);
14 14
 
15
-    public function addHttpHeaders(array $httpHeaders);
15
+	public function addHttpHeaders(array $httpHeaders);
16 16
 
17
-    public function setStatusCode($statusCode);
17
+	public function setStatusCode($statusCode);
18 18
 
19
-    public function setError($statusCode, $name, $description = null, $uri = null);
19
+	public function setError($statusCode, $name, $description = null, $uri = null);
20 20
 
21
-    public function setRedirect($statusCode, $url, $state = null, $error = null, $errorDescription = null, $errorUri = null);
21
+	public function setRedirect($statusCode, $url, $state = null, $error = null, $errorDescription = null, $errorUri = null);
22 22
 
23
-    public function getParameter($name);
23
+	public function getParameter($name);
24 24
 }
Please login to merge, or discard this patch.
libraries/redcore/api/oauth2/Controller/AuthorizeControllerInterface.php 1 patch
Indentation   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -25,19 +25,19 @@
 block discarded – undo
25 25
  */
26 26
 interface AuthorizeControllerInterface
27 27
 {
28
-    /**
29
-     * List of possible authentication response types.
30
-     * The "authorization_code" mechanism exclusively supports 'code'
31
-     * and the "implicit" mechanism exclusively supports 'token'.
32
-     *
33
-     * @var string
34
-     * @see http://tools.ietf.org/html/rfc6749#section-4.1.1
35
-     * @see http://tools.ietf.org/html/rfc6749#section-4.2.1
36
-     */
37
-    const RESPONSE_TYPE_AUTHORIZATION_CODE = 'code';
38
-    const RESPONSE_TYPE_ACCESS_TOKEN = 'token';
28
+	/**
29
+	 * List of possible authentication response types.
30
+	 * The "authorization_code" mechanism exclusively supports 'code'
31
+	 * and the "implicit" mechanism exclusively supports 'token'.
32
+	 *
33
+	 * @var string
34
+	 * @see http://tools.ietf.org/html/rfc6749#section-4.1.1
35
+	 * @see http://tools.ietf.org/html/rfc6749#section-4.2.1
36
+	 */
37
+	const RESPONSE_TYPE_AUTHORIZATION_CODE = 'code';
38
+	const RESPONSE_TYPE_ACCESS_TOKEN = 'token';
39 39
 
40
-    public function handleAuthorizeRequest(RequestInterface $request, ResponseInterface $response, $is_authorized, $user_id = null);
40
+	public function handleAuthorizeRequest(RequestInterface $request, ResponseInterface $response, $is_authorized, $user_id = null);
41 41
 
42
-    public function validateAuthorizeRequest(RequestInterface $request, ResponseInterface $response);
42
+	public function validateAuthorizeRequest(RequestInterface $request, ResponseInterface $response);
43 43
 }
Please login to merge, or discard this patch.
extensions/libraries/redcore/api/oauth2/Controller/AuthorizeController.php 1 patch
Indentation   +364 added lines, -364 removed lines patch added patch discarded remove patch
@@ -13,371 +13,371 @@
 block discarded – undo
13 13
  */
14 14
 class AuthorizeController implements AuthorizeControllerInterface
15 15
 {
16
-    private $scope;
17
-    private $state;
18
-    private $client_id;
19
-    private $redirect_uri;
20
-    private $response_type;
21
-
22
-    protected $clientStorage;
23
-    protected $responseTypes;
24
-    protected $config;
25
-    protected $scopeUtil;
26
-
27
-    /**
28
-     * @param OAuth2\Storage\ClientInterface $clientStorage REQUIRED Instance of OAuth2\Storage\ClientInterface to retrieve client information
29
-     * @param array                          $responseTypes OPTIONAL Array of OAuth2\ResponseType\ResponseTypeInterface objects.  Valid array
30
-     *                                                      keys are "code" and "token"
31
-     * @param array                          $config        OPTIONAL Configuration options for the server
32
-     *                                                      <code>
33
-     *                                                      $config = array(
34
-     *                                                      'allow_implicit' => false,            // if the controller should allow the "implicit" grant type
35
-     *                                                      'enforce_state'  => true              // if the controller should require the "state" parameter
36
-     *                                                      'require_exact_redirect_uri' => true, // if the controller should require an exact match on the "redirect_uri" parameter
37
-     *                                                      'redirect_status_code' => 302,        // HTTP status code to use for redirect responses
38
-     *                                                      );
39
-     *                                                      </code>
40
-     * @param OAuth2\ScopeInterface          $scopeUtil     OPTIONAL Instance of OAuth2\ScopeInterface to validate the requested scope
41
-     */
42
-    public function __construct(ClientInterface $clientStorage, array $responseTypes = array(), array $config = array(), ScopeInterface $scopeUtil = null)
43
-    {
44
-        $this->clientStorage = $clientStorage;
45
-        $this->responseTypes = $responseTypes;
46
-        $this->config = array_merge(array(
47
-            'allow_implicit' => false,
48
-            'enforce_state'  => true,
49
-            'require_exact_redirect_uri' => true,
50
-            'redirect_status_code' => 302,
51
-        ), $config);
52
-
53
-        if (is_null($scopeUtil)) {
54
-            $scopeUtil = new Scope();
55
-        }
56
-        $this->scopeUtil = $scopeUtil;
57
-    }
58
-
59
-    public function handleAuthorizeRequest(RequestInterface $request, ResponseInterface $response, $is_authorized, $user_id = null)
60
-    {
61
-        if (!is_bool($is_authorized)) {
62
-            throw new \InvalidArgumentException('Argument "is_authorized" must be a boolean.  This method must know if the user has granted access to the client.');
63
-        }
64
-
65
-        // We repeat this, because we need to re-validate. The request could be POSTed
66
-        // by a 3rd-party (because we are not internally enforcing NONCEs, etc)
67
-        if (!$this->validateAuthorizeRequest($request, $response)) {
68
-            return;
69
-        }
70
-
71
-        // If no redirect_uri is passed in the request, use client's registered one
72
-        if (empty($this->redirect_uri)) {
73
-            $clientData              = $this->clientStorage->getClientDetails($this->client_id);
74
-            $registered_redirect_uri = $clientData['redirect_uri'];
75
-        }
76
-
77
-        // the user declined access to the client's application
78
-        if ($is_authorized === false) {
79
-            $redirect_uri = $this->redirect_uri ?: $registered_redirect_uri;
80
-            $this->setNotAuthorizedResponse($request, $response, $redirect_uri, $user_id);
81
-
82
-            return;
83
-        }
84
-
85
-        // build the parameters to set in the redirect URI
86
-        if (!$params = $this->buildAuthorizeParameters($request, $response, $user_id)) {
87
-            return;
88
-        }
89
-
90
-        $authResult = $this->responseTypes[$this->response_type]->getAuthorizeResponse($params, $user_id);
91
-
92
-        list($redirect_uri, $uri_params) = $authResult;
93
-
94
-        if (empty($redirect_uri) && !empty($registered_redirect_uri)) {
95
-            $redirect_uri = $registered_redirect_uri;
96
-        }
97
-
98
-        $uri = $this->buildUri($redirect_uri, $uri_params);
99
-
100
-        // return redirect response
101
-        $response->setRedirect($this->config['redirect_status_code'], $uri);
102
-    }
103
-
104
-    protected function setNotAuthorizedResponse(RequestInterface $request, ResponseInterface $response, $redirect_uri, $user_id = null)
105
-    {
106
-        $error = 'access_denied';
107
-        $error_message = 'The user denied access to your application';
108
-        $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $this->state, $error, $error_message);
109
-    }
110
-
111
-    /*
16
+	private $scope;
17
+	private $state;
18
+	private $client_id;
19
+	private $redirect_uri;
20
+	private $response_type;
21
+
22
+	protected $clientStorage;
23
+	protected $responseTypes;
24
+	protected $config;
25
+	protected $scopeUtil;
26
+
27
+	/**
28
+	 * @param OAuth2\Storage\ClientInterface $clientStorage REQUIRED Instance of OAuth2\Storage\ClientInterface to retrieve client information
29
+	 * @param array                          $responseTypes OPTIONAL Array of OAuth2\ResponseType\ResponseTypeInterface objects.  Valid array
30
+	 *                                                      keys are "code" and "token"
31
+	 * @param array                          $config        OPTIONAL Configuration options for the server
32
+	 *                                                      <code>
33
+	 *                                                      $config = array(
34
+	 *                                                      'allow_implicit' => false,            // if the controller should allow the "implicit" grant type
35
+	 *                                                      'enforce_state'  => true              // if the controller should require the "state" parameter
36
+	 *                                                      'require_exact_redirect_uri' => true, // if the controller should require an exact match on the "redirect_uri" parameter
37
+	 *                                                      'redirect_status_code' => 302,        // HTTP status code to use for redirect responses
38
+	 *                                                      );
39
+	 *                                                      </code>
40
+	 * @param OAuth2\ScopeInterface          $scopeUtil     OPTIONAL Instance of OAuth2\ScopeInterface to validate the requested scope
41
+	 */
42
+	public function __construct(ClientInterface $clientStorage, array $responseTypes = array(), array $config = array(), ScopeInterface $scopeUtil = null)
43
+	{
44
+		$this->clientStorage = $clientStorage;
45
+		$this->responseTypes = $responseTypes;
46
+		$this->config = array_merge(array(
47
+			'allow_implicit' => false,
48
+			'enforce_state'  => true,
49
+			'require_exact_redirect_uri' => true,
50
+			'redirect_status_code' => 302,
51
+		), $config);
52
+
53
+		if (is_null($scopeUtil)) {
54
+			$scopeUtil = new Scope();
55
+		}
56
+		$this->scopeUtil = $scopeUtil;
57
+	}
58
+
59
+	public function handleAuthorizeRequest(RequestInterface $request, ResponseInterface $response, $is_authorized, $user_id = null)
60
+	{
61
+		if (!is_bool($is_authorized)) {
62
+			throw new \InvalidArgumentException('Argument "is_authorized" must be a boolean.  This method must know if the user has granted access to the client.');
63
+		}
64
+
65
+		// We repeat this, because we need to re-validate. The request could be POSTed
66
+		// by a 3rd-party (because we are not internally enforcing NONCEs, etc)
67
+		if (!$this->validateAuthorizeRequest($request, $response)) {
68
+			return;
69
+		}
70
+
71
+		// If no redirect_uri is passed in the request, use client's registered one
72
+		if (empty($this->redirect_uri)) {
73
+			$clientData              = $this->clientStorage->getClientDetails($this->client_id);
74
+			$registered_redirect_uri = $clientData['redirect_uri'];
75
+		}
76
+
77
+		// the user declined access to the client's application
78
+		if ($is_authorized === false) {
79
+			$redirect_uri = $this->redirect_uri ?: $registered_redirect_uri;
80
+			$this->setNotAuthorizedResponse($request, $response, $redirect_uri, $user_id);
81
+
82
+			return;
83
+		}
84
+
85
+		// build the parameters to set in the redirect URI
86
+		if (!$params = $this->buildAuthorizeParameters($request, $response, $user_id)) {
87
+			return;
88
+		}
89
+
90
+		$authResult = $this->responseTypes[$this->response_type]->getAuthorizeResponse($params, $user_id);
91
+
92
+		list($redirect_uri, $uri_params) = $authResult;
93
+
94
+		if (empty($redirect_uri) && !empty($registered_redirect_uri)) {
95
+			$redirect_uri = $registered_redirect_uri;
96
+		}
97
+
98
+		$uri = $this->buildUri($redirect_uri, $uri_params);
99
+
100
+		// return redirect response
101
+		$response->setRedirect($this->config['redirect_status_code'], $uri);
102
+	}
103
+
104
+	protected function setNotAuthorizedResponse(RequestInterface $request, ResponseInterface $response, $redirect_uri, $user_id = null)
105
+	{
106
+		$error = 'access_denied';
107
+		$error_message = 'The user denied access to your application';
108
+		$response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $this->state, $error, $error_message);
109
+	}
110
+
111
+	/*
112 112
      * We have made this protected so this class can be extended to add/modify
113 113
      * these parameters
114 114
      */
115
-    protected function buildAuthorizeParameters($request, $response, $user_id)
116
-    {
117
-        // @TODO: we should be explicit with this in the future
118
-        $params = array(
119
-            'scope'         => $this->scope,
120
-            'state'         => $this->state,
121
-            'client_id'     => $this->client_id,
122
-            'redirect_uri'  => $this->redirect_uri,
123
-            'response_type' => $this->response_type,
124
-        );
125
-
126
-        return $params;
127
-    }
128
-
129
-    public function validateAuthorizeRequest(RequestInterface $request, ResponseInterface $response)
130
-    {
131
-        // Make sure a valid client id was supplied (we can not redirect because we were unable to verify the URI)
132
-        if (!$client_id = $request->query('client_id', $request->request('client_id'))) {
133
-            // We don't have a good URI to use
134
-            $response->setError(400, 'invalid_client', "No client id supplied");
135
-
136
-            return false;
137
-        }
138
-
139
-        // Get client details
140
-        if (!$clientData = $this->clientStorage->getClientDetails($client_id)) {
141
-            $response->setError(400, 'invalid_client', 'The client id supplied is invalid');
142
-
143
-            return false;
144
-        }
145
-
146
-        $registered_redirect_uri = isset($clientData['redirect_uri']) ? $clientData['redirect_uri'] : '';
147
-
148
-        // Make sure a valid redirect_uri was supplied. If specified, it must match the clientData URI.
149
-        // @see http://tools.ietf.org/html/rfc6749#section-3.1.2
150
-        // @see http://tools.ietf.org/html/rfc6749#section-4.1.2.1
151
-        // @see http://tools.ietf.org/html/rfc6749#section-4.2.2.1
152
-        if ($supplied_redirect_uri = $request->query('redirect_uri', $request->request('redirect_uri'))) {
153
-            // validate there is no fragment supplied
154
-            $parts = parse_url($supplied_redirect_uri);
155
-            if (isset($parts['fragment']) && $parts['fragment']) {
156
-                $response->setError(400, 'invalid_uri', 'The redirect URI must not contain a fragment');
157
-
158
-                return false;
159
-            }
160
-
161
-            // validate against the registered redirect uri(s) if available
162
-            if ($registered_redirect_uri && !$this->validateRedirectUri($supplied_redirect_uri, $registered_redirect_uri)) {
163
-                $response->setError(400, 'redirect_uri_mismatch', 'The redirect URI provided is missing or does not match', '#section-3.1.2');
164
-
165
-                return false;
166
-            }
167
-            $redirect_uri = $supplied_redirect_uri;
168
-        } else {
169
-            // use the registered redirect_uri if none has been supplied, if possible
170
-            if (!$registered_redirect_uri) {
171
-                $response->setError(400, 'invalid_uri', 'No redirect URI was supplied or stored');
172
-
173
-                return false;
174
-            }
175
-
176
-            if (count(explode(' ', $registered_redirect_uri)) > 1) {
177
-                $response->setError(400, 'invalid_uri', 'A redirect URI must be supplied when multiple redirect URIs are registered', '#section-3.1.2.3');
178
-
179
-                return false;
180
-            }
181
-            $redirect_uri = $registered_redirect_uri;
182
-        }
183
-
184
-        // Select the redirect URI
185
-        $response_type = $request->query('response_type', $request->request('response_type'));
186
-
187
-        // for multiple-valued response types - make them alphabetical
188
-        if (false !== strpos($response_type, ' ')) {
189
-            $types = explode(' ', $response_type);
190
-            sort($types);
191
-            $response_type = ltrim(implode(' ', $types));
192
-        }
193
-
194
-        $state = $request->query('state', $request->request('state'));
195
-
196
-        // type and client_id are required
197
-        if (!$response_type || !in_array($response_type, $this->getValidResponseTypes())) {
198
-            $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'invalid_request', 'Invalid or missing response type', null);
199
-
200
-            return false;
201
-        }
202
-
203
-        if ($response_type == self::RESPONSE_TYPE_AUTHORIZATION_CODE) {
204
-            if (!isset($this->responseTypes['code'])) {
205
-                $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'unsupported_response_type', 'authorization code grant type not supported', null);
206
-
207
-                return false;
208
-            }
209
-            if (!$this->clientStorage->checkRestrictedGrantType($client_id, 'authorization_code')) {
210
-                $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'unauthorized_client', 'The grant type is unauthorized for this client_id', null);
211
-
212
-                return false;
213
-            }
214
-            if ($this->responseTypes['code']->enforceRedirect() && !$redirect_uri) {
215
-                $response->setError(400, 'redirect_uri_mismatch', 'The redirect URI is mandatory and was not supplied');
216
-
217
-                return false;
218
-            }
219
-        } else {
220
-            if (!$this->config['allow_implicit']) {
221
-                $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'unsupported_response_type', 'implicit grant type not supported', null);
222
-
223
-                return false;
224
-            }
225
-            if (!$this->clientStorage->checkRestrictedGrantType($client_id, 'implicit')) {
226
-                $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'unauthorized_client', 'The grant type is unauthorized for this client_id', null);
227
-
228
-                return false;
229
-            }
230
-        }
231
-
232
-        // validate requested scope if it exists
233
-        $requestedScope = $this->scopeUtil->getScopeFromRequest($request);
234
-
235
-        if ($requestedScope) {
236
-            // restrict scope by client specific scope if applicable,
237
-            // otherwise verify the scope exists
238
-            $clientScope = $this->clientStorage->getClientScope($client_id);
239
-            if ((is_null($clientScope) && !$this->scopeUtil->scopeExists($requestedScope))
240
-                || ($clientScope && !$this->scopeUtil->checkScope($requestedScope, $clientScope))) {
241
-                $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'invalid_scope', 'An unsupported scope was requested', null);
242
-
243
-                return false;
244
-            }
245
-        } else {
246
-            // use a globally-defined default scope
247
-            $defaultScope = $this->scopeUtil->getDefaultScope($client_id);
248
-
249
-            if (false === $defaultScope) {
250
-                $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'invalid_client', 'This application requires you specify a scope parameter', null);
251
-
252
-                return false;
253
-            }
254
-
255
-            $requestedScope = $defaultScope;
256
-        }
257
-
258
-        // Validate state parameter exists (if configured to enforce this)
259
-        if ($this->config['enforce_state'] && !$state) {
260
-            $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, null, 'invalid_request', 'The state parameter is required');
261
-
262
-            return false;
263
-        }
264
-
265
-        // save the input data and return true
266
-        $this->scope         = $requestedScope;
267
-        $this->state         = $state;
268
-        $this->client_id     = $client_id;
269
-        // Only save the SUPPLIED redirect URI (@see http://tools.ietf.org/html/rfc6749#section-4.1.3)
270
-        $this->redirect_uri  = $supplied_redirect_uri;
271
-        $this->response_type = $response_type;
272
-
273
-        return true;
274
-    }
275
-
276
-    /**
277
-     * Build the absolute URI based on supplied URI and parameters.
278
-     *
279
-     * @param $uri    An absolute URI.
280
-     * @param $params Parameters to be append as GET.
281
-     *
282
-     * @return
283
-     * An absolute URI with supplied parameters.
284
-     *
285
-     * @ingroup oauth2_section_4
286
-     */
287
-    private function buildUri($uri, $params)
288
-    {
289
-        $parse_url = parse_url($uri);
290
-
291
-        // Add our params to the parsed uri
292
-        foreach ($params as $k => $v) {
293
-            if (isset($parse_url[$k])) {
294
-                $parse_url[$k] .= "&" . http_build_query($v, '', '&');
295
-            } else {
296
-                $parse_url[$k] = http_build_query($v, '', '&');
297
-            }
298
-        }
299
-
300
-        // Put humpty dumpty back together
301
-        return
302
-            ((isset($parse_url["scheme"])) ? $parse_url["scheme"] . "://" : "")
303
-            . ((isset($parse_url["user"])) ? $parse_url["user"]
304
-            . ((isset($parse_url["pass"])) ? ":" . $parse_url["pass"] : "") . "@" : "")
305
-            . ((isset($parse_url["host"])) ? $parse_url["host"] : "")
306
-            . ((isset($parse_url["port"])) ? ":" . $parse_url["port"] : "")
307
-            . ((isset($parse_url["path"])) ? $parse_url["path"] : "")
308
-            . ((isset($parse_url["query"]) && !empty($parse_url['query'])) ? "?" . $parse_url["query"] : "")
309
-            . ((isset($parse_url["fragment"])) ? "#" . $parse_url["fragment"] : "")
310
-        ;
311
-    }
312
-
313
-    protected function getValidResponseTypes()
314
-    {
315
-        return array(
316
-            self::RESPONSE_TYPE_ACCESS_TOKEN,
317
-            self::RESPONSE_TYPE_AUTHORIZATION_CODE,
318
-        );
319
-    }
320
-
321
-    /**
322
-     * Internal method for validating redirect URI supplied
323
-     *
324
-     * @param string $inputUri            The submitted URI to be validated
325
-     * @param string $registeredUriString The allowed URI(s) to validate against.  Can be a space-delimited string of URIs to
326
-     *                                    allow for multiple URIs
327
-     *
328
-     * @see http://tools.ietf.org/html/rfc6749#section-3.1.2
329
-     */
330
-    protected function validateRedirectUri($inputUri, $registeredUriString)
331
-    {
332
-        if (!$inputUri || !$registeredUriString) {
333
-            return false; // if either one is missing, assume INVALID
334
-        }
335
-
336
-        $registered_uris = preg_split('/\s+/', $registeredUriString);
337
-        foreach ($registered_uris as $registered_uri) {
338
-            if ($this->config['require_exact_redirect_uri']) {
339
-                // the input uri is validated against the registered uri using exact match
340
-                if (strcmp($inputUri, $registered_uri) === 0) {
341
-                    return true;
342
-                }
343
-            } else {
344
-                // the input uri is validated against the registered uri using case-insensitive match of the initial string
345
-                // i.e. additional query parameters may be applied
346
-                if (strcasecmp(substr($inputUri, 0, strlen($registered_uri)), $registered_uri) === 0) {
347
-                    return true;
348
-                }
349
-            }
350
-        }
351
-
352
-        return false;
353
-    }
354
-
355
-    /**
356
-     * Convenience methods to access the parameters derived from the validated request
357
-     */
358
-
359
-    public function getScope()
360
-    {
361
-        return $this->scope;
362
-    }
363
-
364
-    public function getState()
365
-    {
366
-        return $this->state;
367
-    }
368
-
369
-    public function getClientId()
370
-    {
371
-        return $this->client_id;
372
-    }
373
-
374
-    public function getRedirectUri()
375
-    {
376
-        return $this->redirect_uri;
377
-    }
378
-
379
-    public function getResponseType()
380
-    {
381
-        return $this->response_type;
382
-    }
115
+	protected function buildAuthorizeParameters($request, $response, $user_id)
116
+	{
117
+		// @TODO: we should be explicit with this in the future
118
+		$params = array(
119
+			'scope'         => $this->scope,
120
+			'state'         => $this->state,
121
+			'client_id'     => $this->client_id,
122
+			'redirect_uri'  => $this->redirect_uri,
123
+			'response_type' => $this->response_type,
124
+		);
125
+
126
+		return $params;
127
+	}
128
+
129
+	public function validateAuthorizeRequest(RequestInterface $request, ResponseInterface $response)
130
+	{
131
+		// Make sure a valid client id was supplied (we can not redirect because we were unable to verify the URI)
132
+		if (!$client_id = $request->query('client_id', $request->request('client_id'))) {
133
+			// We don't have a good URI to use
134
+			$response->setError(400, 'invalid_client', "No client id supplied");
135
+
136
+			return false;
137
+		}
138
+
139
+		// Get client details
140
+		if (!$clientData = $this->clientStorage->getClientDetails($client_id)) {
141
+			$response->setError(400, 'invalid_client', 'The client id supplied is invalid');
142
+
143
+			return false;
144
+		}
145
+
146
+		$registered_redirect_uri = isset($clientData['redirect_uri']) ? $clientData['redirect_uri'] : '';
147
+
148
+		// Make sure a valid redirect_uri was supplied. If specified, it must match the clientData URI.
149
+		// @see http://tools.ietf.org/html/rfc6749#section-3.1.2
150
+		// @see http://tools.ietf.org/html/rfc6749#section-4.1.2.1
151
+		// @see http://tools.ietf.org/html/rfc6749#section-4.2.2.1
152
+		if ($supplied_redirect_uri = $request->query('redirect_uri', $request->request('redirect_uri'))) {
153
+			// validate there is no fragment supplied
154
+			$parts = parse_url($supplied_redirect_uri);
155
+			if (isset($parts['fragment']) && $parts['fragment']) {
156
+				$response->setError(400, 'invalid_uri', 'The redirect URI must not contain a fragment');
157
+
158
+				return false;
159
+			}
160
+
161
+			// validate against the registered redirect uri(s) if available
162
+			if ($registered_redirect_uri && !$this->validateRedirectUri($supplied_redirect_uri, $registered_redirect_uri)) {
163
+				$response->setError(400, 'redirect_uri_mismatch', 'The redirect URI provided is missing or does not match', '#section-3.1.2');
164
+
165
+				return false;
166
+			}
167
+			$redirect_uri = $supplied_redirect_uri;
168
+		} else {
169
+			// use the registered redirect_uri if none has been supplied, if possible
170
+			if (!$registered_redirect_uri) {
171
+				$response->setError(400, 'invalid_uri', 'No redirect URI was supplied or stored');
172
+
173
+				return false;
174
+			}
175
+
176
+			if (count(explode(' ', $registered_redirect_uri)) > 1) {
177
+				$response->setError(400, 'invalid_uri', 'A redirect URI must be supplied when multiple redirect URIs are registered', '#section-3.1.2.3');
178
+
179
+				return false;
180
+			}
181
+			$redirect_uri = $registered_redirect_uri;
182
+		}
183
+
184
+		// Select the redirect URI
185
+		$response_type = $request->query('response_type', $request->request('response_type'));
186
+
187
+		// for multiple-valued response types - make them alphabetical
188
+		if (false !== strpos($response_type, ' ')) {
189
+			$types = explode(' ', $response_type);
190
+			sort($types);
191
+			$response_type = ltrim(implode(' ', $types));
192
+		}
193
+
194
+		$state = $request->query('state', $request->request('state'));
195
+
196
+		// type and client_id are required
197
+		if (!$response_type || !in_array($response_type, $this->getValidResponseTypes())) {
198
+			$response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'invalid_request', 'Invalid or missing response type', null);
199
+
200
+			return false;
201
+		}
202
+
203
+		if ($response_type == self::RESPONSE_TYPE_AUTHORIZATION_CODE) {
204
+			if (!isset($this->responseTypes['code'])) {
205
+				$response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'unsupported_response_type', 'authorization code grant type not supported', null);
206
+
207
+				return false;
208
+			}
209
+			if (!$this->clientStorage->checkRestrictedGrantType($client_id, 'authorization_code')) {
210
+				$response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'unauthorized_client', 'The grant type is unauthorized for this client_id', null);
211
+
212
+				return false;
213
+			}
214
+			if ($this->responseTypes['code']->enforceRedirect() && !$redirect_uri) {
215
+				$response->setError(400, 'redirect_uri_mismatch', 'The redirect URI is mandatory and was not supplied');
216
+
217
+				return false;
218
+			}
219
+		} else {
220
+			if (!$this->config['allow_implicit']) {
221
+				$response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'unsupported_response_type', 'implicit grant type not supported', null);
222
+
223
+				return false;
224
+			}
225
+			if (!$this->clientStorage->checkRestrictedGrantType($client_id, 'implicit')) {
226
+				$response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'unauthorized_client', 'The grant type is unauthorized for this client_id', null);
227
+
228
+				return false;
229
+			}
230
+		}
231
+
232
+		// validate requested scope if it exists
233
+		$requestedScope = $this->scopeUtil->getScopeFromRequest($request);
234
+
235
+		if ($requestedScope) {
236
+			// restrict scope by client specific scope if applicable,
237
+			// otherwise verify the scope exists
238
+			$clientScope = $this->clientStorage->getClientScope($client_id);
239
+			if ((is_null($clientScope) && !$this->scopeUtil->scopeExists($requestedScope))
240
+				|| ($clientScope && !$this->scopeUtil->checkScope($requestedScope, $clientScope))) {
241
+				$response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'invalid_scope', 'An unsupported scope was requested', null);
242
+
243
+				return false;
244
+			}
245
+		} else {
246
+			// use a globally-defined default scope
247
+			$defaultScope = $this->scopeUtil->getDefaultScope($client_id);
248
+
249
+			if (false === $defaultScope) {
250
+				$response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'invalid_client', 'This application requires you specify a scope parameter', null);
251
+
252
+				return false;
253
+			}
254
+
255
+			$requestedScope = $defaultScope;
256
+		}
257
+
258
+		// Validate state parameter exists (if configured to enforce this)
259
+		if ($this->config['enforce_state'] && !$state) {
260
+			$response->setRedirect($this->config['redirect_status_code'], $redirect_uri, null, 'invalid_request', 'The state parameter is required');
261
+
262
+			return false;
263
+		}
264
+
265
+		// save the input data and return true
266
+		$this->scope         = $requestedScope;
267
+		$this->state         = $state;
268
+		$this->client_id     = $client_id;
269
+		// Only save the SUPPLIED redirect URI (@see http://tools.ietf.org/html/rfc6749#section-4.1.3)
270
+		$this->redirect_uri  = $supplied_redirect_uri;
271
+		$this->response_type = $response_type;
272
+
273
+		return true;
274
+	}
275
+
276
+	/**
277
+	 * Build the absolute URI based on supplied URI and parameters.
278
+	 *
279
+	 * @param $uri    An absolute URI.
280
+	 * @param $params Parameters to be append as GET.
281
+	 *
282
+	 * @return
283
+	 * An absolute URI with supplied parameters.
284
+	 *
285
+	 * @ingroup oauth2_section_4
286
+	 */
287
+	private function buildUri($uri, $params)
288
+	{
289
+		$parse_url = parse_url($uri);
290
+
291
+		// Add our params to the parsed uri
292
+		foreach ($params as $k => $v) {
293
+			if (isset($parse_url[$k])) {
294
+				$parse_url[$k] .= "&" . http_build_query($v, '', '&');
295
+			} else {
296
+				$parse_url[$k] = http_build_query($v, '', '&');
297
+			}
298
+		}
299
+
300
+		// Put humpty dumpty back together
301
+		return
302
+			((isset($parse_url["scheme"])) ? $parse_url["scheme"] . "://" : "")
303
+			. ((isset($parse_url["user"])) ? $parse_url["user"]
304
+			. ((isset($parse_url["pass"])) ? ":" . $parse_url["pass"] : "") . "@" : "")
305
+			. ((isset($parse_url["host"])) ? $parse_url["host"] : "")
306
+			. ((isset($parse_url["port"])) ? ":" . $parse_url["port"] : "")
307
+			. ((isset($parse_url["path"])) ? $parse_url["path"] : "")
308
+			. ((isset($parse_url["query"]) && !empty($parse_url['query'])) ? "?" . $parse_url["query"] : "")
309
+			. ((isset($parse_url["fragment"])) ? "#" . $parse_url["fragment"] : "")
310
+		;
311
+	}
312
+
313
+	protected function getValidResponseTypes()
314
+	{
315
+		return array(
316
+			self::RESPONSE_TYPE_ACCESS_TOKEN,
317
+			self::RESPONSE_TYPE_AUTHORIZATION_CODE,
318
+		);
319
+	}
320
+
321
+	/**
322
+	 * Internal method for validating redirect URI supplied
323
+	 *
324
+	 * @param string $inputUri            The submitted URI to be validated
325
+	 * @param string $registeredUriString The allowed URI(s) to validate against.  Can be a space-delimited string of URIs to
326
+	 *                                    allow for multiple URIs
327
+	 *
328
+	 * @see http://tools.ietf.org/html/rfc6749#section-3.1.2
329
+	 */
330
+	protected function validateRedirectUri($inputUri, $registeredUriString)
331
+	{
332
+		if (!$inputUri || !$registeredUriString) {
333
+			return false; // if either one is missing, assume INVALID
334
+		}
335
+
336
+		$registered_uris = preg_split('/\s+/', $registeredUriString);
337
+		foreach ($registered_uris as $registered_uri) {
338
+			if ($this->config['require_exact_redirect_uri']) {
339
+				// the input uri is validated against the registered uri using exact match
340
+				if (strcmp($inputUri, $registered_uri) === 0) {
341
+					return true;
342
+				}
343
+			} else {
344
+				// the input uri is validated against the registered uri using case-insensitive match of the initial string
345
+				// i.e. additional query parameters may be applied
346
+				if (strcasecmp(substr($inputUri, 0, strlen($registered_uri)), $registered_uri) === 0) {
347
+					return true;
348
+				}
349
+			}
350
+		}
351
+
352
+		return false;
353
+	}
354
+
355
+	/**
356
+	 * Convenience methods to access the parameters derived from the validated request
357
+	 */
358
+
359
+	public function getScope()
360
+	{
361
+		return $this->scope;
362
+	}
363
+
364
+	public function getState()
365
+	{
366
+		return $this->state;
367
+	}
368
+
369
+	public function getClientId()
370
+	{
371
+		return $this->client_id;
372
+	}
373
+
374
+	public function getRedirectUri()
375
+	{
376
+		return $this->redirect_uri;
377
+	}
378
+
379
+	public function getResponseType()
380
+	{
381
+		return $this->response_type;
382
+	}
383 383
 }
Please login to merge, or discard this patch.
extensions/libraries/redcore/api/oauth2/Controller/TokenController.php 1 patch
Indentation   +255 added lines, -255 removed lines patch added patch discarded remove patch
@@ -16,259 +16,259 @@
 block discarded – undo
16 16
  */
17 17
 class TokenController implements TokenControllerInterface
18 18
 {
19
-    protected $accessToken;
20
-    protected $grantTypes;
21
-    protected $clientAssertionType;
22
-    protected $scopeUtil;
23
-    protected $clientStorage;
24
-
25
-    public function __construct(AccessTokenInterface $accessToken, ClientInterface $clientStorage, array $grantTypes = array(), ClientAssertionTypeInterface $clientAssertionType = null, ScopeInterface $scopeUtil = null)
26
-    {
27
-        if (is_null($clientAssertionType)) {
28
-            foreach ($grantTypes as $grantType) {
29
-                if (!$grantType instanceof ClientAssertionTypeInterface) {
30
-                    throw new \InvalidArgumentException('You must supply an instance of OAuth2\ClientAssertionType\ClientAssertionTypeInterface or only use grant types which implement OAuth2\ClientAssertionType\ClientAssertionTypeInterface');
31
-                }
32
-            }
33
-        }
34
-        $this->clientAssertionType = $clientAssertionType;
35
-        $this->accessToken = $accessToken;
36
-        $this->clientStorage = $clientStorage;
37
-        foreach ($grantTypes as $grantType) {
38
-            $this->addGrantType($grantType);
39
-        }
40
-
41
-        if (is_null($scopeUtil)) {
42
-            $scopeUtil = new Scope();
43
-        }
44
-        $this->scopeUtil = $scopeUtil;
45
-    }
46
-
47
-    public function handleTokenRequest(RequestInterface $request, ResponseInterface $response)
48
-    {
49
-        if ($token = $this->grantAccessToken($request, $response)) {
50
-            // @see http://tools.ietf.org/html/rfc6749#section-5.1
51
-            // server MUST disable caching in headers when tokens are involved
52
-            $response->setStatusCode(200);
53
-            $response->addParameters($token);
54
-            $response->addHttpHeaders(array('Cache-Control' => 'no-store', 'Pragma' => 'no-cache'));
55
-        }
56
-    }
57
-
58
-    /**
59
-     * Grant or deny a requested access token.
60
-     * This would be called from the "/token" endpoint as defined in the spec.
61
-     * You can call your endpoint whatever you want.
62
-     *
63
-     * @param $request - RequestInterface
64
-     * Request object to grant access token
65
-     *
66
-     * @throws InvalidArgumentException
67
-     * @throws LogicException
68
-     *
69
-     * @see http://tools.ietf.org/html/rfc6749#section-4
70
-     * @see http://tools.ietf.org/html/rfc6749#section-10.6
71
-     * @see http://tools.ietf.org/html/rfc6749#section-4.1.3
72
-     *
73
-     * @ingroup oauth2_section_4
74
-     */
75
-    public function grantAccessToken(RequestInterface $request, ResponseInterface $response)
76
-    {
77
-        if (strtolower($request->server('REQUEST_METHOD')) != 'post') {
78
-            $response->setError(405, 'invalid_request', 'The request method must be POST when requesting an access token', '#section-3.2');
79
-            $response->addHttpHeaders(array('Allow' => 'POST'));
80
-
81
-            return null;
82
-        }
83
-
84
-        /**
85
-         * Determine grant type from request
86
-         * and validate the request for that grant type
87
-         */
88
-        if (!$grantTypeIdentifier = $request->request('grant_type')) {
89
-            $response->setError(400, 'invalid_request', 'The grant type was not specified in the request');
90
-
91
-            return null;
92
-        }
93
-
94
-        if (!isset($this->grantTypes[$grantTypeIdentifier])) {
95
-            /* TODO: If this is an OAuth2 supported grant type that we have chosen not to implement, throw a 501 Not Implemented instead */
96
-            $response->setError(400, 'unsupported_grant_type', sprintf('Grant type "%s" not supported', $grantTypeIdentifier));
97
-
98
-            return null;
99
-        }
100
-
101
-        $grantType = $this->grantTypes[$grantTypeIdentifier];
102
-
103
-        /**
104
-         * Retrieve the client information from the request
105
-         * ClientAssertionTypes allow for grant types which also assert the client data
106
-         * in which case ClientAssertion is handled in the validateRequest method
107
-         *
108
-         * @see OAuth2\GrantType\JWTBearer
109
-         * @see OAuth2\GrantType\ClientCredentials
110
-         */
111
-        if (!$grantType instanceof ClientAssertionTypeInterface) {
112
-            if (!$this->clientAssertionType->validateRequest($request, $response)) {
113
-                return null;
114
-            }
115
-            $clientId = $this->clientAssertionType->getClientId();
116
-        }
117
-
118
-        /**
119
-         * Retrieve the grant type information from the request
120
-         * The GrantTypeInterface object handles all validation
121
-         * If the object is an instance of ClientAssertionTypeInterface,
122
-         * That logic is handled here as well
123
-         */
124
-        if (!$grantType->validateRequest($request, $response)) {
125
-            return null;
126
-        }
127
-
128
-        if ($grantType instanceof ClientAssertionTypeInterface) {
129
-            $clientId = $grantType->getClientId();
130
-        } else {
131
-            // validate the Client ID (if applicable)
132
-            if (!is_null($storedClientId = $grantType->getClientId()) && $storedClientId != $clientId) {
133
-                $response->setError(400, 'invalid_grant', sprintf('%s doesn\'t exist or is invalid for the client', $grantTypeIdentifier));
134
-
135
-                return null;
136
-            }
137
-        }
138
-
139
-        /**
140
-         * Validate the client can use the requested grant type
141
-         */
142
-        if (!$this->clientStorage->checkRestrictedGrantType($clientId, $grantTypeIdentifier)) {
143
-            $response->setError(400, 'unauthorized_client', 'The grant type is unauthorized for this client_id');
144
-
145
-            return false;
146
-        }
147
-
148
-        /**
149
-         * Validate the scope of the token
150
-         *
151
-         * requestedScope - the scope specified in the token request
152
-         * availableScope - the scope associated with the grant type
153
-         *  ex: in the case of the "Authorization Code" grant type,
154
-         *  the scope is specified in the authorize request
155
-         *
156
-         * @see http://tools.ietf.org/html/rfc6749#section-3.3
157
-         */
158
-
159
-        $requestedScope = $this->scopeUtil->getScopeFromRequest($request);
160
-        $availableScope = $grantType->getScope();
161
-
162
-        if ($requestedScope) {
163
-            // validate the requested scope
164
-            if ($availableScope) {
165
-                if (!$this->scopeUtil->checkScope($requestedScope, $availableScope)) {
166
-                    $response->setError(400, 'invalid_scope', 'The scope requested is invalid for this request');
167
-
168
-                    return null;
169
-                }
170
-            } else {
171
-                // validate the client has access to this scope
172
-                if ($clientScope = $this->clientStorage->getClientScope($clientId)) {
173
-                    if (!$this->scopeUtil->checkScope($requestedScope, $clientScope)) {
174
-                        $response->setError(400, 'invalid_scope', 'The scope requested is invalid for this client');
175
-
176
-                        return false;
177
-                    }
178
-                } elseif (!$this->scopeUtil->scopeExists($requestedScope)) {
179
-                    $response->setError(400, 'invalid_scope', 'An unsupported scope was requested');
180
-
181
-                    return null;
182
-                }
183
-            }
184
-        } elseif ($availableScope) {
185
-            // use the scope associated with this grant type
186
-            $requestedScope = $availableScope;
187
-        } else {
188
-            // use a globally-defined default scope
189
-            $defaultScope = $this->scopeUtil->getDefaultScope($clientId);
190
-
191
-            // "false" means default scopes are not allowed
192
-            if (false === $defaultScope) {
193
-                $response->setError(400, 'invalid_scope', 'This application requires you specify a scope parameter');
194
-
195
-                return null;
196
-            }
197
-
198
-            $requestedScope = $defaultScope;
199
-        }
200
-
201
-        return $grantType->createAccessToken($this->accessToken, $clientId, $grantType->getUserId(), $requestedScope);
202
-    }
203
-
204
-    /**
205
-     * addGrantType
206
-     *
207
-     * @param grantType - OAuth2\GrantTypeInterface
208
-     * the grant type to add for the specified identifier
209
-     * @param identifier - string
210
-     * a string passed in as "grant_type" in the response that will call this grantType
211
-     */
212
-    public function addGrantType(GrantTypeInterface $grantType, $identifier = null)
213
-    {
214
-        if (is_null($identifier) || is_numeric($identifier)) {
215
-            $identifier = $grantType->getQuerystringIdentifier();
216
-        }
217
-
218
-        $this->grantTypes[$identifier] = $grantType;
219
-    }
220
-
221
-    public function handleRevokeRequest(RequestInterface $request, ResponseInterface $response)
222
-    {
223
-        if ($this->revokeToken($request, $response)) {
224
-            $response->setStatusCode(200);
225
-            $response->addParameters(array('revoked' => true));
226
-        }
227
-    }
228
-
229
-    /**
230
-     * Revoke a refresh or access token. Returns true on success and when tokens are invalid
231
-     *
232
-     * Note: invalid tokens do not cause an error response since the client
233
-     * cannot handle such an error in a reasonable way.  Moreover, the
234
-     * purpose of the revocation request, invalidating the particular token,
235
-     * is already achieved.
236
-     *
237
-     * @param RequestInterface $request
238
-     * @param ResponseInterface $response
239
-     * @return bool|null
240
-     */
241
-    public function revokeToken(RequestInterface $request, ResponseInterface $response)
242
-    {
243
-        if (strtolower($request->server('REQUEST_METHOD')) != 'post') {
244
-            $response->setError(405, 'invalid_request', 'The request method must be POST when revoking an access token', '#section-3.2');
245
-            $response->addHttpHeaders(array('Allow' => 'POST'));
246
-
247
-            return null;
248
-        }
249
-
250
-        $token_type_hint = $request->request('token_type_hint');
251
-        if (!in_array($token_type_hint, array(null, 'access_token', 'refresh_token'), true)) {
252
-            $response->setError(400, 'invalid_request', 'Token type hint must be either \'access_token\' or \'refresh_token\'');
253
-
254
-            return null;
255
-        }
256
-
257
-        $token = $request->request('token');
258
-        if ($token === null) {
259
-            $response->setError(400, 'invalid_request', 'Missing token parameter to revoke');
260
-
261
-            return null;
262
-        }
263
-
264
-        // @todo remove this check for v2.0
265
-        if (!method_exists($this->accessToken, 'revokeToken')) {
266
-            $class = get_class($this->accessToken);
267
-            throw new \RuntimeException("AccessToken {$class} does not implement required revokeToken method");
268
-        }
269
-
270
-        $this->accessToken->revokeToken($token, $token_type_hint);
271
-
272
-        return true;
273
-    }
19
+	protected $accessToken;
20
+	protected $grantTypes;
21
+	protected $clientAssertionType;
22
+	protected $scopeUtil;
23
+	protected $clientStorage;
24
+
25
+	public function __construct(AccessTokenInterface $accessToken, ClientInterface $clientStorage, array $grantTypes = array(), ClientAssertionTypeInterface $clientAssertionType = null, ScopeInterface $scopeUtil = null)
26
+	{
27
+		if (is_null($clientAssertionType)) {
28
+			foreach ($grantTypes as $grantType) {
29
+				if (!$grantType instanceof ClientAssertionTypeInterface) {
30
+					throw new \InvalidArgumentException('You must supply an instance of OAuth2\ClientAssertionType\ClientAssertionTypeInterface or only use grant types which implement OAuth2\ClientAssertionType\ClientAssertionTypeInterface');
31
+				}
32
+			}
33
+		}
34
+		$this->clientAssertionType = $clientAssertionType;
35
+		$this->accessToken = $accessToken;
36
+		$this->clientStorage = $clientStorage;
37
+		foreach ($grantTypes as $grantType) {
38
+			$this->addGrantType($grantType);
39
+		}
40
+
41
+		if (is_null($scopeUtil)) {
42
+			$scopeUtil = new Scope();
43
+		}
44
+		$this->scopeUtil = $scopeUtil;
45
+	}
46
+
47
+	public function handleTokenRequest(RequestInterface $request, ResponseInterface $response)
48
+	{
49
+		if ($token = $this->grantAccessToken($request, $response)) {
50
+			// @see http://tools.ietf.org/html/rfc6749#section-5.1
51
+			// server MUST disable caching in headers when tokens are involved
52
+			$response->setStatusCode(200);
53
+			$response->addParameters($token);
54
+			$response->addHttpHeaders(array('Cache-Control' => 'no-store', 'Pragma' => 'no-cache'));
55
+		}
56
+	}
57
+
58
+	/**
59
+	 * Grant or deny a requested access token.
60
+	 * This would be called from the "/token" endpoint as defined in the spec.
61
+	 * You can call your endpoint whatever you want.
62
+	 *
63
+	 * @param $request - RequestInterface
64
+	 * Request object to grant access token
65
+	 *
66
+	 * @throws InvalidArgumentException
67
+	 * @throws LogicException
68
+	 *
69
+	 * @see http://tools.ietf.org/html/rfc6749#section-4
70
+	 * @see http://tools.ietf.org/html/rfc6749#section-10.6
71
+	 * @see http://tools.ietf.org/html/rfc6749#section-4.1.3
72
+	 *
73
+	 * @ingroup oauth2_section_4
74
+	 */
75
+	public function grantAccessToken(RequestInterface $request, ResponseInterface $response)
76
+	{
77
+		if (strtolower($request->server('REQUEST_METHOD')) != 'post') {
78
+			$response->setError(405, 'invalid_request', 'The request method must be POST when requesting an access token', '#section-3.2');
79
+			$response->addHttpHeaders(array('Allow' => 'POST'));
80
+
81
+			return null;
82
+		}
83
+
84
+		/**
85
+		 * Determine grant type from request
86
+		 * and validate the request for that grant type
87
+		 */
88
+		if (!$grantTypeIdentifier = $request->request('grant_type')) {
89
+			$response->setError(400, 'invalid_request', 'The grant type was not specified in the request');
90
+
91
+			return null;
92
+		}
93
+
94
+		if (!isset($this->grantTypes[$grantTypeIdentifier])) {
95
+			/* TODO: If this is an OAuth2 supported grant type that we have chosen not to implement, throw a 501 Not Implemented instead */
96
+			$response->setError(400, 'unsupported_grant_type', sprintf('Grant type "%s" not supported', $grantTypeIdentifier));
97
+
98
+			return null;
99
+		}
100
+
101
+		$grantType = $this->grantTypes[$grantTypeIdentifier];
102
+
103
+		/**
104
+		 * Retrieve the client information from the request
105
+		 * ClientAssertionTypes allow for grant types which also assert the client data
106
+		 * in which case ClientAssertion is handled in the validateRequest method
107
+		 *
108
+		 * @see OAuth2\GrantType\JWTBearer
109
+		 * @see OAuth2\GrantType\ClientCredentials
110
+		 */
111
+		if (!$grantType instanceof ClientAssertionTypeInterface) {
112
+			if (!$this->clientAssertionType->validateRequest($request, $response)) {
113
+				return null;
114
+			}
115
+			$clientId = $this->clientAssertionType->getClientId();
116
+		}
117
+
118
+		/**
119
+		 * Retrieve the grant type information from the request
120
+		 * The GrantTypeInterface object handles all validation
121
+		 * If the object is an instance of ClientAssertionTypeInterface,
122
+		 * That logic is handled here as well
123
+		 */
124
+		if (!$grantType->validateRequest($request, $response)) {
125
+			return null;
126
+		}
127
+
128
+		if ($grantType instanceof ClientAssertionTypeInterface) {
129
+			$clientId = $grantType->getClientId();
130
+		} else {
131
+			// validate the Client ID (if applicable)
132
+			if (!is_null($storedClientId = $grantType->getClientId()) && $storedClientId != $clientId) {
133
+				$response->setError(400, 'invalid_grant', sprintf('%s doesn\'t exist or is invalid for the client', $grantTypeIdentifier));
134
+
135
+				return null;
136
+			}
137
+		}
138
+
139
+		/**
140
+		 * Validate the client can use the requested grant type
141
+		 */
142
+		if (!$this->clientStorage->checkRestrictedGrantType($clientId, $grantTypeIdentifier)) {
143
+			$response->setError(400, 'unauthorized_client', 'The grant type is unauthorized for this client_id');
144
+
145
+			return false;
146
+		}
147
+
148
+		/**
149
+		 * Validate the scope of the token
150
+		 *
151
+		 * requestedScope - the scope specified in the token request
152
+		 * availableScope - the scope associated with the grant type
153
+		 *  ex: in the case of the "Authorization Code" grant type,
154
+		 *  the scope is specified in the authorize request
155
+		 *
156
+		 * @see http://tools.ietf.org/html/rfc6749#section-3.3
157
+		 */
158
+
159
+		$requestedScope = $this->scopeUtil->getScopeFromRequest($request);
160
+		$availableScope = $grantType->getScope();
161
+
162
+		if ($requestedScope) {
163
+			// validate the requested scope
164
+			if ($availableScope) {
165
+				if (!$this->scopeUtil->checkScope($requestedScope, $availableScope)) {
166
+					$response->setError(400, 'invalid_scope', 'The scope requested is invalid for this request');
167
+
168
+					return null;
169
+				}
170
+			} else {
171
+				// validate the client has access to this scope
172
+				if ($clientScope = $this->clientStorage->getClientScope($clientId)) {
173
+					if (!$this->scopeUtil->checkScope($requestedScope, $clientScope)) {
174
+						$response->setError(400, 'invalid_scope', 'The scope requested is invalid for this client');
175
+
176
+						return false;
177
+					}
178
+				} elseif (!$this->scopeUtil->scopeExists($requestedScope)) {
179
+					$response->setError(400, 'invalid_scope', 'An unsupported scope was requested');
180
+
181
+					return null;
182
+				}
183
+			}
184
+		} elseif ($availableScope) {
185
+			// use the scope associated with this grant type
186
+			$requestedScope = $availableScope;
187
+		} else {
188
+			// use a globally-defined default scope
189
+			$defaultScope = $this->scopeUtil->getDefaultScope($clientId);
190
+
191
+			// "false" means default scopes are not allowed
192
+			if (false === $defaultScope) {
193
+				$response->setError(400, 'invalid_scope', 'This application requires you specify a scope parameter');
194
+
195
+				return null;
196
+			}
197
+
198
+			$requestedScope = $defaultScope;
199
+		}
200
+
201
+		return $grantType->createAccessToken($this->accessToken, $clientId, $grantType->getUserId(), $requestedScope);
202
+	}
203
+
204
+	/**
205
+	 * addGrantType
206
+	 *
207
+	 * @param grantType - OAuth2\GrantTypeInterface
208
+	 * the grant type to add for the specified identifier
209
+	 * @param identifier - string
210
+	 * a string passed in as "grant_type" in the response that will call this grantType
211
+	 */
212
+	public function addGrantType(GrantTypeInterface $grantType, $identifier = null)
213
+	{
214
+		if (is_null($identifier) || is_numeric($identifier)) {
215
+			$identifier = $grantType->getQuerystringIdentifier();
216
+		}
217
+
218
+		$this->grantTypes[$identifier] = $grantType;
219
+	}
220
+
221
+	public function handleRevokeRequest(RequestInterface $request, ResponseInterface $response)
222
+	{
223
+		if ($this->revokeToken($request, $response)) {
224
+			$response->setStatusCode(200);
225
+			$response->addParameters(array('revoked' => true));
226
+		}
227
+	}
228
+
229
+	/**
230
+	 * Revoke a refresh or access token. Returns true on success and when tokens are invalid
231
+	 *
232
+	 * Note: invalid tokens do not cause an error response since the client
233
+	 * cannot handle such an error in a reasonable way.  Moreover, the
234
+	 * purpose of the revocation request, invalidating the particular token,
235
+	 * is already achieved.
236
+	 *
237
+	 * @param RequestInterface $request
238
+	 * @param ResponseInterface $response
239
+	 * @return bool|null
240
+	 */
241
+	public function revokeToken(RequestInterface $request, ResponseInterface $response)
242
+	{
243
+		if (strtolower($request->server('REQUEST_METHOD')) != 'post') {
244
+			$response->setError(405, 'invalid_request', 'The request method must be POST when revoking an access token', '#section-3.2');
245
+			$response->addHttpHeaders(array('Allow' => 'POST'));
246
+
247
+			return null;
248
+		}
249
+
250
+		$token_type_hint = $request->request('token_type_hint');
251
+		if (!in_array($token_type_hint, array(null, 'access_token', 'refresh_token'), true)) {
252
+			$response->setError(400, 'invalid_request', 'Token type hint must be either \'access_token\' or \'refresh_token\'');
253
+
254
+			return null;
255
+		}
256
+
257
+		$token = $request->request('token');
258
+		if ($token === null) {
259
+			$response->setError(400, 'invalid_request', 'Missing token parameter to revoke');
260
+
261
+			return null;
262
+		}
263
+
264
+		// @todo remove this check for v2.0
265
+		if (!method_exists($this->accessToken, 'revokeToken')) {
266
+			$class = get_class($this->accessToken);
267
+			throw new \RuntimeException("AccessToken {$class} does not implement required revokeToken method");
268
+		}
269
+
270
+		$this->accessToken->revokeToken($token, $token_type_hint);
271
+
272
+		return true;
273
+	}
274 274
 }
Please login to merge, or discard this patch.