AXIAM PHP SDK

AuthzDispatcher
in package

FinalYes

Transparent REST/gRPC authz transport selection (CONTRACT.md §1, D-03, SC#3).

checkAccess()/can()/batchCheck() ALWAYS work: with no grpc PECL extension (or restOnly: true) they route over POST /api/v1/authz/check[/batch] via AuthzRestClient (FND-04). When the extension IS present and restOnly is false, the SAME public methods transparently upgrade to AuthzGrpcClient — callers never need to know which transport actually ran.

PITFALL 4 / T-22-16 (high severity, mitigate): \Axiam\Sdk\Grpc\AuthzGrpcClient (which extends \Grpc\BaseStub) is referenced ONLY inside the !$this->restOnly && extension_loaded('grpc') branches below and inside self::grpcClient(), which those branches are the only callers of. On a REST-only runtime (no grpc PECL extension — the actual condition in this SDK's own CI/dev sandbox) that class is never autoloaded, so no "Class 'Grpc\BaseStub' not found" fatal is possible — AuthzDispatcherFallbackTest proves this empirically. The typed property self::$grpcClient and self::grpcClient()'s return type both name AuthzGrpcClient, but PHP resolves class names used only in type declarations lazily (confirmed empirically: a typed property/return-type naming a nonexistent class does not fatal a class that never assigns/invokes it) — the guard is what matters, not merely avoiding the class name in source text.

D-06: the gRPC path shares Session's single-flight refresh/token — $tokenAccessor is expected to be Session::accessToken(...) (or an equivalent live reader), never a second independent refresh mechanism.

Table of Contents

Methods

__construct()  : mixed
batchCheck()  : array<int, bool>
`batchCheck` (CONTRACT.md §1) — results preserve input order, mirroring {@see AuthzRestClient::batchCheck()}'s contract exactly regardless of transport.
can()  : bool
`can` (CONTRACT.md §1) — the browser/UI-scenario alias for {@see self::checkAccess()}.
checkAccess()  : bool
`checkAccess` (CONTRACT.md §1).
getUserInfo()  : UserInfo
`getUserInfo` (CONTRACT.md §1.1) — the gRPC-ONLY OIDC-style userinfo operation, the low-latency counterpart of the server's REST `GET /oauth2/userinfo`. Unlike {@see self::checkAccess()}/{@see self::batchCheck()} it has NO REST fallback: §1.1.6 explicitly forbids substituting the REST endpoint, so on a runtime without the `grpc` PECL extension (or with `restOnly: true`) this raises a {@see NetworkError} rather than silently degrading.

Methods

__construct()

public __construct(AuthzRestClient $restClient[, bool $restOnly = false ][, string|null $grpcTarget = null ][, string|null $tenantId = null ][, callable(): Array $tokenAccessor = null ][, callable(): string $subjectIdAccessor = null ][, string|null $customCaPem = null ][, string|null $clientCertPem = null ][, Sensitive|null $clientKey = null ][, callable(): void $refreshAccessor = null ]) : mixed
Parameters
$restClient : AuthzRestClient
$restOnly : bool = false
$grpcTarget : string|null = null
$tenantId : string|null = null
$tokenAccessor : callable(): Array = null

Reads the CURRENT access token live (D-06) — required only for the gRPC path; ignored entirely when restOnly is true or the grpc extension is absent.

$subjectIdAccessor : callable(): string = null

Reads the current authenticated user's subject UUID — the gRPC wire contract (unlike REST) requires subject_id explicitly, cross-validated server-side against the verified JWT (SEC-003); required only for the gRPC path.

$customCaPem : string|null = null
$clientCertPem : string|null = null

§6.1 (mTLS): PEM client-certificate chain the gRPC channel presents for mutual TLS; null leaves the channel using bearer-token auth only. Ignored on the REST path (Guzzle receives its own file-based copy).

$clientKey : Sensitive|null = null

§6.1/§7 (mTLS): the matching private key, wrapped in Sensitive so it never leaks; revealed only when building the gRPC channel credentials. Must be present iff $clientCertPem is.

$refreshAccessor : callable(): void = null

Drives the shared §9 single-flight token refresh (expected to be Session::refreshIfNeeded()->wait() or equivalent) — used ONLY by self::getUserInfo() to refresh-and-retry once on a gRPC UNAUTHENTICATED (CONTRACT.md §1.1.4), exactly as REST does on a 401. Never a second, independent refresh mechanism (D-06); null disables the retry (the UNAUTHENTICATED AuthError then surfaces directly).

batchCheck()

`batchCheck` (CONTRACT.md §1) — results preserve input order, mirroring {@see AuthzRestClient::batchCheck()}'s contract exactly regardless of transport.

public batchCheck(array<int, array{action: string, resourceId: string, scope?: string|null}> $checks) : array<int, bool>
Parameters
$checks : array<int, array{action: string, resourceId: string, scope?: string|null}>
Return values
array<int, bool>

can()

`can` (CONTRACT.md §1) — the browser/UI-scenario alias for {@see self::checkAccess()}.

public can(string $resource, string $action) : bool
Parameters
$resource : string
$action : string
Return values
bool

checkAccess()

`checkAccess` (CONTRACT.md §1).

public checkAccess(string $action, string $resourceId[, string|null $scope = null ][, string|null $subjectId = null ]) : bool
Parameters
$action : string
$resourceId : string
$scope : string|null = null
$subjectId : string|null = null

Additive, optional (CONTRACT.md §11.2.2 — declarative authorization helpers): when given, the check is evaluated for THIS subject rather than whichever identity the dispatcher's own configured session represents. null (the default) preserves the pre-§11 behavior exactly on both transports — REST omits subject_id from the wire body (server derives it from the verified JWT) and gRPC falls back to self::currentSubjectId() (the dispatcher's own session subject), exactly as before this parameter was added.

Return values
bool

getUserInfo()

`getUserInfo` (CONTRACT.md §1.1) — the gRPC-ONLY OIDC-style userinfo operation, the low-latency counterpart of the server's REST `GET /oauth2/userinfo`. Unlike {@see self::checkAccess()}/{@see self::batchCheck()} it has NO REST fallback: §1.1.6 explicitly forbids substituting the REST endpoint, so on a runtime without the `grpc` PECL extension (or with `restOnly: true`) this raises a {@see NetworkError} rather than silently degrading.

public getUserInfo() : UserInfo

Behavior (mirrors the checkAccess gRPC path + the REST 401 refresh path exactly):

  • §1.1.3 precondition: with no current access token, raises AuthError client-side WITHOUT any wire call.
  • §1.1.2 metadata: authorization: Bearer <token> + x-tenant-id on the RPC, via the same UserInfoGrpcClient channel machinery AuthzGrpcClient uses.
  • §1.1.4 auth-failure: a gRPC UNAUTHENTICATED (surfaced as AuthError) drives the shared §9 single-flight refresh ($refreshAccessor) and retries the RPC exactly once; a second failure propagates (no loop, §9.3).
  • §1.1.5 return shape: a typed UserInfosub/tenantId/orgId always set, email/preferredUsername present only when the token carried the "email"/"profile" scope (the server gates them; an absent optional is surfaced as null).
Return values
UserInfo
On this page

Search results