This technical user guide provides step-by-step instructions for connecting to secured CBSS REST APIs.
1. Related documents
Document | Author |
---|---|
[1] CBSS REST Client Onboarding |
CBSS |
CBSS |
|
Smals |
2. Prerequisites
Prerequisites for Creating a signed client assertion and Obtaining an access token:
-
You have correctly followed the Smals OAuth2 Onboarding procedure [1], by which you have provided Smals with the public key of your client certificate and have received your client_id.
-
You possess the private key of your client certificate which will be used for authentication by your client application.
-
If you are a new client and wish to use the Smals Authorization Server internet endpoint in the integration environment, your IP (range) needs to be whitelisted at Smals. This process is documented in [1]. This IP whitelisting is not needed for the acceptance and production environments, nor for the extranet endpoints.
-
While this document tries not to assume any prior knowledge, having some basic understanding of concepts like OAuth 2.0 and JWT can be beneficial:
-
For a high level introduction of OAuth 2.0, we refer to the "Smals OAuth Cookbook" (NL - FR).
-
For JWT, there is this Introduction to JSON Web Tokens.
-
Prerequisites for Calling the CBSS REST API:
-
If you are a new client and wish to use the extranet endpoint of the CBSS API Gateway, a network flux may need to be opened (NL - FR) to the CBSS API Gateway.
-
You have received the API Cookbook and client annex documentation of the target API from CBSS.
-
The target API has been deployed and the application authorizations have been configured at CBSS.
3. Overview
CBSS REST APIs are secured via the OAuth 2.0 protocol, using the client credentials grant flow (more specifically with a digitally signed JWT client assertion for authentication).
For this purpose, CBSS makes use of the OAuth 2.0 infrastructure provided by Smals.
4. Instructions
These step-by-step instructions with code examples illustrate how to create a signed client assertion, use it to obtain an access token from the Smals Authorization Server, and finally use that access token to call a CBSS REST API.
Two variants of the code examples are provided:
-
Plain Java without any third-party libraries, to clearly demonstrate what’s going on behind the scenes
-
Java with Nimbus library
Not using Java? Find an OAuth library for your programming environment here. |
Show java imports used in code samples
import static java.nio.charset.StandardCharsets.*;
import java.io.StringReader;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Signature;
import java.time.Duration;
import java.time.Instant;
import java.util.Base64;
import java.util.UUID;
import jakarta.json.Json;
import jakarta.json.JsonObject;
import jakarta.json.JsonReader;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.time.Duration;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import com.nimbusds.oauth2.sdk.AccessTokenResponse;
import com.nimbusds.oauth2.sdk.ClientCredentialsGrant;
import com.nimbusds.oauth2.sdk.Scope;
import com.nimbusds.oauth2.sdk.TokenRequest;
import com.nimbusds.oauth2.sdk.TokenResponse;
import com.nimbusds.oauth2.sdk.auth.PrivateKeyJWT;
import com.nimbusds.oauth2.sdk.http.HTTPRequest;
import com.nimbusds.oauth2.sdk.token.AccessToken;
4.1. Creating a signed client assertion
A JWT client assertion should be created which is digitally signed using RS256
(RSA Signature with SHA-256).
A JWT token consists of 3 parts (header, payload and signature) separated by dots (.
), where each part is base64url encoded (without padding).
See this concrete example in the jwt.io token debugger.
Header:
{
"alg": "RS256",
"typ": "JWT"
}
Payload:
{
"jti": "427a9f8a-bca0-4c23-9b7d-0ab286199e70", (1)
"iss": "ksz-bcss:testing:service:client:confidentiel", (2)
"sub": "ksz-bcss:testing:service:client:confidentiel", (3)
"aud": "https://oauth-v5.int.socialsecurity.be", (4)
"iat": 1697621511, (5)
"exp": 1697625111 (6)
}
1 | a unique JWT identifier to prevent replay attacks |
2 | issuer = your client_id |
3 | subject = your client_id |
4 | audience = depending on the environment:
|
5 | issued at = the current timestamp (seconds since Unix Epoch) |
6 | expiration timestamp (seconds since Unix Epoch), in this example "issued at" + 1 hour |
Signature:
The concatenation of base64UrlEncode(header) + "." + base64UrlEncode(payload) is digitally signed using the private key of the client certificate. The result is base64url encoded, forming the 3rd part of the JWT client assertion. The resulting signed JWT client assertion is then used in the next step for Obtaining an access token.
private static String createJwtClientAssertion() throws Exception {
JsonObject header = Json.createObjectBuilder()
.add("alg", "RS256")
.add("typ", "JWT")
.build();
String base64Header = base64UrlEncode(header.toString());
JsonObject payload = Json.createObjectBuilder()
.add("jti", UUID.randomUUID().toString())
.add("iss", "ksz-bcss:testing:service:client:confidentiel")
.add("sub", "ksz-bcss:testing:service:client:confidentiel")
.add("aud", "https://oauth-v5.int.socialsecurity.be")
.add("iat", Instant.now().getEpochSecond())
.add("exp", Instant.now().getEpochSecond() + Duration.ofHours(1).toSeconds())
.build();
String base64Payload = base64UrlEncode(payload.toString());
PrivateKey privateKey = getPrivateKey();
Signature signature = Signature.getInstance("SHA256withRSA"); // = RS256
signature.initSign(privateKey);
signature.update((base64Header + "." + base64Payload).getBytes(ISO_8859_1));
String base64Signature = base64UrlEncode(signature.sign());
return base64Header + "." + base64Payload + "." + base64Signature;
}
private static String base64UrlEncode(String string) {
return base64UrlEncode(string.getBytes(ISO_8859_1));
}
private static String base64UrlEncode(byte[] bytes) {
return Base64.getUrlEncoder()
.withoutPadding()
.encodeToString(bytes);
}
private static SignedJWT createJwtClientAssertion() throws Exception {
JWTClaimsSet jwtClaims = new JWTClaimsSet.Builder()
.jwtID(UUID.randomUUID().toString())
.issuer("ksz-bcss:testing:service:client:confidentiel")
.subject("ksz-bcss:testing:service:client:confidentiel")
.audience("https://oauth-v5.int.socialsecurity.be")
.issueTime(new Date(System.currentTimeMillis()))
.expirationTime(new Date(System.currentTimeMillis()
+ Duration.ofHours(1).toMillis()))
.build();
SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), jwtClaims);
signedJWT.sign(new RSASSASigner(getPrivateKey()));
return signedJWT;
}
4.2. Obtaining an access token
With this signed JWT client assertion, an access token can be requested from the Smals OAuth 2.0 Authorization Server.
This is done by sending a POST request to the /REST/oauth/v5/token
endpoint of the Authorization Server, containing the following application/x-www-form-urlencoded
data:
-
grant_type:
client_credentials
-
scope: a space-delimited list of requested scopes
If your client_id has been configured [1] on the Authorization Server with a specific list of scopes, then the scope may be omitted from the token request. Nonetheless, it is recommended to specify the requested scope(s), following the general security principle of least privilege. |
-
client_assertion_type:
urn:ietf:params:oauth:client-assertion-type:jwt-bearer
-
client_assertion: the signed JWT client assertion
Show example of HTTP POST request to Smals Authorization Server
POST /REST/oauth/v5/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&scope=scope:cbss:api:monitoring scope:cbss:api:foreignLegalEntity:consult
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
&client_assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI4YmI2MTY2Mi0zMGYzLTRhNjUtYTQwMS1iYWEwYTExODk3ODciLCJpc3MiOiJrc3otYmNzczp0ZXN0aW5nOnNlcnZpY2U6Y2xpZW50OmNvbmZpZGVudGllbCIsInN1YiI6Imtzei1iY3NzOnRlc3Rpbmc6c2VydmljZTpjbGllbnQ6Y29uZmlkZW50aWVsIiwiYXVkIjoiaHR0cHM6Ly9vYXV0aC12NS5pbnQuc29jaWFsc2VjdXJpdHkuYmUiLCJpYXQiOjE2OTc2MjE1MTEsImV4cCI6MTY5NzYyNTExMX0.gpTamVWpaRXkiJRT_smPh5oCEtUA3j4hE64krkwbf_fAPojp3X4PSK_kTQIpuDJSwOvNlE8IjMjFp1ML0shbWSHils_QTIkC4WpNIKyVR-f0tCXBfFh0GUHh1hdAdE7j5sJhEeniJk5h2-JiovG4NefLAoa4QoEUzAg_RfhYY38fCrRxKjsf_vcLJXHomyrdKKV6jzzj2JuYhDU0hm_6XZWU1jj-tjDsT9c8PZB5Wq-odtf0Vdq6-cpmk3gvHTHJSe6F4q6tiE3cvoAJCd9vF-8u6y0Iw7rbXcWwdGtkb1wAZD5xftJdJV_ahOURXcr6OHuGnctRQJwu0r2mTgHvBw
If the request was successful, the response payload looks like this:
{
"access_token": "<JWT access token omitted>", (1)
"scope": "scope:cbss:api:monitoring", (2)
"token_type": "Bearer", (3)
"expires_in": 599 (4)
}
1 | the JWT access token, which is then used in the next step for Calling the CBSS REST API |
2 | the space-delimited list of granted scopes for this access token, this can be a subset of the requested scopes |
3 | the token type = Bearer |
4 | the lifetime of the access token, expressed in seconds, e.g. 599 = 9 minutes and 59 seconds |
It is strongly recommended to cache and reuse the access token during its validity period, and only request a new access token once it has expired. |
See Smals Authorization Server error handling for failure scenarios.
private static String requestAccessToken(String jwtClientAssertion) throws Exception {
String requestBody = "grant_type=client_credentials"
+ "&scope=scope:cbss:api:monitoring scope:cbss:api:foreignLegalEntity:consult"
+ "&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
+ "&client_assertion=" + jwtClientAssertion;
URI tokenEndpoint =
URI.create("https://services-int.socialsecurity.be/REST/oauth/v5/token");
HttpRequest tokenRetrievalRequest = HttpRequest.newBuilder()
.uri(tokenEndpoint)
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
JsonObject tokenRetrievalResponse = executeHttpRequest(tokenRetrievalRequest);
String accessToken = tokenRetrievalResponse.getString("access_token");
Duration lifespan = Duration.ofSeconds(tokenRetrievalResponse.getInt("expires_in"));
String[] scopes = tokenRetrievalResponse.getString("scope").split(" ");
return accessToken;
}
private static JsonObject executeHttpRequest(HttpRequest request) throws Exception {
HttpResponse<String> response = HttpClient.newHttpClient().send(
request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
println("Unexpected HTTP status: " + response.statusCode());
println(response.body());
throw new RuntimeException("Unexpected HTTP status: " + response.statusCode());
}
return parseJson(response.body());
}
private static JsonObject parseJson(String string) {
try (JsonReader reader = Json.createReader(new StringReader(string))) {
return reader.readObject();
}
}
private static AccessToken requestAccessToken(SignedJWT clientAssertion) throws Exception {
Scope requestedScope = new Scope(
"scope:cbss:api:monitoring",
"scope:cbss:api:foreignLegalEntity:consult");
// Create the HTTP request
URI tokenEndpoint =
URI.create("https://services-int.socialsecurity.be/REST/oauth/v5/token");
TokenRequest tokenRequest = new TokenRequest(
tokenEndpoint, new PrivateKeyJWT(clientAssertion),
new ClientCredentialsGrant(), requestedScope);
HTTPRequest tokenRetrievalRequest = tokenRequest.toHTTPRequest();
// Retrieve the access token
TokenResponse response = TokenResponse.parse(tokenRetrievalRequest.send());
if (!response.indicatesSuccess()) {
throw new IllegalStateException("Unexpected response: "
+ response.toErrorResponse().toJSONObject().toJSONString());
}
AccessTokenResponse tokenRetrievalResponse = response.toSuccessResponse();
AccessToken accessToken = tokenRetrievalResponse.getTokens().getAccessToken();
// Extract relevant values
Duration lifespan = Duration.ofSeconds(accessToken.getLifetime());
List<String> scopes = accessToken.getScope().toStringList();
return accessToken;
}
In principle, it is not necessary to decode and interpret the JWT access token received from the Authorization Server.
But for the sake of completeness, the structure of the access token is documented in the (collapsed) section below:
Structure of JWT access token issued by Smals OAuth 2.0 Authorization Server
Header:
{
"alg": "RS256", (1)
"kid": "oauth2-authz-server" (2)
}
1 | RSA Signature with SHA-256 |
2 | The JWKS key id used to sign the access token |
Payload:
{
"aud": [
"ksz-bcss:consultation:foleencbss-rest"
], (1)
"iat": 1697621512, (2)
"exp": 1697622112, (3)
"iss": "https://oauth-v5.int.socialsecurity.be", (4)
"nbf": 1697621452, (5)
"version": "4", (6)
"jti": "n1vum6elf9fjf63vt8e1iimhaj" (7)
}
1 | audience = the list of resource urn this access token is intended for |
2 | issued at timestamp (seconds since Unix Epoch) |
3 | expiration timestamp (seconds since Unix Epoch), in this example "issued at" + 10 minutes |
4 | issuer = depending on the environment:
|
5 | the timestamp before which the token should not be accepted (in this case "issued at" - 1 minute) |
6 | the version of the JWT token claims format |
7 | a unique JWT identifier to prevent replay attacks |
Signature:
The access token is digitally signed with the "oauth2-authz-server" key of the Smals OAuth 2.0 Authorization Server.
4.3. Calling the CBSS REST API
The JWT access token should be passed as a Bearer token to the CBSS REST API in the Authorization
HTTP header.
Furthermore, the following HTTP headers should be passed:
-
Legal-Context
: the legal context as specified in your client annex -
BelGov-Trace-Id
: optional (but recommended) unique trace id
The CBSS REST API returns the following HTTP response headers:
-
BelGov-Trace-Id
: the trace UUID generated by CBSS (please use this reference when contacting CBSS service desk) -
BelGov-Related-Trace-Id
: contains theBelGov-Trace-Id
you specified in the request (if any)
private static void invokeApi(String accessToken) throws Exception {
URI apiEndpoint =
URI.create("https://api.test.ksz-bcss.fgov.be/foreignLegalEntity/v1/health");
HttpRequest healthRequest = HttpRequest.newBuilder()
.uri(apiEndpoint)
.header("Authorization", "Bearer " + accessToken)
.header("BelGov-Trace-Id", UUID.randomUUID().toString()) // Client trace id
.header("Legal-Context", "EXAMPLE:DO_NOT_USE") // See client annex
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient().send(
healthRequest, HttpResponse.BodyHandlers.ofString());
println(response.body()); // {"status":"UP"}
println(response.headers().allValues("BelGov-Trace-Id")); // CBSS trace id
println(response.headers().allValues("BelGov-Related-Trace-Id")); // Client trace id
}
See CBSS API Gateway error handling for failure scenarios.
5. Testing
In order to verify that your implementation works, and that all relevant flows and configurations are OK, we suggest the following test procedure:
-
Verify the Smals OAuth 2.0 Authorization Server can be accessed by sending a GET request to the appropriate well-known endpoint found in Smals Authorization Server. Note that this request doesn’t require any authorization: the endpoint is accessible to all users.
-
If you receive a JSON response with HTTP status code 200, you can move to the next test step.
-
In all other cases you should investigate where the request goes wrong, first with your own network team and if that yields no results with Smals as documented in [1].
-
-
Verify the CBSS API Gateway can be accessed by sending a GET request to the appropriate JSON Web Key Sets (JWKS) endpoint found in CBSS API Gateway. This request doesn’t require any authorization: the endpoint is accessible to all users.
-
If you receive a JSON response with HTTP status code 200, you can move to the next test step.
-
In all other cases you should investigate where the request goes wrong, first with your own network team and if that yields no results with CBSS.
-
-
Verify you can retrieve an access token from the Smals OAuth 2.0 Authorization Server as described in Obtaining an access token.
-
If you receive a JSON response with HTTP status code 200, you can move to the next test step.
-
In all other cases you should analyze the returned error response. Look at Smals Authorization Server error handling for an overview of typical issues and solutions.
-
-
Verify you can perform a health check on the CBSS REST API by sending a GET
/health
request (relative to the API base path, e.g./example/v1/health
) to the API you plan on using.The CBSS REST API health check is more lenient in terms of authorization:
-
It does not require the
Legal-Context
HTTP header. -
It only requires a generic
scope:cbss:api:monitoring
scope that grants access to the health check of ALL CBSS REST APIs.
It is therefore used separately in our test procedure to quickly detect any issues with the Smals OAuth configuration.
-
If you receive a JSON response with HTTP status code 200, you can move to the next test step.
-
In all other cases you should analyze the returned error response. Look at CBSS API Gateway error handling for an overview of typical issues and solutions.
-
-
Verify you can use an actual operation of the CBSS REST API by sending a request, that isn’t a health check, to the API you plan on using.
-
If you receive a response valid according to the OpenAPI specification of the REST API, all tests are OK and you can continue with any functional testing you want to perform.
-
In all other cases you should analyze the returned error response. Look at CBSS API Gateway error handling for an overview of typical issues and solutions.
-
6. Endpoints
The correct endpoint depends on the environment and access channel (extranet/internet). This section contains an overview of all possible combinations, allowing you the easily select the most appropriate URL for each scenario.
Integration and test refer to the same environment, the name simply differs between Smals and CBSS. |
6.1. Smals Authorization Server
6.1.1. Integration
Service | Channel | Endpoint |
---|---|---|
Token retrieval |
extranet |
|
Token retrieval |
internet |
|
well-known |
|
6.1.2. Acceptance
Service | Channel | Endpoint |
---|---|---|
Token retrieval |
extranet |
|
Token retrieval |
internet |
|
well-known |
|
6.1.3. Production
Service | Channel | Endpoint |
---|---|---|
Token retrieval |
extranet |
|
Token retrieval |
internet |
|
well-known |
|
6.2. CBSS API Gateway
6.2.1. Test
Service | Channel | Endpoint |
---|---|---|
Any REST API |
extranet |
|
Any REST API |
internet |
|
JWKS |
6.2.2. Acceptance
Service | Channel | Endpoint |
---|---|---|
Any REST API |
extranet |
|
Any REST API |
internet |
|
JWKS |
7. Troubleshooting
7.1. Smals Authorization Server error handling
If you encounter a problem with the Smals Authorization Server that cannot be diagnosed with the help of the information listed below, you can contact the Smals IAM team for support. |
7.1.1. HTTP 403 Forbidden: "ip not allowed"
The internet endpoint in the integration environment of the Smals Authorization Server is protected with an ACL whitelist of allowed IP ranges, and responds with HTTP 403 Forbidden: "ip not allowed" if the client IP is not configured.
The procedure documented in [1] should be followed to add the client’s IP (range) to the ACL. Note that this is only required for this specific internet endpoint in the integration environments. The extranet endpoints, and the internet endpoints in the other environments, are not protected by such ACL.
7.1.2. HTTP 400 Bad Request
The Authorization Server may return with a HTTP 400 Bad Request, containing a JSON payload with the following fields:
-
error: invalid_request, invalid_client, invalid_grant, unauthorized_client, unsupported_grant_type, invalid_scope
Only invalid_request, invalid_client and invalid_scope are documented in further detail below, because we have never encountered the other errors in practice. |
-
error_description: human readable description providing additional information
-
id: a unique reference (corresponds with the BelGov-Trace-Id HTTP header)
invalid_request
{
"error" : "invalid_request",
"error_description" : "The request is missing a required parameter, includes an unsupported parameter value (other than grant type), repeats a parameter, includes multiple credentials, or is otherwise malformed.",
"error_uri" : "https://datatracker.ietf.org/doc/html/rfc6749",
"id" : "6c5029655667a6c771dcdc06"
}
Additionally, we have observed that in some scenario’s the Authorization Server may return a general HTTP 400 Bad Request that has a different structure:
{
"id": "bf4e29657da322b9fc20a0d3",
"code": "Bad Request",
"message": "The input message is incorrect"
}
Possible causes:
-
invalid request parameter values
invalid_client
{
"error" : "invalid_client",
"error_description" : "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method).",
"error_uri" : "https://datatracker.ietf.org/doc/html/rfc6749",
"id" : "2434296590d19ba234afd66c"
}
Possible causes:
-
typo in client_id
-
JWT client assertion signed with unknown certificate
-
expired JWT client assertion
-
incorrect audience in JWT client assertion (e.g. wrong environment)
invalid_scope
{
"error" : "invalid_scope",
"error_description" : "The requested scope is invalid, unknown, malformed, or exceeds the scope granted by the resource owner.",
"error_uri" : "https://datatracker.ietf.org/doc/html/rfc6749",
"id" : "83362965fe44ed0f2dce0d84"
}
Possible causes:
-
typo in requested scope(s)
-
authorization not configured for requested scope(s)
7.2. CBSS API Gateway error handling
If the CBSS API Gateway encountered an issue during the OAuth flow an appropriate Belgif problem type is returned.
An overview of the possible errors, with a brief explanation of the scenarios that can trigger them, is available below.
7.2.1. HTTP 400 Bad Request: Legal-Context
Operations of a CBSS API (that aren’t a health check) can only be executed within a certain legal context.
When a request did not include a (valid) value in the Legal-Context
HTTP header, a Bad Request problem type is returned.
Possible causes:
-
The request did not contain a
Legal-Context
HTTP header.
→ Verify your implementation. -
The value provided for the
Legal-Context
HTTP header is not syntactically valid.
→ Verify that the provided value corresponds to the one agreed upon with CBSS (see client annex).
The Legal-Context HTTP header value is case-sensitive.
|
Legal-Context
HTTP header{
"type": "urn:problem-type:belgif:badRequest",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/badRequest.html",
"title": "Bad Request",
"status": 400,
"detail": "The input message is incorrect",
"instance": "urn:cbss:trace-id:d0807cff-1152-4c60-b2de-079f1c832f15",
"issues": [
{
"type": "urn:problem-type:belgif:input-validation:schemaViolation",
"title": "input value is invalid with respect to the schema",
"detail": "is required",
"in": "header",
"name": "Legal-Context"
}
]
}
Legal-Context
HTTP header{
"type": "urn:problem-type:belgif:badRequest",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/badRequest.html",
"title": "Bad Request",
"status": 400,
"detail": "The input message is incorrect",
"instance": "urn:cbss:trace-id:dd9c1099-dc01-44d3-b944-c35ecf4783ca",
"issues": [
{
"type": "urn:problem-type:belgif:input-validation:schemaViolation",
"title": "input value is invalid with respect to the schema",
"detail": "ECMA 262 regex \"^[A-Z0-9]+([_-][A-Z0-9]+)*:[A-Z0-9]+([_-][A-Z0-9]+)*$\" does not match input string \"cbss:foo\"",
"in": "header",
"name": "Legal-Context",
"value": "cbss:foo"
}
]
}
7.2.2. HTTP 401: No Access Token
When the request did not contain a Bearer access token, a No Access Token problem type is returned.
Possible causes:
-
The request did not contain an
Authorization
HTTP header. -
The
Authorization
HTTP header was empty. -
The
Authorization
HTTP header used an authentication scheme which wasn’t "Bearer".
{
"type": "urn:problem-type:belgif:noAccessToken",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/noAccessToken.html",
"title": "No Access Token",
"status": 401,
"detail": "No Bearer access token found in Authorization HTTP header"
}
7.2.3. HTTP 401: Expired Access Token
The access token returned by the Smals OAuth authorization server is only valid for a limited amount of time, specified by the "expires_in" field in the response payload that also contained the token (see Obtaining an access token). Attempting to access a CBSS API using such an expired token results in an Expired Access Token problem response.
When caching the access token it is therefore recommended to foresee a safety margin: a token that is about to expire should not be used and a new access token should be retrieved. That way a slow request, i.e. one that takes a number of seconds to reach the CBSS API Gateway, doesn’t result in a refusal due to an already expired access token when it arrives at the CBSS.
{
"type": "urn:problem-type:belgif:expiredAccessToken",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/expiredAccessToken.html",
"status": 401,
"title": "Expired Access Token",
"detail": "The Bearer access token found in the Authorization HTTP header has expired"
}
7.2.4. HTTP 401: Invalid Access Token
The Bearer access token present in the request is validated by the CBSS API Gateway and upon detecting an issue an Invalid Access Token problem type is returned.
Validations:
-
Token structure: The Bearer access token should be an Encrypted JSON Web Token, consisting of 3 base64url encoded parts separated by dots (
.
). -
Issuer: The CBSS API Gateway only supports access tokens issued by the Smals OAuth 2.0 Authorization Server, which we identify by looking at the token’s
iss
value. Values which don’t appear in Access token issuers supported by the CBSS API Gateway result in an Invalid Access Token problem response. -
Smals OAuth configuration: The CBSS API Gateway verifies that the configuration, linked to the access token, corresponds to the CBSS standards. Any missing and/or invalid values result in an Invalid Access Token problem response.
Upon encountering such a problem response your implementation should be checked to make sure the access token sent to the CBSS API Gateway is correct (i.e. appropriate token structure and issuer). If that’s the case you should contact the CBSS project SPOC for further investigation as they will be able to identify and resolve any issues with the Smals OAuth configuration.
Environment | Supported iss values |
---|---|
Test |
|
Acceptance |
|
Production |
{
"type": "urn:problem-type:belgif:invalidAccessToken",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/invalidAccessToken.html",
"status": 401,
"title": "Invalid Access Token",
"detail": "The Bearer access token found in the Authorization HTTP header is invalid"
}
7.2.5. HTTP 403: Missing Scope
When the access token provided in the request was not linked to the scope(s) required by the resource, the CBSS API Gateway refuses the request by returning a Missing Scope problem response. In it, you’ll find a list of the required scope(s) for the requested resource.
Possible causes:
-
The client id for which an access token was provided isn’t linked to the required scope(s).
→ There is an issue with the Smals OAuth configuration, contact the Smals IAM team for assistance. -
An access token was requested for an incorrect scope.
→ Verify your implementation (see Obtaining an access token for an explanation on how to request scopes). -
A cached access token for another resource/scope was used.
→ Verify your implementation.
{
"type": "urn:problem-type:belgif:missingScope",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/missingScope.html",
"title": "Missing Scope",
"status": 403,
"instance": "urn:cbss:trace-id:1eee3628-e8f8-4028-a718-e6022140c064",
"requiredScopes": [
"scope:cbss:api:monitoring"
]
}
7.2.6. HTTP 403: Unauthorized Legal Context / (legacy) Missing Permission
Operations of a CBSS API (that aren’t a health check) can only be executed within a certain legal context. When the legal context that was passed in the HTTP header is not authorized to access the requested resource, a CBSS-specific "Unauthorized Legal Context" problem is returned.
{
"type": "urn:problem-type:cbss:unauthorizedLegalContext",
"title": "Unauthorized Legal Context",
"status": 403,
"detail": "Access is not allowed with the given legal context",
"legalContext": "CLIENT:SOME_LEGALCONTEXT",
"instance": "urn:cbss:trace-id:2742f0ed-8bbd-428f-a863-63bef36f29c9"
}
Verify that the provided value corresponds to the one agreed upon with CBSS (see client annex), and if that’s the case contact the project SPOC for further investigation. A problem response for a valid legal context might indicate it was not yet configured at CBSS.
Some older APIs may still return a Missing Permission problem, instead of "Unauthorized Legal Context". Missing Permission problem exampleMissing Permission problem response as defined by the Belgif REST Guidelines
|
7.2.7. HTTP 404: Not Found
When contacting a newly developed CBSS API you might encounter a 404 Not Found response. If you still encounter it after verifying the correct endpoint is called, the service is not yet deployed at CBSS. Contact the project SPOC for a timeline on when the service will become available.
<html lang="en">
<head>
<meta charset="UTF-8">
<title>No service available on this URL</title>
</head>
<body>No service available on this URL</body>
</html>
Another potential cause of HTTP 404 is that you are using the wrong access channel, for example when using the internet endpoint for an API that is only exposed through the extranet.
7.2.8. HTTP 500: Internal Server Error
When an unexpected internal error is encountered while processing the request, the CBSS API Gateway returns an Internal Server Error problem response. As this indicates there was an issue with a CBSS component, you should contact the project SPOC (test/acceptance) or the CBSS Service Desk (production) for further investigation.
{
"type": "urn:problem-type:belgif:internalServerError",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/internalServerError.html",
"status": 500,
"title": "Internal Server Error",
"instance": "urn:cbss:trace-id:6b4fefc8-a4c0-415d-83db-a318effa9d31"
}
7.2.9. HTTP 502: Bad Gateway
When there was an issue contacting a supplier service, the CBSS API Gateway returns a Bad Gateway problem response. Typically, this means the service is/was temporarly unavailable, which could potentially be solved by retrying a bit later. If the problem persists, however, you should contact the project SPOC (test/acceptance) or the CBSS Service Desk (production) for further investigation.
{
"type": "urn:problem-type:belgif:badGateway",
"href": "https://www.belgif.be/specification/rest/api-guide/problems/badGateway.html",
"title": "Bad Gateway",
"status": 502,
"detail": "Error in communication with upstream service",
"instance": "urn:cbss:trace-id:6b4fefc8-a4c0-415d-83db-a318effa9d31"
}
7.2.10. HTTP 503: Service Unavailable
As specified by the Belgif REST Guidelines a GET request to the /health
endpoint of an application can return a "Service Unavailable" response if the application is currently down.
If this downtime persists, i.e. lasts longer than a few minutes, you should contact the project SPOC (test/acceptance) or the CBSS Service Desk (production) for further investigation.
{
"status": "DOWN"
}
8. Contact info
Organisation | Name | Contact |
---|---|---|
CBSS |
Project SPOC |
See API Cookbook |
Service Desk |
servicedesk@ksz-bcss.fgov.be |
|
Smals |
IAM Team |