forgelab-me/ci4-trusted-publishing

Let a CI workflow authenticate to your CodeIgniter 4 app with the identity token its runner mints, and get a short-lived Shield access token in exchange — no long-lived secret stored in the repository.

Maintainers

Package info

github.com/forgelab-me/ci4-trusted-publishing

pkg:composer/forgelab-me/ci4-trusted-publishing

Transparency log

Statistics

Installs: 39

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-08-18 15:40 UTC

This package is auto-updated.

Last update: 2026-08-18 15:41:27 UTC


README

Let a CI workflow authenticate to your CodeIgniter 4 application with the identity token its runner mints for it, and hand it a short-lived Shield access token in exchange.

No long-lived secret in the repository settings. Nothing to rotate. A stolen pipeline log yields a credential that expired fifteen minutes ago.

POST /api/my-app/publish/token   Authorization: Bearer <GitHub OIDC token>
  → { "token": "…", "expires_in": 900, "scope": "publish:my-app" }

What it does, and what it leaves to you

It owns identity: verifying somebody else's signed statement about which repository, workflow and environment is running, and deciding whether that matches something you already trust.

You own authorization: what that identity may then do, and where the trust is recorded. That boundary is deliberate — one application grants publish:{slug} on a project, another grants a package-push scope on a feed with its own pattern rules. Neither belongs in a shared package.

In here verifying the token (signature, issuer, audience), normalising claims across providers, matching a verified identity against your trusted publishers, minting/listing/revoking/purging scoped Shield tokens
Yours the trusted publishers table and its foreign key, the route and controller, what a scope permits, the admin screen

Requirements

  • PHP 8.2+
  • CodeIgniter 4.7+
  • codeigniter4/shield 1.4+ for the token side (ScopedTokens); the verification side works without it

Install

composer require forgelab-me/ci4-trusted-publishing

The shape of it

use Forgelabme\TrustedPublishing\Config\TrustedPublishing;
use Forgelabme\TrustedPublishing\PublisherMatcher;
use Forgelabme\TrustedPublishing\ScopedTokens;
use Forgelabme\TrustedPublishing\Verifier;

// 1. Verify what the runner presented.
$result = config(TrustedPublishing::class)
    ->verifier('github')
    ->verify(Verifier::bearer($this->request->getHeaderLine('Authorization')));

if (! $result->ok) {
    return $this->failUnauthorized($result->error);
}

// 2. Is this identity one you already trust? Candidates come from your table.
$publisher = (new PublisherMatcher())->match(
    $publishers->forProject($project['id']),
    $result->identity,
);

if ($publisher === null) {
    return $this->failForbidden('No trusted publisher matches this token.');
}

// 3. Hand back a credential that expires on its own.
ScopedTokens::purgeExpired($scope);
$token = ScopedTokens::mint($user, $result->identity->describe(), [$scope], 15);

return $this->respond(['token' => $token->raw_token, 'expires_in' => 900, 'scope' => $scope]);

Documentation

  • Integrating it — the table, the endpoint, the workflow, the admin screen, and how to test your own integration. Start here.
  • Providers — how GitHub Actions is wired, and how to add GitLab CI or anything else that issues OIDC tokens.
  • Security — what each check is for, and the mistakes this package exists to stop you making twice.

Licence

MIT — see LICENSE.