/** * Fetches the JSON web key set from the `jwks_uri` parameter. */ public function fetchJWKs() { if (isset($this->container['oauth']['jwks_uri'])) { $web = \Web::instance(); $response = new HTTPResponse($web->request($this->container['oauth']['jwks_uri'], array('headers' => array('Accept' => 'application/jwk-set+json,application/json,text/plain,application/octet-stream')))); if ($response->isHttpError()) { return; } $jwks = json_decode($response->getBody(), true); if ($jwks == NULL) { return; } $this->container['oauth']['jwks'] = $jwks; } }
/** * Verifies a sector identifier URI. * * This function retrieves the JSON document specified by `$sector_identifier_uri` and checks * whether the URIs in that document are contained in `$expected_redirect_uris` * * @param string $sector_identifier_uri the sector identifier URI * @param array $expected_redirect_uris an array of URIs that the document in `$sector_identifier_uri` * is expected to match * @return bool true if the sector identifier is verified */ protected function verifySectorIdentifier($sector_identifier_uri, $expected_redirect_uris) { $web = \Web::instance(); $this->logger->log(LogLevel::INFO, 'OAuth dynamic client registration request: verifying OpenID Connect sector_identifier_uri ' . $sector_identifier_uri); if (parse_url($sector_identifier_uri, PHP_URL_SCHEME) != 'https') { $this->logger->log(LogLevel::ERROR, 'Not https:' . $sector_identifier_uri); return false; } $response = new HTTPResponse($web->request($sector_identifier_uri, array('headers' => array('Accept' => 'application/json')))); if ($response->isHttpError()) { $this->logger->log(LogLevel::ERROR, 'Cannot retrieve sector_identifier_uri:' . $sector_identifier_uri); return false; } $test_redirect_uris = json_decode($response->getBody(), true); if ($test_redirect_uris == NULL) { $this->logger->log(LogLevel::ERROR, 'Invalid sector_identifier_uri: not valid JSON'); return false; } elseif (count(array_diff($expected_redirect_uris, $test_redirect_uris)) > 0 || count(array_diff($test_redirect_uris, $expected_redirect_uris)) > 0) { $this->logger->log(LogLevel::ERROR, 'Redirect URIs in sector_identifier_uri do not match redirect_uris'); return false; } else { $this->logger->log(LogLevel::DEBUG, 'sector_identifier_uri verified'); return true; } }
/** * Parses and returns the request's `Authorization` header. * * This method extracts the request's `Authorization` header and returns an * array with the following elements: * * - `#scheme` - the authentication scheme, e.g. Basic, Bearer * - `#credentials` - the credentials following the scheme * * If `$parse_credentials` is true, the method will also attempt to parse * the credential information. For the `Basic` scheme, the user name and * password will be returned in the array as `#username` and `#password` * respectively. For other schemes with delimited name-value parameters, * those name-value pairs will be returned. * * @param bool $parse_credentials whether to parse the credential information * @return array the parsed `Authorization` header, or `null` if none * exists */ public function getAuthorizationHeader($parse_credentials = false) { if (!$this->hasHeader('Authorization')) { return null; } $results = array(); $header = $this->getHeader('Authorization'); list($scheme, $credentials) = preg_split('/\\s+/', $header, 2); $results['#scheme'] = HTTPResponse::httpCase($scheme); $results['#credentials'] = $credentials; if ($parse_credentials) { if ($results['#scheme'] == 'Basic') { list($username, $password) = explode(':', base64_decode($credentials)); $results['#username'] = $username; $results['#password'] = $password; } else { $matches = array(); preg_match_all('/([-a-zA-Z]+)=\\"([^\\"]+)\\"/', $credentials, $matches, PREG_SET_ORDER); foreach ($matches as $match) { $results[$match[1]] = $match[2]; } } } return $results; }
/** * @see SimpleID\API\OAuthHooks::oAuthResolveAuthRequestHook() */ public function oAuthResolveAuthRequestHook($request, $response) { $store = StoreManager::instance(); $web = Web::instance(); // 1. Check if request_uri parameter is present. If so, fetch the JWT // from this URL and place it in the request parameter if (isset($request['request_uri'])) { $this->logger->log(LogLevel::INFO, 'OpenID request object: getting object from ' . $request['request_uri']); $parts = parse_url($request['request_uri']); $http_response = new HTTPResponse($web->request($request['request_uri'], array('headers' => array('Accept' => 'application/jwt,text/plain,application/octet-stream')))); if ($http_response->isHTTPError()) { $this->logger->log(LogLevel::ERROR, 'Cannot retrieve request file from request_uri:' . $request['request_uri']); $response->setError('invalid_request_uri', 'cannot retrieve request file from request_uri'); return; } $request['request'] = $http_response->getBody(); unset($request['request_uri']); } // 2. Check if the request parameter is present. If so, we are dealing with // an additional OpenID Connect request object. We need to parse this object if (isset($request['request'])) { $this->logger->log(LogLevel::INFO, 'OpenID request object token: ' . $request['request']); $client = $store->loadClient($request['client_id'], 'SimpleID\\Protocols\\OAuth\\OAuthClient'); if (!isset($client['connect']['request_object_signing_alg'])) { $this->logger->log(LogLevel::ERROR, 'Invalid OpenID request object: signature algorithm not registered'); $response->setError('invalid_openid_request_object', 'signature algorithm not registered'); return; } $jwt_alg = isset($client['connect']['request_object_signing_alg']) ? $client['connect']['request_object_signing_alg'] : null; $jwe_alg = isset($client['connect']['request_object_encryption_alg']) ? $client['connect']['request_object_encryption_alg'] : null; $builder = new KeySetBuilder($client); $set = $builder->addClientSecret()->addClientPublicKeys()->addServerPrivateKeys()->toKeySet(); try { AlgorithmFactory::addNoneAlg(); $helper = new Helper($request['request']); $jwt = $helper->getJWTObject($set, $jwe_alg, $jwt_alg); $request->loadData($jwt->getClaims()); } catch (\UnexpectedValueException $e) { $this->logger->log(LogLevel::ERROR, 'Invalid OpenID request object: ' . $e->getMessage()); $response->setError('invalid_openid_request_object', $e->getMessage()); return; } } AlgorithmFactory::removeNoneAlg(); // 3. nonce if ($request->paramContains('scope', 'openid') && $request->paramContains('response_type', 'token') && !isset($request['nonce'])) { $this->logger->log(LogLevel::ERROR, 'Protocol Error: nonce not set when using implicit flow'); $response->setError('invalid_request', 'nonce not set when using implicit flow')->renderRedirect(); return; } }