Skip to content

Commit

Permalink
chore: improve testing and override fetch url to use Guzzle (#21)
Browse files Browse the repository at this point in the history
* chore: improve testing

* Update description of getRoute function

* Move descriptions to docblock

* Add additional test for nonce, state and code challenge

* Cleanup

* Override fetchUrl and add test for id token logic

* Generate JWT in test

* Run linter

* Improve test with signed id token

* Reformat headers

* Add additional request tests

* Add some description

* Use tls_verify config option for ca path
  • Loading branch information
ricklambrechts authored Apr 23, 2024
1 parent 71bf08f commit 59b1fc6
Show file tree
Hide file tree
Showing 6 changed files with 402 additions and 14 deletions.
10 changes: 7 additions & 3 deletions config/oidc.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,13 @@
],

/**
* TLS Verify
* Can be disabled for local development.
* Is used in OpenIDConfigurationLoader and in the ServiceProvider for OpenIDConnectClient.
* TLS Verify - Used as the verify option for Guzzle.
*
* Default is true and verifies the certificate and uses the default CA bundle of the system.
* When set to `false` it disables the certificate verification (this is insecure!).
* When set to a path of a CA bundle, the custom certificate is used.
*
* @link https://docs.guzzlephp.org/en/latest/request-options.html#verify
*/
'tls_verify' => env('OIDC_TLS_VERIFY', true),
];
8 changes: 4 additions & 4 deletions src/OpenIDConfiguration/OpenIDConfigurationLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public function __construct(
protected string $issuer,
protected ?Repository $cacheStore = null,
protected int $cacheTtl = 3600,
protected bool $tlsVerify = true,
protected bool|string $tlsVerify = true,
) {
}

Expand All @@ -39,9 +39,9 @@ protected function getConfigurationFromIssuer(): OpenIDConfiguration
$url = $this->getOpenIDConfigurationUrl();

$response = Http::baseUrl($this->issuer)
->when(!$this->tlsVerify, function ($pendingRequest) {
return $pendingRequest->withoutVerifying();
})
->withOptions([
'verify' => $this->tlsVerify
])
->get($url);

if (!$response->successful()) {
Expand Down
104 changes: 103 additions & 1 deletion src/OpenIDConnectClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
use Jumbojett\OpenIDConnectClientException;
Expand All @@ -19,6 +20,15 @@ class OpenIDConnectClient extends \Jumbojett\OpenIDConnectClient
{
protected ?JweDecryptInterface $jweDecrypter;
protected ?OpenIDConfiguration $openIDConfiguration;
/**
* @var int|null Response code from the server
*/
protected ?int $responseCode;

/**
* @var string|null Content type from the server
*/
private ?string $responseContentType;

public function __construct(
?string $providerUrl = null,
Expand Down Expand Up @@ -95,7 +105,6 @@ protected function handleJweResponse($jwe): string
* @param string|string[]|bool|null $default optional
* @throws OpenIDConnectClientException
* @return string|string[]|bool
* @psalm-suppress ImplementedReturnTypeMismatch
*/
protected function getWellKnownConfigValue($param, $default = null): string|array|bool
{
Expand Down Expand Up @@ -156,4 +165,97 @@ protected function getAuthorizationEndpoint(): string

return $authorizationEndpoint;
}

/**
* Override the fetchURL method to use Laravel HTTP client.
* This uses Guzzle in the background.
*
* @param string $url
* @param string | null $post_body string If this is set the post type will be POST
* @param array<array-key, mixed> $headers Extra headers to be sent with the request.
* @return string
* @throws OpenIDConnectClientException
*/
protected function fetchURL(string $url, string $post_body = null, array $headers = []): string
{
$pendingRequest = Http::withUserAgent($this->getUserAgent())
->timeout($this->timeOut)
->withOptions([
'verify' => $this->getCertPath() ?: $this->getVerifyPeer()
]);

if (count($headers) > 0) {
$pendingRequest->withHeaders($this->reformatHeaders($headers));
}

if ($post_body === null) {
$request = $pendingRequest->get($url);
} else {
$isJson = is_object(json_decode($post_body, false));

$request = $pendingRequest
->withBody($post_body, $isJson ? 'application/json' : 'application/x-www-form-urlencoded')
->post($url);
}

$this->responseCode = $request->status();
$this->responseContentType = $request->header('Content-Type');

if ($request->failed()) {
throw new OpenIDConnectClientException(
'Request failed with status code ' . $this->responseCode . ' ' . $request->reason()
);
}

return $request->body();
}

/**
* Get the response code from last action/curl request.
*
* @return int
*/
public function getResponseCode(): int
{
return $this->responseCode ?? 0;
}

/**
* Get the content type from last action/curl request.
*
* @return string|null
*/
public function getResponseContentType(): ?string
{
return $this->responseContentType;
}

public function setTlsVerify(bool|string $tlsVerify): void
{
$verify = (bool)$tlsVerify;
$this->setVerifyHost($verify);
$this->setVerifyPeer($verify);

if (is_string($tlsVerify)) {
$this->setCertPath($tlsVerify);
}
}

/**
* Reformat the headers from string to array for Guzzle.
*
* @param array<string> $headers
* @return array<string, array<int, string>>
*/
protected function reformatHeaders(array $headers): array
{
$result = [];

foreach ($headers as $header) {
[$key, $value] = explode(": ", $header, 2);
$result[$key] = [$value];
}

return $result;
}
}
11 changes: 5 additions & 6 deletions src/OpenIDConnectServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ protected function registerConfigurationLoader(): void
$app['config']->get('oidc.issuer'),
$app['cache']->store($app['config']->get('oidc.configuration_cache.store')),
$app['config']->get('oidc.configuration_cache.ttl'),
$app['config']->get('oidc.tls_verify') === true,
$app['config']->get('oidc.tls_verify'),
);
});
}
Expand All @@ -104,18 +104,17 @@ protected function registerClient(): void
if (!empty($app['config']->get('oidc.client_secret'))) {
$oidc->setClientSecret($app['config']->get('oidc.client_secret'));
}
$oidc->setCodeChallengeMethod($app['config']->get('oidc.code_challenge_method'));
if (!empty($app['config']->get('oidc.code_challenge_method'))) {
$oidc->setCodeChallengeMethod($app['config']->get('oidc.code_challenge_method'));
}
$oidc->setRedirectURL($app['url']->route('oidc.login'));

$additionalScopes = $app['config']->get('oidc.additional_scopes');
if (is_array($additionalScopes) && count($additionalScopes) > 0) {
$oidc->addScope($additionalScopes);
}

if ($app['config']->get('oidc.tls_verify') !== true) {
$oidc->setVerifyHost(false);
$oidc->setVerifyPeer(false);
}
$oidc->setTlsVerify($app['config']->get('oidc.tls_verify'));
return $oidc;
});
}
Expand Down
Loading

0 comments on commit 59b1fc6

Please sign in to comment.