Skip to main content
Version: v0.13 (Latest)

Holder Functions

This guide covers the complete license lifecycle using the Kiwa eLicense SDK. You'll learn how to:

  • Obtain a wallet certificate for secure API communication
  • Assign a license to a device
  • Issue (retrieve) licenses for a license holder
  • Confirm successful license retrieval
  • Decode and work with license data

Prerequisites

Dependencies

Ensure your project includes the Kiwa eLicense SDK:

implementation("com.sphereon.kiwa:kiwa-holder-sdk-all:0.13.0")

See Installation for detailed setup instructions.

SDK Initialization

Initialize the SDK scopes and obtain service instances:

val kiwaServices = sessionInstance.getKiwaServices()
val authService = kiwaServices.auth
val holderService = kiwaServices.holder

See Scopes and DI for initialization details.

Configuration

Set your Kiwa subscription key via environment variable or configuration:

export KIWA_SUBSCRIPTION_KEY=your_subscription_key_here

Or programmatically through the configuration system. See Getting Started for all configuration options.

License Lifecycle

The typical license workflow follows these steps:

Kiwa License Lifecycle

Step 1: Obtaining a Wallet Certificate

Before performing license operations, authenticate and obtain an mTLS certificate:

import com.sphereon.kiwa.elicense.sdk.auth.model.GetWalletCertificateRequestOptions
import com.sphereon.kiwa.elicense.sdk.intern.env.model.ApiEnvironment

// Request a wallet certificate
val options = GetWalletCertificateRequestOptions(
alias = "kiwa-wallet-certificate", // Certificate alias for storage
environment = ApiEnvironment.PROD // Target environment (DEV, TEST, ACC, PROD)
)

val result = authService.commands.getWalletCertificate.execute(options)

result.onSuccess { certResult ->
println("Certificate obtained successfully")
// Certificate is now stored and will be used for subsequent API calls
}.onFailure { error ->
println("Error obtaining certificate: ${error.message}")
}

GetWalletCertificateRequestOptions

ParameterTypeDescription
aliasStringUnique identifier for storing the certificate
keyPairManagedKeyPair?Optional existing key pair (auto-generated if not provided)
certificateSigningRequestPemString?Optional CSR in PEM format
environmentApiEnvironment?Target environment (defaults to configured environment)
tip

The wallet certificate is typically obtained once per device and reused. The SDK automatically manages certificate storage and renewal.

Step 2: Assigning a License

After a license has been issued to a user's email address via the Kiwa IA API, assign it to the device:

import com.sphereon.kiwa.elicense.sdk.holder.license.model.AssignDeviceLicenseRequest

// Create the assignment request
val assignRequest = AssignDeviceLicenseRequest(
code = "ACTIVATION_CODE_FROM_EMAIL", // Activation code received via email
email = "user@example.com", // Email address associated with the license
environment = ApiEnvironment.PROD // Optional: override default environment
)

// Execute the assignment
val assignResult = holderService.commands.assign.execute(assignRequest)

assignResult.onSuccess { result ->
println("License assigned successfully")
}.onFailure { error ->
println("Error assigning license: ${error.message}")
}

AssignDeviceLicenseRequest

ParameterTypeDescription
codeStringActivation code provided by Kiwa (via email)
emailStringEmail address of the license holder
environmentApiEnvironment?Optional environment override

Step 3: Issuing a License

Retrieve all licenses available for the authenticated holder:

import com.sphereon.kiwa.elicense.sdk.holder.license.model.IssueLicenseRequest

// Create the issue request (environment is optional)
val issueRequest = IssueLicenseRequest(
environment = ApiEnvironment.PROD
)

// Issue the license
val issueResult = holderService.commands.issue.execute(issueRequest)

issueResult.onSuccess { result ->
// Access the parsed license documents
val documents = result.documents

// Access raw CBOR bytes if needed
val rawCbor: ByteArray = result.raw

// Process each license document
documents.mobileeIDdocuments.forEach { doc ->
println("License type: ${doc.docType}")

// Convert to display format
val display = doc.toSimpleDisplay()
println("Display JSON: ${display.toJsonString()}")
}
}.onFailure { error ->
println("Error issuing license: ${error.message}")
}

