alibabacloud/sts-20150401
Alibaba Cloud STS SDK for PHP (API version 20150401) lets Laravel and PHP apps request temporary security tokens, assume roles, and obtain short-lived credentials for secure access to Alibaba Cloud resources. Install via Composer.
Install
composer require alibabacloud/sts-20150401
This pulls in alibabacloud/darabonba-openapi, alibabacloud/tea, alibabacloud/tea-utils and Guzzle. It is a Darabonba-generated SDK, so the shape of every call is new Client($config) → new XxxRequest([...]) → $client->xxx($request).
Where to look first
vendor/alibabacloud/sts-20150401/src/Sts.php — the client class and its method list (assumeRole, assumeRoleWithSAML, assumeRoleWithOIDC, getCallerIdentity, generateSessionAccessKey, getAccessKeyInfo).vendor/alibabacloud/sts-20150401/src/Models/ — one *Request and one *Response* class per API. The response bodies are the source of truth for property names.Config model from AlibabaCloud\Darabonba\OpenApi\Models\Config — that is where endpoint, timeouts, retries and credentials live.First use case: exchange a long-lived AccessKey for a short-lived role token
use AlibabaCloud\Darabonba\OpenApi\Models\Config;
use AlibabaCloud\SDK\Sts\V20150401\Sts;
use AlibabaCloud\SDK\Sts\V20150401\Models\AssumeRoleRequest;
$client = new Sts(new Config([
'accessKeyId' => getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'),
'accessKeySecret' => getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),
'endpoint' => 'sts.ap-southeast-1.aliyuncs.com',
]));
$response = $client->assumeRole(new AssumeRoleRequest([
'roleArn' => 'acs:ram::1234567890123456:role/deployer',
'roleSessionName' => 'ci-deploy-' . date('YmdHis'),
'durationSeconds' => 3600,
]));
$creds = $response->body->credentials;
// $creds->accessKeyId, $creds->accessKeySecret,
// $creds->securityToken, $creds->expiration (ISO-8601 string, UTC)
Sanity check without assuming anything — getCallerIdentity() takes no parameters, costs nothing, and tells you which account/role your configured key actually resolves to. Make it the first thing you run when wiring up a new environment.
$me = $client->getCallerIdentity();
echo $me->body->accountId, ' ', $me->body->arn;
1. Cache and refresh tokens centrally, do not call STS per request.
STS is an RPC endpoint with rate limits; a token valid for 3600s is meant to be reused. Wrap assumeRole in a small service that stores the credential set in Redis/APCu keyed by roleArn + roleSessionName, and refreshes when expiration - now < 10% of lifetime:
class StsTokenProvider
{
public function __construct(private Sts $client, private Cache $cache) {}
public function get(string $roleArn, string $session = 'app'): array
{
$key = "sts:{$roleArn}:{$session}";
if ($cached = $this->cache->get($key)) {
return $cached;
}
$res = $this->client->assumeRole(new AssumeRoleRequest([
'roleArn' => $roleArn,
'roleSessionName' => $session,
'durationSeconds' => 3600,
]));
$c = $res->body->credentials;
$ttl = (new \DateTimeImmutable($c->expiration))->getTimestamp() - time() - 300;
return $this->cache->remember($key, $ttl, fn () => [
'accessKeyId' => $c->accessKeyId,
'accessKeySecret' => $c->accessKeySecret,
'securityToken' => $c->securityToken,
]);
}
}
Add ±60s of jitter to the TTL so a fleet of workers does not all refresh on the same tick.
2. Let alibabacloud/credentials do the wrapping.
In most production code you should not touch this SDK directly. alibabacloud/credentials ships AssumeRoleCredential and OidcCredential that wrap exactly this package and refresh transparently. You then hand the Credential object to any other Alibaba Cloud SDK (oss, dysmsapi, ecs, …) via Config(['credential' => $credential]). Reach for the raw sts-20150401 client only when you need the raw token (e.g. to hand to a non-PHP service or to a browser).
3. OIDC for CI/CD instead of storing an AccessKey.
GitHub Actions, GitLab CI and Kubernetes all issue OIDC JWTs. Swap the AK for assumeRoleWithOIDC and the secret never leaves the identity provider:
$client->assumeRoleWithOIDC(new AssumeRoleWithOIDCRequest([
'OIDCProviderArn' => 'acs:ram::1234567890123456:oidc-provider/github',
'OIDCToken' => $jwt,
'roleArn' => 'acs:ram::1234567890123456:role/gh-deploy',
'roleSessionName' => 'gh-' . getenv('GITHUB_RUN_ID'),
'durationSeconds' => 1800,
]));
Same shape applies to assumeRoleWithSAML for enterprise SSO. Note that these two calls are unsigned — they do not need an AccessKey in Config, only an endpoint.
4. Cross-account fan-out.
The common enterprise topology is: one "hub" account holds the automation keys, and you assumeRole into each workload account. Keep a map of roleArn per tenant/environment, resolve the credential once per target, then build per-target SDK clients from the returned triple. Because the returned token is bound to the role's permission policy, downstream clients need no other config.
5. Downstream clients. Every Alibaba Cloud SDK accepts the three STS fields:
new OssClient([
'accessKeyId' => $c->accessKeyId,
'accessKeySecret' => $c->accessKeySecret,
'securityToken' => $c->securityToken, // required, easy to forget
'endpoint' => 'oss-ap-southeast-1.aliyuncs.com',
]);
6. Scope with an inline policy.
Pass a policy JSON string to assumeRole to narrow the session below what the role itself grants — ideal for per-tenant tokens in a multi-tenant SaaS where every customer shares one role:
'policy' => json_encode([
'Version' => '1',
'Statement' => [[
'Effect' => 'Allow',
'Action' => ['oss:GetObject'],
'Resource' => ["acs:oss:*:*:tenant-{$id}/*"],
]],
]),
The SDK serialises it as a query parameter, so keep it under ~2KB or the request will fail with InvalidParameter.PolicySizeLimit.
7. Observability.
Log one line per assumeRole call: roleArn, roleSessionName, HTTP status, $res->body->requestId, latency. The requestId is what Alibaba Cloud support will ask for. Do not log the returned secret material — log the accessKeyId prefix only.
Config(['region' => 'ap-southeast-1']) alone is not enough for STS; set endpoint explicitly (sts.ap-southeast-1.aliyuncs.com, sts.cn-hangzhou.aliyuncs.com). Do not include https:// — the transport prepends the scheme and duplicate prefixes produce confusing 404/InvalidEndpoint errors from the gateway rather than the SDK.DateTime. $creds->expiration arrives as ISO-8601 UTC (e.g. 2026-01-07T12:00:00Z). Forgetting new \DateTimeImmutable(...) and doing string maths is the single most common source of "token expired mid-run" bugs.roleSessionName is validated client- and server-side: 2–64 chars, [a-zA-Z0-9._-] only. $request->roleSessionName = "deploy/" . $branch (slashes from Git refs) will fail with InvalidParameter.RoleSessionName. Sanitise before passing.durationSeconds is silently capped. Minimum 900. Maximum is the role's MaxSessionDuration (default 3600, configurable up to 43200); ask for more and you get an error rather than a truncation. Set MaxSessionDuration on the role if you need long sessions.accessKeyId, securityToken) even though the XML returns PascalCase. When in doubt, var_dump($response->body) once in a REPL — guessing between ->Credentials and ->credentials wastes hours.AlibabaCloud\Darabonba\OpenApi\Exceptions\ClientException (4xx, bad input, bad signature) and ServerException (5xx, throttling) extend Tea\Exception\TeaError. Catch TeaError and branch on $e->getCode() — it returns the string error code (Throttling, NoPermission.AssumeRole, InvalidAccessKeyId.NotFound) rather than an integer. Retry only Throttling / ServiceUnavailable, and only with backoff.OssClient constructed with accessKeyId/accessKeySecret but forgetting securityToken — requests fail with a generic 403 that names nothing useful.InvalidTimeStamp.Expired. Run NTP in containers or pass host-level time sync. The Config has no skew-compensation knob.Config, not per-call. connectTimeout, readTimeout, and autoretry/maxAttempts are accepted in the Config array and applied to every request from that client. Default read timeout is generous; drop it to 3–5s for STS so a network blip fails fast and your refresh path can retry.getCallerIdentity is the debug tool. When a role assumption fails with a permission error, call getCallerIdentity with the base credentials first to confirm which principal is actually being used — it is frequently not the account you assumed, because an unrelated ALIBABA_CLOUD_ACCESS_KEY_ID in the environment shadowed your config.Config object is shared. One Sts client instance is safe to reuse across requests (it is stateless apart from Guzzle's handler stack); build it once in a container binding and inject it. Building a new client per call is the second most common performance mistake after skipping the token cache.2015-04-01 is the only STS API version, so this package will never have a V2 sibling. The V20150401 segment in every namespace is permanent noise — alias the imports (use ...\V20150401\Sts as StsClient;) in files that use several SDKs.endpoint at a local mock server for unit tests rather than mocking the client class; the generated models have public properties, so constructing an AssumeRoleResponseBody by hand for assertions is verbose. For integration tests use getCallerIdentity (free) plus one short assumeRole against a throwaway role.How can I help you explore Laravel packages today?