Skip to content

Commit 83f7e2b

Browse files
authored
Merge pull request #99 from neo4j-php/oidc
OIDC
2 parents 796c5d4 + d81d9ac commit 83f7e2b

File tree

8 files changed

+150
-6
lines changed

8 files changed

+150
-6
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"psr/http-client": "^1.0",
3232
"php-http/message": "^1.0",
3333
"php-http/message-factory": "^1.0",
34-
"stefanak-michal/bolt": "^2.5.3",
34+
"stefanak-michal/bolt": "^2.7.1",
3535
"symfony/polyfill-php80": "^1.2",
3636
"ext-json": "*"
3737
},

docker-compose.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ services:
7373
<<: *common
7474
volumes:
7575
- ./tests/resources:/import
76+
env_file:
77+
- .env
7678
core1:
7779
image: neo4j:4.4-enterprise
7880
healthcheck:
@@ -91,6 +93,8 @@ services:
9193
NEO4J_causal__clustering_raft__advertised__address: core1:7000
9294
NEO4J_dbms_connector_http_advertised__address: core1:7474
9395
NEO4J_dbms_connector_bolt_advertised__address: core1:7687
96+
env_file:
97+
- .env
9498

9599
core2:
96100
image: neo4j:4.4-enterprise
@@ -110,6 +114,8 @@ services:
110114
NEO4J_dbms_connector_bolt_advertised__address: core2:7687
111115
volumes:
112116
- ./tests/resources:/import
117+
env_file:
118+
- .env
113119

114120
core3:
115121
image: neo4j:4.4-enterprise
@@ -129,6 +135,8 @@ services:
129135
NEO4J_dbms_connector_bolt_advertised__address: core3:7687
130136
volumes:
131137
- ./tests/resources:/import
138+
env_file:
139+
- .env
132140

133141
readreplica1:
134142
image: neo4j:4.4-enterprise
@@ -147,5 +155,7 @@ services:
147155
NEO4J_causal__clustering_raft__advertised__address: readreplica1:7000
148156
NEO4J_dbms_connector_http_advertised__address: readreplica1:7474
149157
NEO4J_dbms_connector_bolt_advertised__address: readreplica1:7687
158+
env_file:
159+
- .env
150160
volumes:
151161
- ./tests/resources:/import

src/Authentication/Authenticate.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ public static function kerberos(string $token): KerberosAuth
4040
return new KerberosAuth($token);
4141
}
4242

43+
/**
44+
* Authenticate using a OpenID Connect token.
45+
*
46+
* @pure
47+
*/
48+
public static function oidc(string $token): OpenIDConnectAuth
49+
{
50+
return new OpenIDConnectAuth($token);
51+
}
52+
4353
/**
4454
* Don't authenticate at all.
4555
*

src/Authentication/BasicAuth.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use function base64_encode;
1717
use Bolt\Bolt;
1818
use Bolt\error\MessageException;
19+
use Bolt\helpers\Auth;
1920
use Exception;
2021
use Laudis\Neo4j\Common\TransactionHelper;
2122
use Laudis\Neo4j\Contracts\AuthenticateInterface;
@@ -63,7 +64,9 @@ public function authenticateHttp(RequestInterface $request, UriInterface $uri, s
6364
public function authenticateBolt(Bolt $bolt, UriInterface $uri, string $userAgent): void
6465
{
6566
try {
66-
$bolt->init($userAgent, $this->username, $this->password);
67+
$auth = Auth::basic($this->username, $this->password);
68+
$auth['user_agent'] = $userAgent;
69+
$bolt->init($auth);
6770
} catch (MessageException $e) {
6871
$code = TransactionHelper::extractCode($e) ?? '';
6972
throw new Neo4jException([new Neo4jError($code, $e->getMessage())]);

src/Authentication/KerberosAuth.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
namespace Laudis\Neo4j\Authentication;
1515

1616
use Bolt\Bolt;
17+
use Bolt\helpers\Auth;
1718
use Laudis\Neo4j\Contracts\AuthenticateInterface;
1819
use Psr\Http\Message\RequestInterface;
1920
use Psr\Http\Message\UriInterface;
@@ -49,8 +50,12 @@ public function authenticateHttp(RequestInterface $request, UriInterface $uri, s
4950

5051
public function authenticateBolt(Bolt $bolt, UriInterface $uri, string $userAgent): void
5152
{
52-
$bolt->setScheme('kerberos');
53-
$bolt->init($userAgent, $this->token, $this->token);
53+
/**
54+
* @psalm-suppress DeprecatedMethod
55+
*/
56+
$bearer = Auth::bearer($this->token);
57+
$bearer['user_agent'] = $userAgent;
58+
$bolt->init($bearer);
5459
}
5560

