A Spring Boot 3 client SDK for integrating with the Licensing Service. The SDK handles issuing and validating license access tokens (JWT/EdDSA), manages client‑side caching, signs requests with detached signatures, and provides a simple orchestration API for applications. This document describes only the licensing-service-sdk subproject.
- Simplified client SDK for licensing-service
- License token orchestration (issue, validate, refresh)
- Client-side caching of tokens (Caffeine)
- Detached signature generation for request integrity
- Error handling & i18n (localized messages)
- Swagger/OpenAPI documentation included
- Application calls SDK’s
/v1/licenses/accessendpoint with a license key (format:BSAYLI.<opaquePayloadBase64Url>). - SDK computes clientId (hash of instanceId + service info).
- SDK checks local cache for a token.
- If cache miss → calls Licensing Service /access to issue a new token.
- If cache hit → calls Licensing Service /access/validate with cached token.
- Licensing Service responds:
- Valid token →
TOKEN_ACTIVE - Refreshed token →
TOKEN_REFRESHED(SDK updates cache)
- SDK returns LicenseToken to the calling application.
Base path (configurable):
/licensing-service-sdk
Controller: LicenseController
POST /v1/licenses/access
Request Body — LicenseAccessRequest
{
"licenseKey": "<BSAYLI.<opaquePayloadBase64Url>>",
"instanceId": "crm~host123~00:AA:BB:CC:DD:EE",
"checksum": "<optional>",
"serviceId": "crm",
"serviceVersion": "1.5.0"
}Response — ApiResponse<LicenseToken>
{
"status": 200,
"message": "License is valid",
"data": {
"licenseToken": "<JWT>"
},
"errors": []
}cURL
curl -u licensingSdkUser:licensingSdkPass \
-H 'Content-Type: application/json' \
-d '{
"licenseKey":"<BSAYLI.<opaquePayloadBase64Url>>",
"instanceId":"crm~host123~00:AA:BB:CC:DD:EE",
"checksum":"<OPTIONAL>",
"serviceId":"crm",
"serviceVersion":"1.5.0"
}' \
http://localhost:8082/licensing-service-sdk/v1/licenses/access-
Bean Validation on DTOs (
@NotBlank,@Size, etc.) -
Global advice (
LicenseControllerAdvice) standardizes error output -
Domain & transport errors are mapped to error codes:
INVALID_PARAMETERTRANSPORT_ERRORREMOTE_ERROREMPTY_TOKEN
Response format: ApiResponse<Void> with list of ApiError entries.
- HTTP Basic auth (configurable credentials)
- Stateless sessions
- SDK signs requests with EdDSA private key (detached signature)
src/main/resources/application.yml (snippet)
server:
port: 8082
servlet:
context-path: /licensing-service-sdk
licensing-service-api:
base-url: "http://localhost:8081/licensing-service"
basic:
username: licensingUser
password: licensingPass
connect-timeout-seconds: 10
read-timeout-seconds: 15
caching:
spring:
licenseTokenTTL: 65m # refreshes well before server expiry (server ~90m + jitter)
signature:
private:
key: "<Base64-PKCS8-PrivateKey>"SERVER_PORT,SERVER_SERVLET_CONTEXT_PATHLICENSING_SERVICE_API_BASE_URL,LICENSING_SERVICE_API_BASIC_USERNAME,LICENSING_SERVICE_API_BASIC_PASSWORDCACHING_SPRING_LICENSETOKENTTLSIGNATURE_PRIVATE_KEY(Base64 PKCS#8 Ed25519 private key used to create detached signatures)
licenseTokens— local token cache (Caffeine)- Default TTL is 65m so the SDK renews well before server-side JWT expiry (90m ± jitter)
- Algorithm: EdDSA (Ed25519)
- Detached signature generation for issue/validate requests
- ClientId computed via SHA‑256 hash (instanceId + serviceId + serviceVersion + checksum)
- Swagger UI:
http://localhost:8082/licensing-service-sdk/swagger-ui.html - JSON:
/v3/api-docs· YAML:/v3/api-docs.yaml
- JDK 21
- Maven 3.x
mvn clean packagemvn spring-boot:run
# or
java -jar target/licensing-service-sdk-1.0.0.jarlicensing-service-sdk/
├─ src/main/java/io/github/bsayli/licensing/sdk/
│ ├─ api/(controller, dto, exception)
│ ├─ cache/(CacheNames.java, CacheConfig)
│ ├─ common/(api, exception, i18n)
│ ├─ config/(SecurityConfig)
│ ├─ generator/(ClientIdGeneratorImpl, SignatureGeneratorImpl)
│ ├─ service/(impl, client, handler)
│ └─ ...
├─ src/main/resources/
│ ├─ application.yml
│ └─ messages.properties
└─ pom.xml
- 401 Unauthorized: wrong Basic Auth credentials or signature mismatch
- 400 Bad Request: invalid input, check validation error messages
- 502 Bad Gateway: transport error when contacting Licensing Service
- Token empty: ensure Licensing Service is correctly configured
- licensing-service — server component
- license-generator — key & signature tooling
- licensing-service-sdk-cli — command-line client demo