Skip to main content
For deployments that prefer not to manage static API keys, the .NET SDK can authenticate via Workload Identity Federation (WIF). The SDK obtains a workload credential from the running environment, exchanges it for a Nexus session JWT, and uses Authorization: Bearer <jwt> on every subsequent API call. The developer provider is the one exception: it reads a session the Westyx CLI already obtained, so there is nothing to exchange. WIF is opt-in. When NexusConfig.Wif is null (the default), the SDK authenticates with the static ApiKey.

Enabling

ApiKey becomes optional when WIF is enabled.
Security note: BaseUrl must use https:// - NexusClient.CreateAsync refuses plain-http endpoints (loopback/localhost excepted for development), since credentials travel on every request.

Supported providers

When Provider is WifProvider.Auto the SDK probes the environment in this order and picks the first provider whose credential material is actually present - every free filesystem or environment check before every live probe: developer sits last on purpose: on a real cloud host a real cloud identity must win, and a stale laptop credential must never shadow it. The OIDC providers use only the BCL HTTP client. aws_iam needs AWSSDK.Core for the credential and region chain, which is why it ships in its own package - a consumer who does not use it never carries that assembly. You can also pin a specific provider:
Provider is an enum and the set is closed at construction: a value that is not a declared member throws ArgumentOutOfRangeException listing the valid ones. On the dependency-injection path the bindable WifOptions.Provider is a string instead - see Design decisions.

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. The aws_iam provider closes this gap:
How it works:
  1. The SDK SigV4-signs an STS GetCallerIdentity request using the standard AWS credential chain (task role, instance profile, env). The request is never sent to AWS by the SDK.
  2. The signed request (headers + body) is posted to /v1/auth/token-exchange as {"provider":"aws_iam","aws_sts_request":...}.
  3. Nexus replays it against a pinned STS endpoint; AWS answers with the caller’s IAM identity, which Nexus matches against an aws_iam trust policy (subject = the assumed-role ARN, normalized to arn:aws:iam::<account>:role/<name>).
Nothing else to configure: the SDK signs your service’s own host (the BaseUrl host) into the X-Nexus-Server-ID header inside the SigV4 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 - select it explicitly.

Custom credential source

Implement IWifCredentialSource to supply a credential the SDK does not produce natively - a topology it does not recognise, a custom CI flow, or a test. It is called once per session refresh, never per request, and it takes precedence over Provider. The credential it returns is either shape the token-exchange endpoint accepts, which is what makes the extension point able to express a non-OIDC identity:
For a plain token-returning delegate:
On the dependency-injection path, registering one in the container is enough - services.AddSingleton<IWifCredentialSource, MyCredentialSource>(). WifOptions has no CredentialSource property of its own, since a service reference cannot come from configuration - the container is the only source on that path. aws_iam is itself a credential source, so there is nothing to combine it with - WifConfig.CredentialSource holds one source. A configuration that names aws_iam as a Provider throws at startup, naming the package to add and the call to make.

Local development (developer WIF)

Run westyx dev setup and the Westyx CLI exchanges your identity for a Nexus session and writes it to its credentials file. The SDK finds it with no configuration at all:
The same application code then authenticates with a projected service-account token in the cluster and with your own credentials on your laptop. Where the file lives - the CLI’s own configuration directory, per platform: How an entry is chosen - by matching its endpoint’s authority (host plus non-default port) against the client’s BaseUrl, case-insensitively, so a trailing slash or an explicit :443 does not defeat it. Expired entries are skipped, and a file holding entries only for other services is a perfectly normal state that simply yields no developer credential. WESTYX_DEV_TOKEN - when set and non-empty, it is used directly as the session token and no file is read. Two properties worth knowing. developer is probed last, after every cloud signal. And there is no token exchange: the CLI already performed it, so the session is used as the bearer directly and POST /v1/auth/token-exchange is never called. On expiry the client fails closed - no unauthenticated request is ever sent - with a message naming the command that renews the credential:
The credentials file is re-read on every session refresh, so running westyx dev setup while your process is alive is picked up without a restart.

Audience

GCP and Azure require an audience claim when issuing the OIDC token:
The default is "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

The developer provider skips steps 1 and 2 entirely - the CLI already holds a session.
  1. Credential fetch - WifConfig.CredentialSource (or the auto-detected built-in equivalent) returns a workload-identity JWT.
  2. Token exchange - the SDK POSTs {"oidc_token": "<jwt>"} to /v1/auth/token-exchange. The backend validates the OIDC issuer/signature and returns:
  3. Bearer auth on subsequent calls - every /v1/* request carries Authorization: Bearer <session_token>.
  4. Auto-refresh - ~60 s before expiry the SDK silently re-runs steps 1-2 from a background Task. The active SSE stream is not interrupted.
  5. auth_expiring event - when the server emits this control event over SSE, the SDK pre-emptively refreshes the session before the server force-closes the stream.

Split HTTP clients

The SDK uses two separate HttpClient instances - one for short requests (/sync, /token-exchange, Timeout = 10s) and one for the long-lived SSE stream (Timeout = Timeout.InfiniteTimeSpan). This prevents a consumer-supplied short timeout from killing the stream after a few seconds. If you pass your own HttpClient via NexusConfig.HttpClient, the SDK clones it for the stream side and overrides the timeout - your sync client is untouched.

Project-level vs service-level trust policies

The Nexus backend supports trust policies at two scopes:
A project-level trust policy is less restrictive than a service-level one. Any workload that satisfies 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.