5661
/**

src/Authentication/NoAuth.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
namespace Laudis\Neo4j\Authentication;
1515

1616
use Bolt\Bolt;
17+
use Bolt\helpers\Auth;
1718
use Laudis\Neo4j\Contracts\AuthenticateInterface;
1819
use Psr\Http\Message\RequestInterface;
1920
use Psr\Http\Message\UriInterface;
@@ -38,8 +39,12 @@ public function authenticateHttp(RequestInterface $request, UriInterface $uri, s
3839

3940
public function authenticateBolt(Bolt $bolt, UriInterface $uri, string $userAgent): void
4041
{
41-
$bolt->setScheme('none');
42-
$bolt->init($userAgent, '', '');
42+
$auth = Auth::none();
43+
$auth['user_agent'] = $userAgent;
44+
/**
45+
* @psalm-suppress DeprecatedMethod
46+
*/
47+
$bolt->init($auth);
4348
}
4449

4550
/**
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Laudis Neo4j package.
5+
*
6+
* (c) Laudis technologies <http://laudis.tech>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Laudis\Neo4j\Authentication;
13+
14+
use Bolt\Bolt;
15+
use Bolt\helpers\Auth;
16+
use Laudis\Neo4j\Contracts\AuthenticateInterface;
17+
use Psr\Http\Message\RequestInterface;
18+
use Psr\Http\Message\UriInterface;
19+
20+
final class OpenIDConnectAuth implements AuthenticateInterface
21+
{
22+
private string $token;
23+
24+
/**
25+
* @psalm-external-mutation-free
26+
*/
27+
public function __construct(string $token)
28+
{
29+
$this->token = $token;
30+
}
31+
32+
/**
33+
* @psalm-mutation-free
34+
*/
35+
public function authenticateHttp(RequestInterface $request, UriInterface $uri, string $userAgent): RequestInterface
36+
{
37+
/**
38+
* @psalm-suppress ImpureMethodCall Request is a pure object:
39+
*
40+
* @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message-meta.md#why-value-objects
41+
*/
42+
return $request->withHeader('Authorization', 'Bearer '.$this->token)
43+
->withHeader('User-Agent', $userAgent);
44+
}
45+
46+
public function authenticateBolt(Bolt $bolt, UriInterface $uri, string $userAgent): void
47+
{
48+
/**
49+
* @psalm-suppress DeprecatedMethod
50+
* @psalm-suppress ImpureMethodCall
51+
*/
52+
$bolt->init(Auth::bearer($this->token));
53+
}
54+
55+
/**
56+
* @psalm-mutation-free
57+
*/
58+
public function extractFromUri(UriInterface $uri): AuthenticateInterface
59+
{
60+
return $this;
61+
}
62+
}

tests/Integration/OIDCTest.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Laudis Neo4j package.
5+
*
6+
* (c) Laudis technologies <http://laudis.tech>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Laudis\Neo4j\Tests\Integration;
13+
14+
use function array_key_exists;
15+
use function is_string;
16+
use Laudis\Neo4j\Authentication\Authenticate;
17+
use Laudis\Neo4j\ClientBuilder;
18+
use Laudis\Neo4j\Http\HttpDriver;
19+
use Monolog\Test\TestCase;
20+
21+
/**
22+
* @psalm-suppress MissingConstructor
23+
*/
24+
final class OIDCTest extends TestCase
25+
{
26+
public function testConnect(): void
27+
{
28+
$this->expectNotToPerformAssertions();
29+
if (!array_key_exists('ACCESS_TOKEN_BEARER', $_ENV) || !is_string($_ENV['ACCESS_TOKEN_BEARER'])) {
30+
$this->markTestSkipped('No OIDC token provided');
31+
}
32+
33+
/** @var mixed */
34+
$connections = $_ENV['NEO4J_CONNECTIONS'] ?? '';
35+
$connections = is_string($connections) ? $connections : '';
36+
foreach (explode(',', $connections) as $connection) {
37+
$driver = ClientBuilder::create()
38+
->withDriver('default', $connection, Authenticate::oidc($_ENV['ACCESS_TOKEN_BEARER']))
39+
->build()
40+
->getDriver('default');
41+
42+
if ($driver instanceof HttpDriver) {
43+
continue;
44+
}
45+
46+
$driver->createSession()->run('RETURN 1');
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)