NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Kubernetes API for Laravel

Service-account tokens

ServiceAccount::requestToken() mints a short-lived token the way kubectl create token does: a TokenRequest (authentication.k8s.io/v1) on the service account’s token subresource. Only the fields you give go into its spec — expirationSeconds, audiences, boundObjectRef — so the apiserver fills in its own defaults (usually an hour and the cluster’s audiences) and may shorten the lifetime. You get back a ServiceAccountToken:

use RoundlyConsulting\KubernetesApi\Facades\Kubernetes;

$token = Kubernetes::namespace('ci')->serviceAccounts()->setName('deployer')->requestToken(
    expirationSeconds: 900,   // 600 or more; null = the apiserver's default (usually an hour)
    audiences: ['vault'],     // empty = the apiserver's default audiences
    boundTo: $pod,            // an existing Pod, Secret or Node: deleting it invalidates the token
);

$token->token;            // the bearer token — masked in var_dump(), print_r() and dump()
$token->expiresAt;        // CarbonImmutable, UTC
$token->audiences;        // list<string>, as the apiserver answered
$token->boundObjectRef;   // ['kind' => 'Pod', 'apiVersion' => 'v1', 'name' => …, 'uid' => …] or null
$token->isExpired();      // or isExpired($at)

// Use it like any other token
Kubernetes::url('https://api.prod.example.com:6443')->withToken($token->token)->pods()->get();

ServiceAccountToken

Property · methodHolds
tokenThe bearer token — a #[SensitiveParameter], masked in var_dump(), print_r() and dump().
expiresAtWhen the token expires, as a CarbonImmutable in UTC.
audienceslist<string> — the audiences the apiserver answered.
boundObjectRefkind, apiVersion, name and uid of the object the token is bound to, or null.
isExpired(?CarbonInterface $at = null)Whether the token has expired by $at — now by default.

Checked before any request

Input the apiserver would refuse throws InvalidResourceException before anything is sent:

  • an unnamed service account — setName() first;
  • a dryRun() resource — a dry-run TokenRequest mints nothing;
  • expirationSeconds under 600 (ServiceAccount::MIN_TOKEN_EXPIRATION_SECONDS);
  • a blank audience;
  • a boundTo that isn’t a Pod, Secret or Node (ServiceAccount::TOKEN_BINDABLE_KINDS), or lacks metadata.name and metadata.uid — find() it first.

Failures and permissions

  • Failures are always body-free — HTTP 403 Forbidden, HTTP 404 NotFound — whatever the cluster’s redaction setting, because this is a credential endpoint. $e->apiMessage() still reads the apiserver’s Status message.
  • An answer without a non-empty status.token or an RFC 3339 status.expirationTimestamp throws KubernetesException::malformedResponse().
  • The request goes through the cluster’s transport, so client.options.timeout, the rate limiter and Kubernetes::fake() all apply. The cluster’s own credentials need create on serviceaccounts/token.

From any entry point

requestToken() is a resource operation like any other, so it works from the facade, an injected manager or any cluster — including an ad-hoc one that carries a credential allowed to mint tokens:

// Through an injected manager or any cluster, like every resource operation
$token = $this->kubernetes->cluster('dev')->serviceAccounts()
    ->setNamespace('ci')
    ->setName('deployer')
    ->requestToken();

// An ad-hoc cluster whose own credential may mint tokens
$token = Kubernetes::url($server)->withToken($minter)->serviceAccounts()
    ->setNamespace($namespace)
    ->setName($serviceAccount)
    ->requestToken($ttl);

Under Kubernetes::fake() a token request answers for seeded service accounts with a deterministic token, and assertTokenRequested() checks it — see Testing.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.