End-to-end example
This ties the pieces together into one runnable program per language. It uses the service-account (client-credentials) flow because that runs unattended, with no browser step, so the whole thing fits in one file. The steps are:
- Check connectivity with
GET /health(no auth). - Authenticate to get an access token.
- Call an authenticated endpoint with
Authorization: Bearer. - Handle a 401 by getting a fresh token and retrying once.
The authenticated call targets an illustrative self/me?api-version=1.0 resource, to show the shape. Today the only live endpoint is /health; swap in a real resource as more of the API opens up. See Endpoints you call.
Set CSIQ_CLIENT_ID and CSIQ_CLIENT_SECRET in the environment first; never hard-code the secret.
#!/usr/bin/env bash
: "${CSIQ_CLIENT_ID:?Set CSIQ_CLIENT_ID to your service account's client id — see the Service accounts guide}"
: "${CSIQ_CLIENT_SECRET:?Set CSIQ_CLIENT_SECRET to your service account's client secret — see the Service accounts guide}"
set -euo pipefail
# Needs: jq (for parsing the token response).
AUTH="https://auth.cybersentriq.com/"
API="https://api.cybersentriq.com"
AUD="https://api.cybersentriq.com"
# 2. Authenticate (client-credentials). Secret comes from the environment.
get_token() {
curl -sS -X POST "${AUTH}oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=client_credentials" \
--data-urlencode "client_id=$CSIQ_CLIENT_ID" \
--data-urlencode "client_secret=$CSIQ_CLIENT_SECRET" \
--data-urlencode "audience=$AUD" | jq -r .access_token
}
# 1. Connectivity (no auth).
curl -sS "$API/health"; echo
TOKEN="$(get_token)"
# 3. Call an authenticated endpoint (illustrative resource).
code=$(curl -sS -o /tmp/csiq_body.json -w '%{http_code}' \
"$API/self/me?api-version=1.0" -H "Authorization: Bearer $TOKEN")
# 4. On 401, get a fresh token and retry once.
if [ "$code" = "401" ]; then
TOKEN="$(get_token)"
curl -sS "$API/self/me?api-version=1.0" -H "Authorization: Bearer $TOKEN"
else
cat /tmp/csiq_body.json
fi
const AUTH = "https://auth.cybersentriq.com/";
const API = "https://api.cybersentriq.com";
const AUD = "https://api.cybersentriq.com";
const CLIENT_ID = process.env.CSIQ_CLIENT_ID;
const CLIENT_SECRET = process.env.CSIQ_CLIENT_SECRET;
if (!CLIENT_ID) throw new Error("Set CSIQ_CLIENT_ID to your service account's client id — see the Service accounts guide");
if (!CLIENT_SECRET) throw new Error("Set CSIQ_CLIENT_SECRET to your service account's client secret — see the Service accounts guide");
async function getToken(): Promise<string> {
const res = await fetch(`${AUTH}oauth/token`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
audience: AUD,
}),
});
return (await res.json()).access_token;
}
async function main() {
// 1. Connectivity (no auth).
console.log(await (await fetch(`${API}/health`)).json());
// 2. Authenticate.
let token = await getToken();
// 3. Call an authenticated endpoint (illustrative resource).
const call = () =>
fetch(`${API}/self/me?api-version=1.0`, {
headers: { Authorization: `Bearer ${token}` },
});
let res = await call();
// 4. On 401, refresh (re-request) and retry once.
if (res.status === 401) {
token = await getToken();
res = await call();
}
console.log(res.status, await res.text());
}
main().catch((e) => { console.error(e); process.exit(1); });
import os
import requests
AUTH = "https://auth.cybersentriq.com/"
API = "https://api.cybersentriq.com"
AUD = "https://api.cybersentriq.com"
CLIENT_ID = os.environ.get("CSIQ_CLIENT_ID")
CLIENT_SECRET = os.environ.get("CSIQ_CLIENT_SECRET")
if not CLIENT_ID:
raise SystemExit("Set CSIQ_CLIENT_ID to your service account's client id — see the Service accounts guide")
if not CLIENT_SECRET:
raise SystemExit("Set CSIQ_CLIENT_SECRET to your service account's client secret — see the Service accounts guide")
def get_token():
res = requests.post(
f"{AUTH}oauth/token",
data={
"grant_type": "client_credentials",
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"audience": AUD,
},
)
return res.json()["access_token"]
def call(token):
return requests.get(
f"{API}/self/me",
params={"api-version": "1.0"},
headers={"Authorization": f"Bearer {token}"},
)
def main():
# 1. Connectivity (no auth).
print(requests.get(f"{API}/health").json())
# 2. Authenticate.
token = get_token()
# 3. Call an authenticated endpoint (illustrative resource).
res = call(token)
# 4. On 401, refresh (re-request) and retry once.
if res.status_code == 401:
token = get_token()
res = call(token)
print(res.status_code, res.text)
if __name__ == "__main__":
main()
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
class Program
{
const string Auth = "https://auth.cybersentriq.com/";
const string Api = "https://api.cybersentriq.com";
const string Aud = "https://api.cybersentriq.com";
static readonly HttpClient Http = new();
static readonly string ClientId = Environment.GetEnvironmentVariable("CSIQ_CLIENT_ID")
?? throw new InvalidOperationException("Set CSIQ_CLIENT_ID to your service account's client id — see the Service accounts guide");
static readonly string ClientSecret = Environment.GetEnvironmentVariable("CSIQ_CLIENT_SECRET")
?? throw new InvalidOperationException("Set CSIQ_CLIENT_SECRET to your service account's client secret — see the Service accounts guide");
static async Task<string> GetToken()
{
var form = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "client_credentials",
["client_id"] = ClientId,
["client_secret"] = ClientSecret,
["audience"] = Aud,
});
var res = await Http.PostAsync($"{Auth}oauth/token", form);
using var doc = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
return doc.RootElement.GetProperty("access_token").GetString();
}
static async Task<HttpResponseMessage> Call(string token)
{
var req = new HttpRequestMessage(HttpMethod.Get, $"{Api}/self/me?api-version=1.0");
req.Headers.Add("Authorization", $"Bearer {token}");
return await Http.SendAsync(req);
}
static async Task Main()
{
// 1. Connectivity (no auth).
Console.WriteLine(await Http.GetStringAsync($"{Api}/health"));
// 2. Authenticate.
var token = await GetToken();
// 3 + 4. Call, and on 401 refresh (re-request) and retry once.
var res = await Call(token);
if (res.StatusCode == HttpStatusCode.Unauthorized)
{
token = await GetToken();
res = await Call(token);
}
Console.WriteLine((int)res.StatusCode + " " + await res.Content.ReadAsStringAsync());
}
}
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
)
const (
authBase = "https://auth.cybersentriq.com/"
apiBase = "https://api.cybersentriq.com"
audience = "https://api.cybersentriq.com"
)
func getToken(clientID, clientSecret string) (string, error) {
form := url.Values{
"grant_type": {"client_credentials"},
"client_id": {clientID},
"client_secret": {clientSecret},
"audience": {audience},
}
res, err := http.PostForm(authBase+"oauth/token", form)
if err != nil {
return "", err
}
defer res.Body.Close()
var tok struct {
AccessToken string `json:"access_token"`
}
if err := json.NewDecoder(res.Body).Decode(&tok); err != nil {
return "", err
}
return tok.AccessToken, nil
}
func call(token string) (*http.Response, error) {
req, _ := http.NewRequest("GET", apiBase+"/self/me?api-version=1.0", nil)
req.Header.Set("Authorization", "Bearer "+token)
return http.DefaultClient.Do(req)
}
func main() {
clientID := os.Getenv("CSIQ_CLIENT_ID")
if clientID == "" {
log.Fatal("Set CSIQ_CLIENT_ID to your service account's client id — see the Service accounts guide")
}
clientSecret := os.Getenv("CSIQ_CLIENT_SECRET")
if clientSecret == "" {
log.Fatal("Set CSIQ_CLIENT_SECRET to your service account's client secret — see the Service accounts guide")
}
// 1. Connectivity (no auth).
health, err := http.Get(apiBase + "/health")
if err != nil {
log.Fatal(err)
}
body, _ := io.ReadAll(health.Body)
health.Body.Close()
fmt.Println(string(body))
// 2. Authenticate.
token, err := getToken(clientID, clientSecret)
if err != nil {
log.Fatal(err)
}
// 3 + 4. Call, and on 401 refresh (re-request) and retry once.
res, err := call(token)
if err != nil {
log.Fatal(err)
}
if res.StatusCode == http.StatusUnauthorized {
res.Body.Close()
if token, err = getToken(clientID, clientSecret); err != nil {
log.Fatal(err)
}
if res, err = call(token); err != nil {
log.Fatal(err)
}
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}
Signing in as a person instead
For a user-facing tool, swap step 2 for the device flow and step 4 for the refresh_token grant. Steps 1 and 3 (connectivity and the authenticated call) are identical; only how you obtain and renew the token changes.
Next
- The facts behind each step: Authentication overview.
- All guides: Developer guides.