Skip to content

Commit

Permalink
Merge pull request #750 from nextcloud/fix/validate-discovery-endpoint
Browse files Browse the repository at this point in the history
Ensure the discovery endpoint is validated and all required fields ar…
  • Loading branch information
julien-nc authored Jan 23, 2024
2 parents 556b71d + 10c0ea2 commit 3a51adb
Showing 1 changed file with 66 additions and 1 deletion.
67 changes: 66 additions & 1 deletion lib/Controller/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

namespace OCA\UserOIDC\Controller;

use Exception;
use OCA\UserOIDC\AppInfo\Application;
use OCA\UserOIDC\Db\Provider;
use OCA\UserOIDC\Db\ProviderMapper;
Expand All @@ -34,8 +35,10 @@
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse;
use OCP\Http\Client\IClientService;
use OCP\IRequest;
use OCP\Security\ICrypto;
use Psr\Log\LoggerInterface;

class SettingsController extends Controller {

Expand All @@ -47,20 +50,64 @@ class SettingsController extends Controller {
private $providerService;
/** @var ICrypto */
private $crypto;
/** @var IClientService */
private $clientService;
/** @var LoggerInterface */
private $logger;

public function __construct(
IRequest $request,
ProviderMapper $providerMapper,
ID4MeService $id4meService,
ProviderService $providerService,
ICrypto $crypto
ICrypto $crypto,
IClientService $clientService,
LoggerInterface $logger
) {
parent::__construct(Application::APP_ID, $request);

$this->providerMapper = $providerMapper;
$this->id4meService = $id4meService;
$this->providerService = $providerService;
$this->crypto = $crypto;
$this->clientService = $clientService;
$this->logger = $logger;
}

public function isDiscoveryEndpointValid($url) {
$result = [
'isReachable' => false,
'missingFields' => [],
];

try {
$client = $this->clientService->newClient();
$response = $client->get($url);
$httpCode = $response->getStatusCode();
$body = $response->getBody();

// Check if the request was successful
if ($httpCode === Http::STATUS_OK && !empty($body)) {
$result['isReachable'] = true;
$data = json_decode($body, true);

// Check for required fields as defined in: https://openid.net/specs/openid-connect-discovery-1_0.html
$requiredFields = [
'issuer', 'authorization_endpoint', 'token_endpoint', 'jwks_uri',
'response_types_supported', 'subject_types_supported', 'id_token_signing_alg_values_supported',
];

foreach ($requiredFields as $field) {
if (!isset($data[$field])) {
$result['missingFields'][] = $field;
}
}
}
} catch (Exception $e) {
$this->logger->error('Discovery endpoint validation error', ['exception' => $e]);
}

return $result;
}

public function createProvider(string $identifier, string $clientId, string $clientSecret, string $discoveryEndpoint,
Expand All @@ -69,6 +116,15 @@ public function createProvider(string $identifier, string $clientId, string $cli
return new JSONResponse(['message' => 'Provider with the given identifier already exists'], Http::STATUS_CONFLICT);
}

$result = $this->isDiscoveryEndpointValid($discoveryEndpoint);
if (!$result['isReachable']) {
$message = 'The discovery endpoint is not reachable.';
return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
} elseif (!empty($result['missingFields'])) {
$message = 'Invalid discovery endpoint. Missing fields: ' . implode(', ', $result['missingFields']);
return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
}

$provider = new Provider();
$provider->setIdentifier($identifier);
$provider->setClientId($clientId);
Expand All @@ -92,6 +148,15 @@ public function updateProvider(int $providerId, string $identifier, string $clie
return new JSONResponse(['message' => 'Provider with the given identifier does not exist'], Http::STATUS_NOT_FOUND);
}

$result = $this->isDiscoveryEndpointValid($discoveryEndpoint);
if (!$result['isReachable']) {
$message = 'The discovery endpoint is not reachable.';
return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
} elseif (!empty($result['missingFields'])) {
$message = 'Invalid discovery endpoint. Missing fields: ' . implode(', ', $result['missingFields']);
return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
}

$provider->setIdentifier($identifier);
$provider->setClientId($clientId);
if ($clientSecret) {
Expand Down

0 comments on commit 3a51adb

Please sign in to comment.