UserInfoGrpcClient
extends BaseStub
in package
gRPC userinfo transport (CONTRACT.md §1.1/§5/§6/§9, contract 1.3) — the low-latency gRPC counterpart of the server's REST `GET /oauth2/userinfo` endpoint. This is the hand-written service-client sibling of {@see AuthzGrpcClient}: it mirrors that class's channel/metadata/status-mapping machinery exactly, differing only in the single RPC it exposes (`GetUserInfo` instead of `CheckAccess`/`BatchCheckAccess`).
PITFALL 4 / T-22-16 (extension_loaded('grpc') guard, high severity): this class
extends \Grpc\BaseStub, exactly like AuthzGrpcClient. PHP only resolves an
extends clause's target when THIS FILE is actually included/executed (which PSR-4
autoloading defers until something references Axiam\Sdk\Grpc\UserInfoGrpcClient by
name) — so merely having this class exist in the SDK never fatals a REST-only runtime.
The invariant this SDK MUST preserve is the same one AuthzGrpcClient documents:
nothing outside AuthzDispatcher's extension_loaded('grpc')-guarded
branches may ever reference this class name — AuthzDispatcher is the
ONLY call site in this SDK, and it references this class exclusively inside that guard.
No grpc_php_plugin was available to generate a *ServiceClient stub (buf CLI
unavailable — the PHP SDK deliberately does not use grpc_php_plugin; see the README's
"Regenerating the gRPC stubs" section), so this class hand-implements GetUserInfo
directly against \Grpc\BaseStub::_simpleRequest() — the exact same primitive
grpc_php_plugin's own generated service-client classes call internally — using the
committed message stubs in Gen.
§1.1.4 (refresh): this class NEVER re-implements token refresh. $tokenAccessor is a
non-blocking closure that reads the CURRENT access token live (the exact same "read
live, never cache a second copy" discipline Session::accessToken()
follows for REST). On an UNAUTHENTICATED gRPC status it surfaces AuthError and
lets the caller (AuthzDispatcher::getUserInfo()) drive the §9
single-flight refresh and retry the RPC once — mirroring the REST 401 path exactly.
§6/D-12: the channel is ALWAYS constructed via \Grpc\ChannelCredentials::createSsl()
(strict TLS, system trust roots) or, when $customCaPem is supplied, that same factory
with the custom CA bytes — the ONLY escape hatch (§6). There is no insecure-channel
construction path anywhere in this class.
§6.1 (mTLS): when a client identity ($clientCertPem + $clientKey) is configured it is
passed to the SAME createSsl(rootCerts, privateKey, certChain) factory, so the channel
presents a client certificate for mutual TLS. This is strictly additive — it changes only
what the CLIENT presents, never how the server is verified (§6.1.2). The private key
arrives wrapped in Sensitive and is revealed only at the
createSsl() call site; it is never stored in plaintext, logged, or exposed.
§5: x-tenant-id metadata is injected on EVERY RPC; authorization metadata is
injected whenever $tokenAccessor returns a non-empty token.
Table of Contents
Methods
- __construct() : mixed
- getUserInfo() : GetUserInfoResponse
- `GetUserInfo` (CONTRACT.md §1.1) — returns the authenticated caller's OIDC-style identity claims. The request is empty; identity is derived entirely server-side from the `authorization` bearer metadata this call carries (§1.1.1/§1.1.2). `sub`, `tenant_id`, and `org_id` are always populated; `email`/`preferred_username` are gated on the "email"/"profile" token scopes respectively.
Methods
__construct()
public
__construct(string $hostname, callable(): Array $tokenAccessor, string $tenantId[, string|null $customCaPem = null ][, string|null $clientCertPem = null ][, Sensitive|null $clientKey = null ][, array<string, mixed> $options = [] ]) : mixed
Parameters
- $hostname : string
-
gRPC target, e.g. "api.axiam.example:9443".
- $tokenAccessor : callable(): Array
-
Reads the CURRENT access token live (shares the Session single-flight refresh mechanism) — never caches a copy, never triggers a refresh itself.
- $tenantId : string
-
Injected as the
x-tenant-idmetadata key on every RPC (§5). - $customCaPem : string|null = null
-
PEM-encoded custom CA bundle (§6's ONLY escape hatch); omit to use the system trust roots.
- $clientCertPem : string|null = null
-
§6.1 (mTLS): PEM client-certificate chain this channel presents for mutual TLS; omit for bearer-token-only auth. Must be present together with
$clientKey. - $clientKey : Sensitive|null = null
-
§6.1/§7 (mTLS): the matching private key, wrapped in Sensitive so it never leaks; revealed only to build the channel credentials, never retained in plaintext.
- $options : array<string, mixed> = []
-
Additional
\Grpc\BaseStubconstructor options (e.g. a caller-supplied per-call deadline);credentialsis always set by this constructor and cannot be overridden via$options.
getUserInfo()
`GetUserInfo` (CONTRACT.md §1.1) — returns the authenticated caller's OIDC-style identity claims. The request is empty; identity is derived entirely server-side from the `authorization` bearer metadata this call carries (§1.1.1/§1.1.2). `sub`, `tenant_id`, and `org_id` are always populated; `email`/`preferred_username` are gated on the "email"/"profile" token scopes respectively.
public
getUserInfo() : GetUserInfoResponse