> ## Documentation Index
> Fetch the complete documentation index at: https://docs.westyx.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Spring Boot SDK - Workload Identity Federation

> Bearer-JWT auth with Kubernetes, AWS, GCP, and Azure for the Spring Boot SDK.

For deployments that prefer not to manage static API keys, the Spring Boot 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 falls back to the static `apiKey` flow and behaves exactly as in v0.1.0.

## Enabling via `application.yml`

```yaml theme={null}
nexus:
  base-url: https://blue-ocean-a5rx7.westyx.dev
  wif:
    enabled: true
    provider: kubernetes   # or aws / gcp / azure / auto
```

When `nexus.wif.enabled` is `true`, `nexus.api-key` becomes optional.

<Note>
  **Security note (v0.9.0):** `nexus.base-url` must use `https://` - `NexusClient.create` rejects plain-http endpoints (loopback/localhost excepted for development), since credentials travel on every request.
</Note>

## Enabling programmatically

```java theme={null}
NexusClient.create(NexusConfig.builder()
    .baseUrl("https://blue-ocean-a5rx7.westyx.dev")
    .wif(WIFConfig.builder()
        .enabled(true)
        .provider(WIFConfig.PROVIDER_AUTO) // default - auto-detect from env
        .build())
    .build());
```

## Custom token source via a `WIFConfig` bean

The auto-configuration picks up any `WIFConfig` bean automatically - supply one for environments the SDK can't auto-detect, or to provide a custom token source for tests:

```java theme={null}
@Configuration
class NexusWifConfig {

    @Bean
    WIFConfig nexusWif(MyOidcProvider oidc) {
        return WIFConfig.builder()
            .enabled(true)
            .tokenSource(oidc::fetchToken) // Supplier<String>
            .build();
    }
}
```

When a `WIFConfig` bean is defined it takes precedence over the `nexus.wif.*` properties.

## Supported providers

When `provider` is `auto` 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)*:

| Provider constant                                       | Auto-detect signal                                                                                                                            | Credential                                                                                                                                                                                      |
| ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `WIFConfig.PROVIDER_KUBERNETES` (`kubernetes`)          | `/var/run/secrets/kubernetes.io/serviceaccount/token` exists                                                                                  | reads the projected service-account token file (rotated by kubelet)                                                                                                                             |
| `WIFConfig.PROVIDER_AWS` (`aws`)                        | `AWS_WEB_IDENTITY_TOKEN_FILE` set **and the file exists** (EKS IRSA)                                                                          | reads the IRSA token file at that path                                                                                                                                                          |
| `WIFConfig.PROVIDER_AZURE` (`azure`, Workload Identity) | `AZURE_FEDERATED_TOKEN_FILE` set and the file exists (AKS)                                                                                    | reads the projected federated token file                                                                                                                                                        |
| `WIFConfig.PROVIDER_GCP` (`gcp`)                        | `metadata.google.internal` reachable (live probe)                                                                                             | GETs `metadata.google.internal/computeMetadata/v1/.../identity?audience=<aud>&format=full` with `Metadata-Flavor: Google`                                                                       |
| `WIFConfig.PROVIDER_AZURE` (`azure`, IMDS)              | `169.254.169.254/metadata` reachable (live probe)                                                                                             | GETs an Azure IMDS managed-identity token - **requires `audience: api://<client-id>`** (your app registration's Application ID URI; the generic default is refused because Azure AD rejects it) |
| `WIFConfig.PROVIDER_AWS_IAM` (`aws_iam`)                | **not auto-detected - select explicitly** (resolvable AWS credentials alone, e.g. a dev laptop's `~/.aws/credentials`, are too weak a signal) | SigV4-signs an STS `GetCallerIdentity` request (never sent to AWS) that Nexus replays to prove your IAM role - works on **ECS/Fargate, Lambda, and plain EC2**                                  |

The OIDC providers use only `java.net.http.HttpClient`; `aws_iam` additionally uses `software.amazon.awssdk:auth` + `:regions` (optional dependencies) for the credential/region chain and the SigV4 signer.

## 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. Add the optional AWS SDK dependencies (`software.amazon.awssdk:auth` + `:regions`), then:

```yaml theme={null}
nexus:
  wif:
    enabled: true
    provider: aws_iam
    aws-region: eu-central-1   # optional - defaults to the AWS region chain (env, config, EC2 IMDS)
```

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 (the base-URL host) into `X-Nexus-Server-ID` inside the signature, and Nexus verifies it against the host the request arrived on - so a captured signed request is valid for that ONE service only. Selecting `aws_iam` without the AWS SDK dependencies present throws an actionable error naming the Maven coordinates.

## Audience

GCP and Azure require an `audience` claim when issuing the OIDC token:

```yaml theme={null}
nexus:
  wif:
    enabled: true
    provider: gcp
    audience: westyx-nexus
```

**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.

Default: `westyx-nexus`.

## Token exchange flow

1. **OIDC token fetch** - the configured `Supplier<String>` 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:
   ```json theme={null}
   {
     "session_token":  "<jwt>",
     "token_type":     "Bearer",
     "expires_in":     3600,
     "service_kind":   "backend",
     "wif_provider":   "kubernetes"
   }
   ```
3. **Session storage** - the SDK caches the session token in memory and uses `Authorization: Bearer <session>` on all subsequent `/sync` and `/stream` requests.
4. **Auto-refresh** - \~60 s before expiry the SDK silently re-runs steps 1-2 from a background thread. 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 sync vs stream `HttpClient`

The SDK uses **two separate `HttpClient` instances** - one for short requests (`/sync`, `/token-exchange`, `connectTimeout = 10s`) and one for the long-lived SSE stream. This prevents a consumer-supplied short timeout from killing the stream after a few seconds.

## Project-level vs service-level trust policies

The Nexus backend supports trust policies at two scopes:

| Scope             | What it covers                               | Recommended for                                   |
| ----------------- | -------------------------------------------- | ------------------------------------------------- |
| **Project-level** | All services in the project share one policy | Most teams - no per-service cloud identity needed |
| **Service-level** | A specific policy bound to one service only  | High-security services (payments, PII)            |

<Warning>
  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 `base-url`. For services handling sensitive data (payments, credentials, PII) consider a dedicated service-level trust policy.
</Warning>
