Skip to content

Commit

Permalink
Add config option for "authentication base url" (#18)
Browse files Browse the repository at this point in the history
* Add "authenticationBaseUrl" to Frontegg Config

Hybrid mode may require different base urls for authentication and API requests. This
is because hybrid mode requires authentication via Frontegg Cloud but the actual API
endpoints will by hosted by the local hybrid instance. So authentication will go through
api.frontegg.com and other API calls will go to whatever url is defined by the hybrid
installation.

* Use authentication url from config to authenticate instead of service url

* Include "authenticationBaseUrl" in readme
  • Loading branch information
rjmccluskey authored May 21, 2021
1 parent 97a0905 commit dfb6dcc
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 17 deletions.
2 changes: 2 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ $config = [
'clientId' => 'YOUR_CLIENT_ID',
'clientSecret' => 'YOUR_SECRET_API_KEY',
'apiBaseUrl' => 'https://api.frontegg.com/',
'authenticationBaseUrl' => 'https://api.frontegg.com/',
'apiUrls' => [
'authentication' => '/auth/vendor',
'audits' => '/audits',
Expand Down Expand Up @@ -89,6 +90,7 @@ $frontegg->init();
| **clientSecret** | string | None | API Key. Required |
| **contextResolver** | callable | None | Callback to provide context info. Required |
| apiBaseUrl | string | https://api.frontegg.com | Base API URL |
| authenticationBaseUrl | string | https://api.frontegg.com | Base URL used for authentication |
| apiUrls | array | [] | List of URLs of the API services |
| disableCors | bool | false | Disabling CORS headers for Middleware Proxy |
| httpClientHandler | special interface* | Curl client** | Custom HTTP client |
Expand Down
2 changes: 1 addition & 1 deletion src/Frontegg/Authenticator/Authenticator.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public function getLastResponse(): ?ApiRawResponse
*/
public function authenticate(): void
{
$url = $this->fronteggConfig->getServiceUrl(
$url = $this->fronteggConfig->getAuthenticationUrl(
Config::AUTHENTICATION_SERVICE
);
$body = json_encode(
Expand Down
47 changes: 41 additions & 6 deletions src/Frontegg/Config/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ class Config
*/
protected $baseUrl;

/**
* Frontegg API base URL for authentication.
* In hybrid mode, the base API url and authentication url may be different
*
* @var string
*/
protected $authenticationBaseUrl;

/**
* Frontegg API endpoints relative URLs.
*
Expand All @@ -79,21 +87,24 @@ class Config
* @param array $urls
* @param bool $disableCors
* @param callable $contextResolver
* @param string $authenticationBaseUrl
*/
public function __construct(
string $clientId,
string $clientSecret,
string $baseUrl,
array $urls,
bool $disableCors,
callable $contextResolver
callable $contextResolver,
string $authenticationBaseUrl
) {
$this->clientId = $clientId;
$this->clientSecret = $clientSecret;
$this->baseUrl = trim($baseUrl, '/');
$this->setApiUrls($urls);
$this->contextResolver = $contextResolver;
$this->disableCors = $disableCors;
$this->authenticationBaseUrl = trim($authenticationBaseUrl, '/');
}

/**
Expand Down Expand Up @@ -136,6 +147,14 @@ public function getBaseUrl(): string
return $this->baseUrl;
}

/**
* @return string
*/
public function getAuthenticationBaseUrl(): string
{
return $this->authenticationBaseUrl;
}

/**
* Returns API URL by service name.
*
Expand All @@ -147,11 +166,7 @@ public function getBaseUrl(): string
*/
public function getServiceUrl(string $urlKey): string
{
if (!isset(static::$API_URL_KEYS[$urlKey])) {
throw new InvalidUrlConfigException(
sprintf('URL "%s" is not a part of allowed API', $urlKey)
);
}
$this->validateUrlKey($urlKey);

if (isset($this->urls[$urlKey])) {
return $this->baseUrl . $this->urls[$urlKey];
Expand All @@ -160,6 +175,17 @@ public function getServiceUrl(string $urlKey): string
return $this->baseUrl . static::$API_URL_KEYS[$urlKey];
}

public function getAuthenticationUrl(string $urlKey): string
{
$this->validateUrlKey($urlKey);

if (isset($this->urls[$urlKey])) {
return $this->authenticationBaseUrl . $this->urls[$urlKey];
}

return $this->authenticationBaseUrl . static::$API_URL_KEYS[$urlKey];
}

/**
* Returns URL of the Frontegg proxy.
*
Expand Down Expand Up @@ -189,4 +215,13 @@ protected function setApiUrls(array $urls = []): void
$this->urls[$key] = $url;
}
}

private function validateUrlKey(string $urlKey): void
{
if (!isset(static::$API_URL_KEYS[$urlKey])) {
throw new InvalidUrlConfigException(
sprintf('URL "%s" is not a part of allowed API', $urlKey)
);
}
}
}
4 changes: 3 additions & 1 deletion src/Frontegg/Frontegg.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ public function __construct(array $config = [])
'clientId' => getenv(static::CLIENT_ID_ENV_NAME),
'clientSecret' => getenv(static::CLIENT_SECRET_ENV_NAME),
'apiBaseUrl' => static::DEFAULT_API_BASE_URL,
'authenticationBaseUrl' => static::DEFAULT_API_BASE_URL,
'apiUrls' => [],
'apiVersion' => static::DEFAULT_API_VERSION,
'httpClientHandler' => null,
Expand Down Expand Up @@ -138,7 +139,8 @@ public function __construct(array $config = [])
$config['apiBaseUrl'],
$config['apiUrls'],
$config['disableCors'],
$config['contextResolver']
$config['contextResolver'],
$config['authenticationBaseUrl']
);
$this->client = $config['httpClientHandler'] ??
new FronteggCurlHttpClient();
Expand Down
30 changes: 30 additions & 0 deletions tests/Authenticator/AuthenticatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
namespace Frontegg\Tests\Authenticator;

use DateTime;
use Frontegg\HttpClient\FronteggCurlHttpClient;
use Frontegg\Tests\Helper\AuthenticatorTestCaseHelper;
use Prophecy\Argument;

class AuthenticatorTest extends AuthenticatorTestCaseHelper
{
Expand Down Expand Up @@ -168,4 +170,32 @@ public function testAuthenticationValidationIsNotWorking(): void
$authenticator->getApiError()->getError()
);
}

public function testAuthenticatorCallsAuthUrl()
{
$authBaseUrl = 'http://authentication';

$client = $this->prophesize(FronteggCurlHttpClient::class);
$client->send(
Argument::containingString($authBaseUrl),
Argument::any(),
Argument::any(),
Argument::any(),
Argument::any()
)
->shouldBeCalledOnce()
->willReturn($this->createAuthHttpApiRawResponse());

$authenticator = $this->createFronteggAuthenticator(
$client->reveal(),
'clientTestID',
'apiTestSecretKey',
'http://test',
[],
true,
null,
$authBaseUrl
);
$authenticator->authenticate();
}
}
27 changes: 20 additions & 7 deletions tests/Config/ConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public function testConfigHasClientCredentials(): void
false,
function (RequestInterface $request) {
return [];
}
},
'https://auth/'
);

// Assert
Expand Down Expand Up @@ -52,14 +53,16 @@ public function testConfigHasApiUrls(): void
false,
function (RequestInterface $request) {
return [];
}
},
'https://auth/'
);

// Assert
$this->assertEquals('https://api.frontegg.com', $config->getBaseUrl());
$this->assertEquals('https://auth', $config->getAuthenticationBaseUrl());
$this->assertEquals(
'https://api.frontegg.com/test/auth',
$config->getServiceUrl(Config::AUTHENTICATION_SERVICE)
'https://auth/test/auth',
$config->getAuthenticationUrl(Config::AUTHENTICATION_SERVICE)
);
$this->assertEquals(
'https://api.frontegg.com/audits',
Expand Down Expand Up @@ -96,18 +99,28 @@ public function testConfigHasDefaultApiUrls(): void
false,
function (RequestInterface $request) {
return [];
}
},
'https://auth/'
);

// Assert
$this->assertEquals(
$config->getBaseUrl() . Config::AUTHENTICATION_SERVICE_DEFAULT_URL,
$config->getServiceUrl(Config::AUTHENTICATION_SERVICE)
$config->getAuthenticationBaseUrl() . Config::AUTHENTICATION_SERVICE_DEFAULT_URL,
$config->getAuthenticationUrl(Config::AUTHENTICATION_SERVICE)
);
$this->assertEquals(
$config->getBaseUrl() . Config::EVENTS_SERVICE_DEFAULT_URL,
$config->getServiceUrl(Config::EVENTS_SERVICE)
);
$this->expectException(InvalidUrlConfigException::class);
$this->assertNotEquals(
'should not be in the config',
$config->getServiceUrl('randomUrl')
);
$this->expectException(InvalidUrlConfigException::class);
$this->assertNotEquals(
'should not be in the config',
$config->getAuthenticationUrl('randomUrl')
);
}
}
8 changes: 8 additions & 0 deletions tests/FronteggTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ public function testFronteggAuthenticatorIsCreated(): void
'apiTestSecretKey',
$frontegg->getConfig()->getClientSecret()
);
$this->assertEquals(
Frontegg::DEFAULT_API_BASE_URL,
$frontegg->getConfig()->getBaseUrl()
);
$this->assertEquals(
Frontegg::DEFAULT_API_BASE_URL,
$frontegg->getConfig()->getAuthenticationBaseUrl()
);
$this->assertInstanceOf(
FronteggHttpClientInterface::class,
$frontegg->getClient()
Expand Down
6 changes: 4 additions & 2 deletions tests/Helper/AuthenticatorTestCaseHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ protected function createFronteggAuthenticator(
string $baseUrl = 'http://test',
array $urls = [],
bool $disbaleCors = true,
?callable $contextResolver = null
?callable $contextResolver = null,
string $authenticationBaseUrl = 'http://auth'
): Authenticator {
$contextResolver = $contextResolver ?? function () {
return [];
Expand All @@ -39,7 +40,8 @@ protected function createFronteggAuthenticator(
$baseUrl,
$urls,
$disbaleCors,
$contextResolver
$contextResolver,
$authenticationBaseUrl
);

return new Authenticator($fronteggConfig, $client);
Expand Down

0 comments on commit dfb6dcc

Please sign in to comment.