IssueLicenseSuccessResult

PropertyTypeDescription
documentsElicenseIssueDocumentsParsed license documents
rawByteArrayRaw CBOR-encoded license data
environmentApiEnvironmentEnvironment where the license was issued
note

The SDK automatically validates license signatures, certificates, and dates during issuance. Invalid licenses are rejected with appropriate error messages.

Step 4: Confirming License Issuance

After successfully retrieving licenses, confirm the receipt to the backend:

import com.sphereon.kiwa.elicense.sdk.holder.license.model.ConfirmDeviceLicenseRequest

// Confirm the license issuance
val confirmRequest = ConfirmDeviceLicenseRequest(
environment = ApiEnvironment.PROD
)

val confirmResult = holderService.commands.confirm.execute(confirmRequest)

confirmResult.onSuccess { result ->
println("License confirmation successful")
}.onFailure { error ->
println("Error confirming license: ${error.message}")
}

Decoding License Data

If you have raw CBOR-encoded license data, decode it using the decode command:

// Decode raw CBOR bytes
val decodeResult = holderService.commands.decode.execute(rawCborBytes)

decodeResult.onSuccess { documents ->
documents.mobileeIDdocuments.forEach { doc ->
val display = doc.toSimpleDisplay()
println(display.toJsonString())
}
}.onFailure { error ->
println("Decode error: ${error.message}")
}

Complete Example

Here's a complete example demonstrating the full license lifecycle:

import com.sphereon.kiwa.elicense.sdk.*
import com.sphereon.kiwa.elicense.sdk.auth.model.GetWalletCertificateRequestOptions
import com.sphereon.kiwa.elicense.sdk.holder.license.model.*
import com.sphereon.kiwa.elicense.sdk.intern.env.model.ApiEnvironment

suspend fun performLicenseWorkflow(
kiwaServices: KiwaServices,
activationCode: String,
email: String,
environment: ApiEnvironment = ApiEnvironment.PROD
) {
// Step 1: Get wallet certificate
val certOptions = GetWalletCertificateRequestOptions(
alias = "kiwa-wallet-certificate",
environment = environment
)

kiwaServices.auth.commands.getWalletCertificate.execute(certOptions)
.onFailure { error ->
println("Certificate error: ${error.message}")
return
}

println("Certificate obtained")

// Step 2: Assign license
val assignRequest = AssignDeviceLicenseRequest(
code = activationCode,
email = email,
environment = environment
)

kiwaServices.holder.commands.assign.execute(assignRequest)
.onFailure { error ->
println("Assignment error: ${error.message}")
return
}

println("License assigned")

// Step 3: Issue license
val issueRequest = IssueLicenseRequest(environment = environment)

val issueResult = kiwaServices.holder.commands.issue.execute(issueRequest)

issueResult.onSuccess { result ->
println("License issued successfully")

// Display license information
result.documents.mobileeIDdocuments.forEach { doc ->
val display = doc.toSimpleDisplay()
println("Document Type: ${display.docType}")
println("Valid From: ${display.validityInfo.validFrom}")
println("Valid Until: ${display.validityInfo.validUntil}")
println("Data: ${display.toJsonString()}")
}

// Step 4: Confirm license
val confirmRequest = ConfirmDeviceLicenseRequest(environment = environment)
kiwaServices.holder.commands.confirm.execute(confirmRequest)
.onSuccess {
println("License confirmed")
}.onFailure { error ->
println("Confirmation error: ${error.message}")
}

}.onFailure { error ->
println("Issue error: ${error.message}")
}
}

Error Handling

All SDK operations return a result type that can be either success or failure. Handle errors appropriately:

result.onSuccess { data ->
// Process successful result
}.onFailure { error ->
when {
error.message.contains("network") -> {
// Handle network errors
}
error.message.contains("certificate") -> {
// Handle certificate errors
}
else -> {
// Handle other errors
println("Error: ${error.message}")
}
}
}

Next Steps