Authorization: Bearer <jwt> on every subsequent API call.
WIF is opt-in. When NexusConfig.wif is undefined (the default), the SDK falls back to the static apiKey flow and behaves exactly as in v0.1.0.
Enabling
apiKey becomes optional when WIF is enabled.
Security note (v0.9.0):
endpoint must use https:// - NexusClient.create() refuses plain-http endpoints (loopback/localhost excepted for development), since credentials travel on every request.Supported providers
Whenprovider is WIFProviderAuto the SDK probes the environment in this order and picks the first provider whose credential material is actually present (v0.9.0 - file stats + live metadata probes, ~1 s timeout; earlier versions keyed on environment variables absent on real cloud nodes):
The OIDC providers need nothing beyond the standard library - the core SDK declares no runtime dependencies. AWS IAM needs the AWS signer, so it ships as a separate package,
@westyx-nexus/wif-aws-nodejs, which a consumer who does not use it never installs.
You can also pin a specific provider:
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 above cannot serve them. AWS IAM closes that gap. It ships as its own package and is selected throughwif.exchangePayload:
provider: 'aws_iam' returns an error naming the package and this replacement.
How it works:
- The provider SigV4-signs an STS
GetCallerIdentityrequest using the standard AWS credential chain (task role, instance profile, env). The request is never sent to AWS by the SDK. - The signed request (headers + body) is posted to
/v1/auth/token-exchangeas{"provider":"aws_iam","aws_sts_request":...}. - Nexus replays it against a pinned STS endpoint; AWS answers with the caller’s IAM identity, which Nexus matches against an
aws_iamtrust policy (subject = the assumed-role ARN, normalized toarn:aws:iam::<account>:role/<name>).
:8443 signs :8443 - to the provider, which signs it into the X-Nexus-Server-ID header before computing the signature, and Nexus verifies it against the host the exchange request actually arrived on. A captured signed request is therefore valid for that ONE service only - it cannot be replayed against any other service or deployment.
AWS IAM is never auto-detected - resolvable AWS credentials alone, such as a developer laptop’s ~/.aws/credentials, are too weak a signal. Supply the payload producer explicitly.
The STS host is partition-aware. sts.<region>.amazonaws.com does not exist in the AWS China partition, so cn-north-1 and cn-northwest-1 sign sts.<region>.amazonaws.com.cn instead.
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.
provider: WIFProviderDeveloper when you want the actionable “run westyx dev setup” error instead of “no provider matched”.
The CLI creates the credentials file 0600 inside a 0700 directory:
The entry is selected by endpoint host, so a trailing slash or a scheme difference between what the CLI stored and what you configured does not hide it.
$WESTYX_DEV_TOKEN overrides the file entirely - the escape hatch for a container or a CI-like shell with no user config directory.
What makes this provider different:
- No token exchange. The stored value is an already-issued Nexus session, so the SDK never posts to
/v1/auth/token-exchangeon this path. - No Keycloak token. The CLI performed the Keycloak exchange; only the resulting Nexus session reaches disk.
- Last in the chain. On a real cloud host the workload’s own credential always wins.
- Renewal without a restart. The source re-reads its input on every session refresh, so re-running
westyx dev setupin another terminal repairs a running process at its next refresh. - Fails closed with a command to run. An absent, expired, or too-soon-to-expire credential throws
NexusWIFNotConfiguredErrornaming the service andwestyx dev setup --service=<name>.
Custom token source
Provide atokenSource function to bypass auto-detection. The right hook for tests, custom CI flows, or topologies the SDK doesn’t natively recognise.
tokenSource is set it takes precedence over provider, and exchangePayload takes precedence over both. The SDK reports either as provider 'custom' - it used the credential you supplied rather than one it resolved itself. 'custom' is reportable but not selectable: provider: 'custom' is a compile error.
The one selector that does not defer to tokenSource is WIFProviderAWSIAM. It throws the message naming @westyx-nexus/wif-aws-nodejs either way, because signing an STS request and sending a bearer token are different authentication methods and the SDK will not quietly pick one.
Audience
GCP and Azure require anaudience claim when issuing the OIDC token:
'westyx-nexus' if unset.
Azure IMDS is the exception: the generic default is refused with a clear error, because Azure AD rejects it as a resource. On the IMDS path (plain VM / App Service - no federated token file) you MUST set audience to your app registration’s Application ID URI (api://<client-id>). On AKS with Azure Workload Identity the projected federated token file ($AZURE_FEDERATED_TOKEN_FILE) is preferred automatically and no audience is needed.
Token exchange flow
- OIDC token fetch - the configured token source returns a workload-identity JWT.
- Token exchange - the SDK POSTs
{"oidc_token": "<jwt>"}to/v1/auth/token-exchange. The backend validates the OIDC issuer/signature and returns: - Session storage - the SDK caches the session token in memory and uses
Authorization: Bearer <session>on all subsequent/syncand/streamrequests. - Auto-refresh - ~60 s before expiry the SDK silently re-runs steps 1-2 from a background promise. The active SSE stream is not interrupted.
auth_expiringevent - when the server emits this control event over SSE, the SDK pre-emptively refreshes the session before the server force-closes the stream.
developer provider skips steps 1 and 2 entirely. The value it reads is already a Nexus session - the CLI performed the exchange - so the SDK goes straight to session storage and never posts to /v1/auth/token-exchange.
Failures throw NexusWIFTokenExchangeFailedError (or NexusWIFNotConfiguredError for environment-detection failures). On the initial connect these are wrapped in NexusInitError. Background-refresh failures are reported through the configured logger with component: "wif", and the existing session is retried.
Service kind
The token-exchange response includes aservice_kind field ("backend" or "frontend"). The SDK propagates this to the NexusClient.kind getter, which getSecret() uses to gate access:
Project-level vs service-level trust policies
The Nexus backend supports trust policies at two scopes:
At token exchange time the backend checks service-level policies first, then falls back to project-level. All services in a project can share one Kubernetes service account.
