Authentication
Billerang v1 uses Keycloak (realm billerang) for authentication. Server-to-server
API calls use the OAuth 2.0 client credentials grant and send the resulting
bearer token on every request.
Get a token
curl -X POST "https://auth.billerang.com/realms/billerang/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET"
For a local stack the token endpoint is the local Keycloak container, typically
http://localhost:8081/realms/billerang/protocol/openid-connect/token.
Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 300
}
Call the API
Send the token as a Bearer credential on every request:
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
https://acme.billerang.com/api/v1/sellers
Required headers:
Authorization: Bearer <token>
Content-Type: application/json
Accept: application/json
Token refresh
The client credentials grant issues a short-lived access token (expires_in
above, in seconds). There is no refresh token in this grant; when the token
expires, request a new one with the same client credentials call. Client code
should treat a 401 on any call as a signal to fetch a fresh token and retry
once.
Scripted setup
#!/bin/bash
export KEYCLOAK_URL="https://auth.billerang.com"
export KEYCLOAK_REALM="billerang"
export CLIENT_ID="your-client-id"
export CLIENT_SECRET="your-client-secret"
get_token() {
curl -s -X POST "${KEYCLOAK_URL}/realms/${KEYCLOAK_REALM}/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=${CLIENT_ID}" \
-d "client_secret=${CLIENT_SECRET}" | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])"
}
export ACCESS_TOKEN=$(get_token)
Postman
{
"auth": {
"type": "oauth2",
"oauth2": [
{"key": "tokenName", "value": "Billerang Token"},
{"key": "accessTokenUrl", "value": "{{keycloak_url}}/realms/{{realm}}/protocol/openid-connect/token"},
{"key": "clientId", "value": "{{client_id}}"},
{"key": "clientSecret", "value": "{{client_secret}}"},
{"key": "grant_type", "value": "client_credentials"}
]
}
}
Error responses
401 Unauthorized
{
"status": "FAIL",
"message": "Authentication required"
}
403 Forbidden
{
"status": "FAIL",
"message": "Insufficient permissions for this operation"
}
See API principles for the full error response convention.