Skip to main content
For deployments that prefer not to manage static API keys, the PHP SDK can authenticate via Workload Identity Federation (WIF). The SDK fetches a workload OIDC token from the running environment, exchanges it for a Nexus session JWT, and uses Authorization: Bearer <jwt> on every subsequent API call. WIF is opt-in. When NexusConfig::$wif is null (the default), the SDK uses the static apiKey flow and behaves identically to a client without WIF configuration.

Enabling WIF

Pass a WifConfig to NexusConfig::$wif:
apiKey becomes optional when wif is set. You can provide both - the SDK uses WIF and falls back to the static key if a session refresh fails after the initial connect.
Security note (v0.9.0): the baseUrl must use https:// - NexusClient::create() rejects plain-http endpoints (loopback/localhost excepted for development), since credentials travel on every request.

WifConfig reference

Provider auto-detection table

When provider is 'auto', the SDK probes the environment in this order, checking for actual credential material (the token files are stat-ed, not just env-var presence): Every free filesystem check precedes every network probe, which is why the Azure file check runs before the GCP probe and the Azure IMDS probe after it: an AKS workload must not wait on a probe that cannot succeed there to find a credential that was in a local file all along. developer is last despite being a file check, because on a real cloud host the workload’s own credential must always win over one that happens to be on the machine. The first matching provider is used. If none match and no credential source is configured, NexusClient::create throws a NexusException naming what was probed. aws_iam is not auto-detected - select it explicitly (resolvable AWS credentials alone, e.g. a dev laptop’s ~/.aws/credentials, are too weak a signal).

AWS IAM (non-EKS AWS compute)

ECS/Fargate tasks, Lambda functions, and plain EC2 instances have IAM credentials but no OIDC token, so the OIDC providers cannot serve them. The aws_iam provider closes this gap. It requires the optional aws/aws-sdk-php package:
The SDK SigV4-signs an STS GetCallerIdentity request with the standard AWS credential chain (task role, instance profile, env) - never sending it to AWS - and posts the signed headers + body to /v1/auth/token-exchange as {"provider":"aws_iam","aws_sts_request":...}. Nexus replays it against a pinned STS endpoint and matches the caller’s IAM role against an aws_iam trust policy (subject = arn:aws:iam::<account>:role/<name>). Nothing else to configure: the SDK signs your service’s own host into X-Nexus-Server-ID inside the signature - the baseUrl host, plus its port when that is not the scheme’s default, matching the host the backend validates against - and Nexus verifies it against the host the request arrived on, so a captured signed request is valid for that ONE service only. A deployment reached at a non-default port such as https://host:8443 is authenticated correctly. Selecting aws_iam without aws/aws-sdk-php installed throws an actionable NexusException.

Explicit provider

Pin a specific provider to skip auto-detection:

Local development (developer credentials)

A laptop has no workload OIDC token, so no cloud provider can match. The developer provider covers that case: the Westyx CLI performs the exchange once and stores the resulting Nexus session, and the SDK sends it as the Bearer.
Select WifProvider::DEVELOPER explicitly when you want the actionable “run westyx dev setup” error instead of “no WIF provider could be detected”. The CLI creates the credentials file 0600 inside a 0700 directory: The entry is matched on the endpoint’s host and on its port when that is not the scheme’s default, so a trailing slash, a scheme difference or a capitalised host does not hide it, while two local services on different ports stay distinct. $WESTYX_DEV_TOKEN overrides the file entirely. There is no token exchange on this path. The stored value is an already-issued Nexus session, so the SDK never posts to /v1/auth/token-exchange and never sees a Keycloak access or refresh token. The source is re-consulted on every session refresh, so re-running westyx dev setup in another terminal repairs a running process. A credential that is absent, expired, or too close to expiry to be refreshed against raises NexusDeveloperCredentialsException naming the service and the command.

Supplying your own credential

WifConfig::$credentialSource replaces the SDK’s resolution entirely - no provider is detected and no provider branch runs, so overriding resolution does not require aws/aws-sdk-php to be installed in order to select aws_iam.
Implement the interface rather than passing a callable when Laravel builds the client: a class-string survives php artisan config:cache and is resolved from the container, while a closure cannot be var_export()ed. A fault inside a source is reported as NexusTokenSourceException with the original as its cause, so it is never mistaken for the backend refusing the credential.

Token exchange flow

Internally, the SDK runs this protocol at startup and on each session refresh:
  1. Credential resolution - the configured credential source, or the selected/auto-detected provider, produces the credential. The developer provider skips this step and the next entirely: what it reads is already a Nexus session, so the SDK goes straight to using it as the Bearer.
  2. Token exchange - POSTs {"oidc_token": "<jwt>"} to /v1/auth/token-exchange. No auth header is sent on this call. The backend validates the OIDC issuer and signature, then returns:
  3. Bearer auth on subsequent calls - every /v1/* request carries Authorization: Bearer <session_token>.
  4. Pre-emptive refresh - when a request finds the session expires within 60 seconds, the SDK refreshes (re-running steps 1-2) before issuing the call.

Session refresh within 60 s of expiry

The SDK stores the session expiry time after each token exchange. Before each sync, write, or A/B test call, it checks whether the JWT expires within 60 seconds. If yes, it runs a token exchange first, then proceeds with the original call. connectStream()’s auth_expiring SSE event triggers a non-blocking early refresh ~5 minutes before expiry, reducing the chance of a mid-stream expiry.

Error cases

Project-level vs service-level trust policies

The Nexus backend supports trust policies at two scopes: At token exchange time the backend applies this resolution order:
  1. Does a service-level trust policy match this workload’s OIDC token claims? - if yes, authorise.
  2. Does a project-level trust policy match? - if yes, authorise.
  3. Neither matches - return 401.
This means you can configure one project-level trust policy and all services in the project accept that workload’s tokens. Each service client still uses its own baseUrl, so each gets its own service’s data.
A project-level trust policy is less restrictive than a service-level one. Any workload satisfying the policy can connect to any service in the project by targeting its baseUrl. For services handling sensitive data (payments, credentials, PII) consider a dedicated service-level trust policy instead.