Skip to content

Commit 17194f6

Browse files
committed
Authentication: trusted publishing setup for artifact publishing
1 parent e389d73 commit 17194f6

File tree

3 files changed

+83
-2
lines changed

3 files changed

+83
-2
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"php-http/discovery": "^1.0",
1919
"psr/http-client-implementation": "^1.0",
2020
"php-http/message-factory": "^1.0",
21-
"psr/http-message-implementation": "^1.0"
21+
"psr/http-message-implementation": "^1.0",
22+
"private-packagist/oidc-identities": "dev-main"
2223
},
2324
"require-dev": {
2425
"friendsofphp/php-cs-fixer": "^3.0",

src/Client.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,26 @@
1616
use PrivatePackagist\ApiClient\HttpClient\Plugin\ExceptionThrower;
1717
use PrivatePackagist\ApiClient\HttpClient\Plugin\PathPrepend;
1818
use PrivatePackagist\ApiClient\HttpClient\Plugin\RequestSignature;
19+
use PrivatePackagist\ApiClient\HttpClient\Plugin\TrustedPublishingTokenExchange;
20+
use Psr\Log\LoggerInterface;
21+
use Psr\Log\NullLogger;
1922

2023
class Client
2124
{
2225
/** @var HttpPluginClientBuilder */
2326
private $httpClientBuilder;
2427
/** @var ResponseMediator */
2528
private $responseMediator;
29+
/** @var LoggerInterface */
30+
private $logger;
2631

2732
/** @param string $privatePackagistUrl */
28-
public function __construct(?HttpPluginClientBuilder $httpClientBuilder = null, $privatePackagistUrl = null, ?ResponseMediator $responseMediator = null)
33+
public function __construct(?HttpPluginClientBuilder $httpClientBuilder = null, $privatePackagistUrl = null, ?ResponseMediator $responseMediator = null, ?LoggerInterface $logger = null)
2934
{
3035
$this->httpClientBuilder = $builder = $httpClientBuilder ?: new HttpPluginClientBuilder();
3136
$privatePackagistUrl = $privatePackagistUrl ? : 'https://packagist.com';
3237
$this->responseMediator = $responseMediator ? : new ResponseMediator();
38+
$this->logger = $logger ? : new NullLogger();
3339

3440
$builder->addPlugin(new Plugin\AddHostPlugin(Psr17FactoryDiscovery::findUriFactory()->createUri($privatePackagistUrl)));
3541
$builder->addPlugin(new PathPrepend('/api'));
@@ -58,6 +64,12 @@ public function authenticate(
5864
$this->httpClientBuilder->addPlugin(new RequestSignature($key, $secret));
5965
}
6066

67+
public function authenticateWithTrustedPublishing(string $organizationUrlName, string $packageName)
68+
{
69+
$this->httpClientBuilder->removePlugin(TrustedPublishingTokenExchange::class);
70+
$this->httpClientBuilder->addPlugin(new TrustedPublishingTokenExchange($organizationUrlName, $packageName, $this->getHttpClientBuilder(), $this->logger));
71+
}
72+
6173
public function credentials()
6274
{
6375
return new Api\Credentials($this, $this->responseMediator);
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php declare(strict_types=1);
2+
3+
/**
4+
* (c) Packagist Conductors GmbH <[email protected]>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
namespace PrivatePackagist\ApiClient\HttpClient\Plugin;
11+
12+
use Http\Client\Common\HttpMethodsClient;
13+
use Http\Client\Common\Plugin;
14+
use Http\Discovery\Psr17FactoryDiscovery;
15+
use Http\Discovery\Psr18ClientDiscovery;
16+
use PrivatePackagist\ApiClient\HttpClient\HttpPluginClientBuilder;
17+
use PrivatePackagist\OIDC\Identities\TokenGenerator;
18+
use Psr\Http\Message\RequestInterface;
19+
use Psr\Log\LoggerInterface;
20+
21+
class TrustedPublishingTokenExchange implements Plugin
22+
{
23+
use Plugin\VersionBridgePlugin;
24+
25+
/** @var string */
26+
private $organizationUrlName;
27+
/** @var string */
28+
private $packageName;
29+
/** @var HttpPluginClientBuilder $httpPluginClientBuilder */
30+
private $httpPluginClientBuilder;
31+
/** @var LoggerInterface */
32+
private $logger;
33+
34+
public function __construct(string $organizationUrlName, string $packageName, HttpPluginClientBuilder $httpPluginClientBuilder, LoggerInterface $logger)
35+
{
36+
$this->organizationUrlName = $organizationUrlName;
37+
$this->packageName = $packageName;
38+
$this->httpPluginClientBuilder = $httpPluginClientBuilder;
39+
$this->logger = $logger;
40+
}
41+
42+
protected function doHandleRequest(RequestInterface $request, callable $next, callable $first)
43+
{
44+
$this->httpPluginClientBuilder->removePlugin(self::class);
45+
46+
$privatePackagistHttpclient = $this->httpPluginClientBuilder->getHttpClient();
47+
$audience = json_decode((string) $privatePackagistHttpclient->get('/oidc/audience')->getBody(), true);
48+
$this->logger->debug('Audience', $audience);
49+
50+
$oidcHttpClient = new HttpMethodsClient(
51+
Psr18ClientDiscovery::find(),
52+
Psr17FactoryDiscovery::findRequestFactory(),
53+
Psr17FactoryDiscovery::findStreamFactory()
54+
);
55+
56+
$tokenGenerator = new TokenGenerator($this->logger, $oidcHttpClient);
57+
$token = $tokenGenerator->generate($audience['audience']);
58+
if (!$token) {
59+
return $next($request);
60+
}
61+
62+
$apiCredentials = json_decode((string) $privatePackagistHttpclient->post('/oidc/token-exchange/' . $this->organizationUrlName . '/' . $this->packageName, ['Authorization' => 'Bearer ' . $token->token])->getBody(), true);
63+
64+
$this->httpPluginClientBuilder->addPlugin($requestSignature = new RequestSignature($apiCredentials['key'], $apiCredentials['secret']));
65+
66+
return $requestSignature->handleRequest($request, $next, $first);
67+
}
68+
}

0 commit comments

Comments
 (0)