DocsGuide
Authentication
An API key, the access token it turns into, how long a token lives, and the pattern for an app that acts as a person.
Keys and tokens
An API key is a client id and a client secret. You do not send the key itself on a call; you exchange it for an access token and send the token. The exchange is the OAuth 2.0 client credentials grant at the identity provider:
curl -u "$KEY_ID:$KEY_SECRET" https://auth.inorbit.hr/oauth2/token \
-d grant_type=client_credentials \
-d audience=tbd-api \
-d scope="identity:read account:read"{ "access_token": "eyJ…", "token_type": "bearer", "expires_in": 899, "scope": "identity:read account:read" }Then, on every call:
Authorization: Bearer eyJ…The token is a signed JWT with audience tbd-api and the scopes it was given. The
gateway verifies the signature and the audience on every request, admits the call only
when the token holds the route's scope, and hands it on with the identity inside; the services behind it never see the secret and never verify a
token themselves.
Where keys come from
Keys are made and revoked in the console at https://console.inorbit.hr/keys/, for your personal account or
for a team you own or administer. The secret is shown once, with this exchange filled in.
Scopes
A key holds the scopes ticked when it was made, and a token holds the ones asked for
in the exchange: scope is a space-separated list, any subset of the key's. Asking
for a scope the key does not hold is refused with invalid_scope; a call to a route
whose scope the token lacks is 403 forbidden. A key's scopes are fixed: to change
them, make a new key and revoke the old one.
| Scope | Lets a token call |
|---|---|
identity:read | GET /v1/me: the caller as the gateway verified it |
account:read | GET /v1/accounts/me: the account, its plan, the key's name and last use |
usage:read | GET /v1/accounts/orgs/{org_id}/units and /usage, GET /v1/accounts/units/categories: the account's units and the price list |
radar:read | GET /v1/radar/digests, /v1/radar/digests/{id}, /v1/radar/items: the radar's published digests |
Any token from a key may fetch GET /v1/openapi.json, the document for its plan.
Every operation in the Reference names its scope. Each product
opens to keys with scopes of its own as it is published.
Lifetime, rotation, revocation
- A token lives fifteen minutes. Fetch a new one before it expires; a call with an
expired token is
401 unauthenticated. - To rotate, make a second key, move your fleet to it, then revoke the first. An account holds up to ten keys at once.
- Revoking a key refuses new tokens at once. A token already issued keeps working until it expires, which is at most fifteen minutes.
- Keep the secret out of code and logs. The platform's own logs carry the key id, the route and the status of a call, never a body and never a secret.
The exchange in three languages
const basic = Buffer.from(`${process.env.KEY_ID}:${process.env.KEY_SECRET}`).toString("base64");
const res = await fetch("https://auth.inorbit.hr/oauth2/token", {
method: "POST",
headers: { authorization: `Basic ${basic}`, "content-type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
audience: "tbd-api",
scope: "identity:read account:read",
}),
});
const { access_token } = await res.json();import os, requests
r = requests.post(
"https://auth.inorbit.hr/oauth2/token",
auth=(os.environ["KEY_ID"], os.environ["KEY_SECRET"]),
data={"grant_type": "client_credentials", "audience": "tbd-api", "scope": "identity:read account:read"},
timeout=10,
)
token = r.json()["access_token"]form := url.Values{"grant_type": {"client_credentials"}, "audience": {"tbd-api"}, "scope": {"identity:read account:read"}}
req, _ := http.NewRequest("POST", "https://auth.inorbit.hr/oauth2/token", strings.NewReader(form.Encode()))
req.SetBasicAuth(os.Getenv("KEY_ID"), os.Getenv("KEY_SECRET"))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")An app that acts as a person
A key represents an account. An app that acts for a signed-in person (a mobile or
desktop client) uses the authorization code grant with PKCE against the same identity
provider, in the system browser, with audience=tbd-api on the authorization request,
and then sends the resulting bearer token exactly as above. Registering such a client
is a request to the operator today.
What the token carries
GET /v1/me shows it: subject (the key id for a key, the person's id for a person),
kind (client for a key, person for a person), scopes, and the account the call
counts against under org: the account a key belongs to, or a person's own.
GET /v1/accounts/me answers the account itself, with its plan and, for a key, the
key's name and when it was last used.