What it is
Every authenticated request to Ekso carries a client marker that distinguishes how the call arrived:| Client | How the request arrived |
|---|---|
Web | Browser session, webapp authorization-code flow. Default when no claim is present. |
Cli | CLI device-flow login (ekso auth login). |
Sdk | API-key auth — every ApiKeyAuth call. |
UserContext.IsCliSdk (true for Cli or Sdk; false for Web). Controllers can read it to gate operations that should not be scriptable.
Why it exists
The webapp and the API are the same surface — there’s one set of HTTP routes, used by both browsers and SDK clients. That’s good for symmetry but creates a problem: some operations are safe in a UI but dangerous from a script. Concrete example — system field updates. An admin in the webapp legitimately renames the “Priority” field’s prompt or tweaks its description. The same request from a renegade script could brick a tenant’s field setup. Same route, different threat surface. The client marker lets the controller distinguish them: if the request arrived via SDK or CLI, apply the lockdown; otherwise let the webapp through.What’s currently gated
| Operation | Webapp | CLI / SDK |
|---|---|---|
| Create a custom field (any type) | ✅ | ✅ |
Update a custom field (IsCore=false) | ✅ | ✅ |
| Update a system field’s metadata (name, description, …) | ✅ | ❌ — 403 |
Update a system list field’s data values (e.g. add Critical to Priority) | ✅ | ✅ — via PUT /api/field/list/data |
Critical to Priority or new statuses to the workflow. That’s a legitimate scripted operation and stays open. What’s blocked is scripted renaming or deletion of system fields.
How it gets stamped
Three points stamp the marker:- Webapp authorization-code grant — issues a JWT with no
EksoClientclaim. Validation maps absent →Web. - CLI device-flow grant — issues a JWT with
EksoClient=Cli. The marker is preserved through token refreshes viaDataOAuthRefreshToken.ClientType, so a refreshed CLI token doesn’t silently downgrade toWeb. - API-key validation — sets
IsCliSdk=trueunconditionally. API keys are by definition non-interactive, soSdkis the right marker.
UserContext.IsCliSdk and is set per request by the auth middleware after token validation. Controllers never need to parse JWT claims directly.
How to react in your code
If you’re calling the SDK and you hit a 403 on a system-field update, you’ve found this gate:ekso field update-list <id> 403s on system lists, but ekso field update-list-data <id> works for both system and custom lists.
When to expect more gates
The CLI/SDK marker is a structural primitive — any operation we don’t want scripted gets the sameif (helper.UserContext.IsCliSdk) ... guard. Today only system fields are gated. Likely future additions:
- Bulk delete operations (already require
--confirmflags; may also gate from SDK). - Tenant-shape-changing operations (workspace renames, admin email changes).
- Anything where “someone left an API key in a Lambda” is a credible blast radius.
See also
- Authentication — how
ApiKeyAuthandRefreshableBearerAuthproduce different markers. - Error handling — handling 403s in your code.