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

# Authentication

> Pick ApiKeyAuth for non-interactive callers or RefreshableBearerAuth for OAuth-issued tokens.

## Pick an auth strategy

The SDK supports two authentication strategies, both implementing `IEksoAuth`:

| Strategy                | Use when                                                                                                                                         |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ApiKeyAuth`            | Server-to-server, agents, CI runners, anything headless. Static credentials.                                                                     |
| `RefreshableBearerAuth` | You already have an OAuth token pair (e.g. obtained via the CLI's device-flow login or the web app's auth-code grant). Auto-refreshes on expiry. |

You only ever pass one of these to `EksoClientOptions.Auth`.

## ApiKeyAuth

API keys are minted via the admin surface (`POST /api/admin/api-key`, or `ekso api-key create --name "..."` from the CLI). The response contains the raw key (`ek_live_...`) **exactly once**; capture it immediately and store somewhere safe.

```csharp theme={null}
using Ekso.Sdk;
using Ekso.Sdk.Authentication;

var client = new EksoClient(new EksoClientOptions
{
    Tenant = "acme",
    Auth = new ApiKeyAuth(Environment.GetEnvironmentVariable("EKSO_API_KEY")!),
});
```

**Properties:**

* The key is sent as a `Bearer` token on every request.
* It carries the permissions of the user who minted it.
* It does **not** expire on a timer (unless `ExpiresAt` was set at mint time). Rotate on a policy.
* The backend stamps API-key requests with `Client=Sdk` — see [CLI/SDK marker](/sdk/cli-sdk-marker).

**Don't:**

* Embed long-lived keys in client-side code, mobile binaries, or browser-shipped JS.
* Commit keys to source control. Use a secrets manager (KeyVault, AWS Secrets Manager, env-var injection from CI).

## RefreshableBearerAuth

Use this when you've obtained an access + refresh token pair from an interactive flow (typically OAuth 2.0 device code or authorization-code-with-PKCE).

```csharp theme={null}
using Ekso.Sdk;
using Ekso.Sdk.Authentication;

var auth = new RefreshableBearerAuth(
    accessToken: stored.AccessToken,
    refreshToken: stored.RefreshToken,
    tokenEndpoint: new Uri("https://ekso.acme.com/token"),
    clientId: stored.ClientId,
    expiresAt: stored.ExpiresAt);

// Persist rotations so the next process picks up the rotated refresh token.
auth.TokensRefreshed += (_, e) =>
{
    SaveCredentials(new StoredCredentials
    {
        AccessToken = e.AccessToken,
        RefreshToken = e.RefreshToken,
        ExpiresAt = e.ExpiresAt,
    });
};

var client = new EksoClient(new EksoClientOptions
{
    BaseUrl = "https://ekso.acme.com",
    Auth = auth,
});
```

**Properties:**

* The SDK transparently exchanges the refresh token for a new access + refresh pair when the access token nears expiry.
* The `TokensRefreshed` event fires after each rotation so you can persist the new pair. **Without this hook**, the next process invocation would try to refresh with a token the backend has already invalidated and trigger family-rotation replay detection.
* Device-flow tokens carry `Client=Cli`; webapp authorization-code tokens carry no client claim (treated as `Web` at the read site).

## Local development

For local development, point the client at your dev install:

```csharp theme={null}
var client = new EksoClient(new EksoClientOptions
{
    BaseUrl = "https://devinc.localhost:7070",
    Auth = new ApiKeyAuth(localKey),
});
```

`BaseUrl` is required — there is no default. Self-host: this is whatever `PublicUrl` you set during your install's `/startup` wizard.

## Logging out / revoking

For `ApiKeyAuth`: revoke via the admin surface (`POST /api/admin/api-key/{id}/revoke` or `ekso api-key delete <id>`). The key stops working immediately on subsequent calls.

For `RefreshableBearerAuth`: there is no client-side "logout" — discard the stored token pair and the user's session is effectively gone. The refresh token expires server-side on a sliding window of inactivity.

## Token-store integration

The CLI persists `RefreshableBearerAuth` tokens to a per-install credential store on disk, keyed on the install URL (see `EksoClientFactory` in `Ekso.Cli`). If you're embedding the SDK in your own app, design your token store the same way — keyed by `(BaseUrl, ClientId)` so a developer with multiple installs (their own + a customer's) doesn't get cross-install token bleed.

## Next steps

* **[Quickstart](/sdk/quickstart)** — your first authenticated call.
* **[Error handling](/sdk/error-handling)** — what to expect when auth fails.
* **[CLI/SDK marker](/sdk/cli-sdk-marker)** — server-side gating semantics that depend on which auth you chose.
