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 aWifConfig 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
Whenprovider 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. Theaws_iam provider closes this gap. It requires the optional aws/aws-sdk-php package:
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. Thedeveloper 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.
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.
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:- Credential resolution - the configured credential source, or the selected/auto-detected provider, produces the credential. The
developerprovider 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. - 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: - Bearer auth on subsequent calls - every
/v1/*request carriesAuthorization: Bearer <session_token>. - 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:
- Does a service-level trust policy match this workload’s OIDC token claims? - if yes, authorise.
- Does a project-level trust policy match? - if yes, authorise.
- Neither matches - return 401.
baseUrl, so each gets its own service’s data.
