Skip to content

Commit

Permalink
Merge pull request #90 from bedita/feat/oauth2-apple
Browse files Browse the repository at this point in the history
Handle Apple Sign-In via OAuth2
  • Loading branch information
batopa authored Mar 20, 2024
2 parents c812e52 + e555107 commit e9b3a0c
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 13 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"php": "^7.4 || ^8.0",
"bedita/php-sdk": "^2.1.0",
"cakephp/cakephp": "^4.2.2",
"firebase/php-jwt": "^6.9",
"cakephp/twig-view": "^1.3.0"
},
"require-dev": {
Expand Down
18 changes: 16 additions & 2 deletions src/Authenticator/OAuth2Authenticator.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Cake\Log\LogTrait;
use Cake\Routing\Router;
use Cake\Utility\Hash;
use Firebase\JWT\JWT;
use Psr\Http\Message\ServerRequestInterface;

/**
Expand Down Expand Up @@ -85,6 +86,11 @@ public function authenticate(ServerRequestInterface $request): ResultInterface
{
// extract provider from request
$provider = basename($request->getUri()->getPath());
// leeway is needed for clock skew
$leeway = (int)$this->getConfig(sprintf('providers.%s.clientOptions.jwtLeeway', $provider), 0);
if ($leeway) {
JWT::$leeway = $leeway;
}

$connect = $this->providerConnect($provider, $request);
if (!empty($connect[static::AUTH_URL_KEY])) {
Expand All @@ -97,6 +103,7 @@ public function authenticate(ServerRequestInterface $request): ResultInterface
'provider_username' => Hash::get($connect, sprintf('user.%s', $usernameField)),
'access_token' => Hash::get($connect, 'token.access_token'),
'provider_userdata' => (array)Hash::get($connect, 'user'),
'id_token' => Hash::get($connect, 'token.id_token'),
];
$user = $this->_identifier->identify($data);

Expand All @@ -119,7 +126,11 @@ protected function providerConnect(string $provider, ServerRequestInterface $req
{
$this->initProvider($provider, $request);

$query = $request->getQueryParams();
if ($request->getMethod() === 'GET') {
$query = $request->getQueryParams();
} else {
$query = $request->getParsedBody();
}
$sessionKey = $this->getConfig('sessionKey');
/** @var \Cake\Http\Session $session */
$session = $request->getAttribute('session');
Expand All @@ -134,7 +145,10 @@ protected function providerConnect(string $provider, ServerRequestInterface $req
}

// Check given state against previously stored one to mitigate CSRF attack
if (empty($query['state']) || ($query['state'] !== $session->read($sessionKey))) {
if (
(empty($query['state']) || $query['state'] !== $session->read($sessionKey))
&& $request->getMethod() === 'GET'
) {
$session->delete($sessionKey);
throw new BadRequestException('Invalid state');
}
Expand Down
50 changes: 50 additions & 0 deletions tests/TestCase/Authenticator/OAuth2AuthenticatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use Cake\Http\Session;
use Cake\TestSuite\TestCase;
use Cake\Utility\Hash;
use Firebase\JWT\JWT;

/**
* {@see \BEdita\WebTools\Authenticator\OAuth2Authenticator} Test Case
Expand Down Expand Up @@ -50,6 +51,7 @@ public function authenticateProvider(): array
'status' => Result::SUCCESS,
],
[
'environment' => ['REQUEST_METHOD' => 'POST'],
'url' => '/ext/login/gustavo',
],
[
Expand Down Expand Up @@ -186,4 +188,52 @@ public function getErrors(): array
static::assertNotNull($result);
static::assertEquals($expected['status'], $result->getStatus());
}

/**
* Test JWT leeway config in `authenticate` method
*
* @return void
* @covers ::authenticate()
*/
public function testAuthenticateLeeway(): void
{
$identifier = new class () implements IdentifierInterface {
public function identify(array $credentials)
{
return $credentials;
}

public function getErrors(): array
{
return [];
}
};
$reqConfig = [
'url' => '/ext/login/gustavo',
];
$request = new ServerRequest($reqConfig);
$session = new Session();
$session->write(Hash::get($reqConfig, 'data'));
$request = $request->withAttribute('session', $session);

$authenticator = new OAuth2Authenticator($identifier, [
'urlResolver' => fn () => '',
'providers' => [
'gustavo' => [
'class' => TestProvider::class,
'setup' => [
'clientId' => '',
],
'clientOptions' => [
'jwtLeeway' => 10,
],
],
],
]);
$result = $authenticator->authenticate($request);

static::assertNotNull($result);
static::assertEquals(Result::SUCCESS, $result->getStatus());
static::assertEquals(JWT::$leeway, 10);
}
}
22 changes: 11 additions & 11 deletions tests/TestCase/View/Helper/HtmlHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,11 @@ public function metaDescriptionProvider(): array
],
'dummy description' => [
'dummy',
'<meta name="description" content="dummy"/>',
'<meta name="description" content="dummy">',
],
'description with special chars and tags' => [
'dummy <> & dummy',
'<meta name="description" content="dummy &amp;amp; dummy"/>',
'<meta name="description" content="dummy &amp;amp; dummy">',
],
];
}
Expand Down Expand Up @@ -185,11 +185,11 @@ public function metaAuthorProvider(): array
],
'dummy creator' => [
'dummy',
'<meta name="author" content="dummy"/>',
'<meta name="author" content="dummy">',
],
'creator with special chars and tags' => [
'dummy <> & dummy',
'<meta name="author" content="dummy &amp;lt;&amp;gt; &amp;amp; dummy"/>',
'<meta name="author" content="dummy &amp;lt;&amp;gt; &amp;amp; dummy">',
],
];
}
Expand Down Expand Up @@ -219,7 +219,7 @@ public function metaCssProvider(): array
return [
'empty docType' => [
'',
'<meta http-equiv="Content-Style-Type" content="text/css"/>',
'<meta http-equiv="Content-Style-Type" content="text/css">',
],
'html5 docType' => [
'html5',
Expand Down Expand Up @@ -266,14 +266,14 @@ public function metaGeneratorProvider(): array
[
'name' => 'Dummy',
],
'<meta name="generator" content="Dummy"/>',
'<meta name="generator" content="Dummy">',
],
'project and version' => [
[
'name' => 'Dummy',
'version' => '1.0',
],
'<meta name="generator" content="Dummy 1.0"/>',
'<meta name="generator" content="Dummy 1.0">',
],
];
}
Expand Down Expand Up @@ -303,7 +303,7 @@ public function metaAllProvider(): array
return [
'empty data' => [
[],
'<meta http-equiv="Content-Style-Type" content="text/css"/>',
'<meta http-equiv="Content-Style-Type" content="text/css">',
],
'full data' => [
[
Expand All @@ -317,7 +317,7 @@ public function metaAllProvider(): array
'version' => '2.0',
],
],
'<meta name="description" content="dummy description"/><meta name="author" content="gustavo"/><meta http-equiv="Content-Style-Type" content="text/css"/><meta name="generator" content="my dummy project 2.0"/><meta name="viewport" content="width=device-width, initial-scale=1.0"/><meta name="msapplication-TileColor" content="#009cc7"/><meta name="theme-color" content="#ABC000"/>',
'<meta name="description" content="dummy description"><meta name="author" content="gustavo"><meta http-equiv="Content-Style-Type" content="text/css"><meta name="generator" content="my dummy project 2.0"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="msapplication-TileColor" content="#009cc7"><meta name="theme-color" content="#ABC000">',
],
];
}
Expand Down Expand Up @@ -356,7 +356,7 @@ public function metaOpenGraphProvider(): array
'description' => 'a dummy data for test',
'image' => 'an image',
],
'<meta property="og:url" content="https://example.com"/><meta property="og:title" content="dummy"/><meta property="og:description" content="a dummy data for test"/><meta property="og:image" content="an image"/>',
'<meta property="og:url" content="https://example.com"><meta property="og:title" content="dummy"><meta property="og:description" content="a dummy data for test"><meta property="og:image" content="an image">',
],
];
}
Expand Down Expand Up @@ -397,7 +397,7 @@ public function metaTwitterProvider(): array
'description' => 'a dummy data for test',
'image' => 'an image',
],
'<meta property="twitter:card" content="whatever"/><meta property="twitter:site" content="example.com"/><meta property="twitter:creator" content="gustavo"/><meta property="twitter:title" content="dummy"/><meta property="twitter:description" content="a dummy data for test"/><meta property="twitter:image" content="an image"/>',
'<meta property="twitter:card" content="whatever"><meta property="twitter:site" content="example.com"><meta property="twitter:creator" content="gustavo"><meta property="twitter:title" content="dummy"><meta property="twitter:description" content="a dummy data for test"><meta property="twitter:image" content="an image">',
],
];
}
Expand Down

0 comments on commit e9b3a0c

Please sign in to comment.