Authentication & credentials
How the SDK authenticates with the Edgible API: which credentials you can supply, how the SDK finds them when you supply none, where sessions are stored, and how token refresh works. You should never need to touch a bearer token yourself — the client attaches and refreshes it on every request.
Zero-config on Node
Section titled “Zero-config on Node”If you have logged in with the edgible CLI on the same machine, the SDK picks up that session automatically:
import { createClient } from '@edgible-team/sdk';
const client = createClient(); // reuses the CLI's login sessionconst me = await client.auth.me();On Node, the default credential store reads (and writes) the same config.json the CLI uses, so the CLI and SDK share one login session.
Credential types
Section titled “Credential types”Pass credentials explicitly via createClient({ credentials }). Three input shapes exist today:
Password
Section titled “Password”const client = createClient({ credentials: { type: 'password', email: 'you@example.com', password: process.env.EDGIBLE_PASSWORD! },});Login happens lazily on the first request. The resulting tokens (including a refresh token) are saved to the credential store, and the client refreshes them automatically from then on. This is the most durable option for long-running processes.
You can also log in imperatively — await client.auth.login(email, password) — which persists the session to the store immediately.
const client = createClient({ credentials: { type: 'token', accessToken: myIdToken, refreshToken: myRefreshToken },});A pre-obtained bearer token. Note: the platform’s bearer token is the Cognito idToken — whatever string you pass as accessToken is attached verbatim as Authorization: Bearer <token>. If you omit refreshToken, the client cannot refresh: once the token expires, requests fail with an AuthenticationError (code: 'AUTH_TOKEN_EXPIRED').
Service accounts — not yet available
Section titled “Service accounts — not yet available”{ type: 'serviceAccount', keyId, secret } // accepted by the types, but…Org-scoped service accounts are a planned platform feature that needs backend support which has not shipped. The SDK accepts the shape so adoption is drop-in later, but using it today throws a ConfigurationError with code: 'SERVICE_ACCOUNT_NOT_SUPPORTED' on the first request. The same applies to the EDGIBLE_API_KEY / EDGIBLE_API_SECRET environment variables.
CI today: until service accounts land, authenticate CI jobs with one of the mechanisms that does work:
// Option A — a dedicated account's password from CI secrets (self-refreshing):import { createClient, InMemoryCredentialStore } from '@edgible-team/sdk';
const client = createClient({ credentials: { type: 'password', email: process.env.CI_EMAIL!, password: process.env.CI_PASSWORD! }, credentialStore: new InMemoryCredentialStore(), // don't persist tokens on the runner});# Option B — inject a short-lived token; no code changes needed:export EDGIBLE_TOKEN="<idToken>"Option B has no refresh token, so it suits jobs shorter than the token’s lifetime.
The provider chain
Section titled “The provider chain”When you don’t pass credentials, the client resolves one by trying sources in order and stopping at the first that yields a credential:
- Explicit —
createClient({ credentials }), when provided. - Environment —
EDGIBLE_TOKEN(a bearer token), thenEDGIBLE_API_KEY+EDGIBLE_API_SECRET(service account — currently throws on use, see above). - Credential store — the configured store; on Node this is the CLI’s
config.jsonsession. - Instance metadata — reserved for managed devices (Node-only; currently yields nothing).
Resolution happens once, on the first request. If no source yields a credential, the request throws an AuthenticationError with code: 'AUTH_NO_CREDENTIALS'.
You can replace the whole chain with createClient({ credentialProvider }) — any object with a resolve(ctx) method returning a ResolvedCredential or null. The building blocks (chainProvider, envProvider, storeProvider, explicitProvider) are exported if you want to compose your own.
Token stores
Section titled “Token stores”Persistence sits behind the CredentialStore interface (load / save / clear):
| Store | Default on | Behaviour |
|---|---|---|
FileCredentialStore | Node | Reads/writes the CLI’s config.json, so the SDK and CLI share one session. Only the token fields are touched — the rest of the file is preserved. Exported from @edgible-team/sdk/node. |
InMemoryCredentialStore | Browser | Holds tokens in memory only; nothing is ever written to disk. Also the right choice for CI. |
The shared config.json lives in the platform user-data directory:
- Linux:
$XDG_DATA_HOME/edgible/config.json, falling back to~/.local/share/edgible/config.json - macOS:
~/Library/Application Support/Edgible/config.json
Override the store with createClient({ credentialStore }); FileCredentialStore also accepts a filePath option to point at a different file.
Refresh behaviour
Section titled “Refresh behaviour”- Tokens are refreshed proactively, 5 minutes before the
expclaim of the idToken. - Refresh uses the stored refresh token against the platform’s refresh endpoint; the refreshed session is saved back to the credential store.
- If a request still gets a 401 (e.g. the token was revoked), the client performs exactly one forced refresh and replays the request once. A second 401 surfaces as an
AuthenticationError— no silent retry loop. - A token with no refresh token fails fast on expiry with
code: 'AUTH_TOKEN_EXPIRED'.
To end a session, await client.auth.logout() — this calls the backend and clears the stored credentials (for FileCredentialStore, it removes only the token fields from config.json).
Environment selection
Section titled “Environment selection”Each client targets one platform stage. Resolution order: explicit env option → EDGIBLE_ENV environment variable → 'prod'.
const client = createClient({ env: 'prod' }); // dev | test | uat | prodThe base URL derives from the stage (https://api.v2.<env>.edgible.com). To point somewhere else entirely, pass baseUrl, which overrides env:
const client = createClient({ baseUrl: 'https://api.example.internal' });An unrecognised stage value throws a ConfigurationError (code: 'CONFIG_INVALID'); an empty baseUrl throws code: 'CONFIG_MISSING_BASE_URL'.
Related
Section titled “Related”- Errors — the
AuthenticationError/ConfigurationErrortypes referenced above.