runtime_keygen_sdk

Dart SDK for Keygen

Overview

Keygen is a fair source software licensing and distribution API. This package is meant to be used on the server and the client. Most actions in the Keygen API are supported.

Getting started with Keygen

Introduction to the Keygen API

Uses

This package will be useful for Dart applications that want to use Keygen for software licensing or distribution.

Supported platforms

All platforms that can use the http package are supported.

Installation

To add runtime_keygen_sdk to your Dart package, include it in your pubspec.yaml file:

dependencies:
  runtime_keygen_sdk: ^1.0.0

Setup

In order to use the Dart SDK for Keygen, you must first create a Keygen account.

Follow the instructions for Getting Started and take note of your:

  • Keygen AccountID
  • Admin Email
  • Admin Password

Example

Here is a pseudocode example of calling the Keygen API.

Note that this example will NOT work out-of-the-box.

KeygenClient client = KeygenClient('<accountId>');

KeygenLicensesApi licensesApi = KeygenLicensesApi(client);

License license = await licensesApi.createLicense(
    token: token,
    policy: policy,
    user: user
);

Tests

Create a .env file and place it in the root of the package.

The .env file needs to contain the Keygen AccountID, Admin Email, and Admin Password, specified like this:

KEYGEN_ACCOUNTID=<accountid>
KEYGEN_ADMIN_EMAIL=<email>
KEYGEN_ADMIN_PASSWORD=<password>

Then you can run:

dart test

Why are there so many calls to await Future.delayed(wait); in the tests?

Keygen enforces Rate Limiting and the delays are intended to prevent extraneous load.

These calls need to be in-between API calls; simply putting delays in setUp and tearDown is not sufficient.

Supported actions

Most actions in the Keygen API are supported, but not all.

The Keygen API can be divided into a few different categories:

Tokens are used by Keygen for authenticating API requests.

Products are used to segment policies and licenses, in the case where you sell multiple products. This allows you to keep licenses and policies between multiple products organized.

Entitlements can be attached to policies and to licenses to grant named "permissions" for things such as application features. Entitlements can be attached to the following resources:

  • Policies: Any entitlement attached to a policy will automatically be attached to all licenses which implement the given policy.
  • Licenses: Entitlements attached to a license are one-off and only apply to that specific license resource.
  • Releases: Entitlements can be attached to releases through constraints, in order to assert that a given licensee possesses the necessary entitlements before being allowed to access a release's artifacts.

Policies define the different "types" of licenses that your product offers. Policies can be for a number of different things, from different "tiers" for your product (e.g. Basic vs Pro) to fine-grained feature policies.

Users represent an identity for an end-user, or licensee, of your software.

Licenses are used to grant access to your software. Each license resource will implement a policy, which will define the "rules" which that license must follow to remain valid. Each license can be associated with a user, but that's optional; this is useful when you're associating multiple licenses with a single user.

Machines can be used to track and manage where your users are allowed to use your product.

There are other less common categories as well:

Components are hardware components for machines.

Environments can be created to segment your Keygen resources into buckets, for example, for an isolated sandbox environment, or a shared production environment.

Processes are concurrent processes across machines.

Distribution is the distribution and updating of software via Keygen.

Below are tables that outline which actions are implemented for each category, as well as what tests exist and where the actions fit in the Client / Server dichotomy.

TLDR:

  • User actions and License actions (except validating and checking-in) will run on server-side.
  • License validating, license checking-in, and Machine actions will run on client-side.

NOTE: Not every action will be executed client-side or server-side. Some actions (e.g. createProduct) are only executed during initial setup and are not executed during regular client / server interaction.

Token Actions Implemented Tests Client / Server
createToken
retrieveToken
regenerateToken
revokeToken
listTokens
Product Actions Implemented Tests Client / Server
createProduct One time only
retrieveProduct
updateProduct
deleteProduct
listProducts
createProductToken
Entitlement Actions Implemented Tests Client / Server
createEntitlement One time only
retrieveEntitlement
updateEntitlement
deleteEntitlement
listEntitlements
Policy Actions Implemented Tests Client / Server
createPolicy One time only
retrievePolicy
updatePolicy
deletePolicy
listPolicies
attachPolicyEntitlements
detachPolicyEntitlements
listEntitlements
User Actions Implemented Tests Client / Server
createUser
retrieveUser
updateUser
deleteUser
listUsers
updatePassword
resetPassword 📧
banUser
unbanUser
createUserToken
changeGroup
addSecondFactor
retrieveSecondFactor
updateSecondFactor
deleteSecondFactor
listSecondFactors
License Actions Implemented Tests Client / Server
createLicense Server
retrieveLicense Server
updateLicense Server
deleteLicense Server
listLicenses Server
validateLicenseByID
validateLicenseByKey Client
suspendLicense Server
reinstateLicense Server
renewLicense Server
revokeLicense Server
checkOutLicense
checkInLicense Client
incrementUsage
decrementUsage
resetUsage
createLicenseToken Server
attachUsers 🐛 🐛 Server
detachUsers 🐛 🐛 Server
listUsers 🐛 🐛 Server
attachEntitlements Server
detachEntitlements Server
listEntitlements Server
changeLicensePolicy Server
changeOwner
changeGroup
Machine Actions Implemented Tests Client / Server
activateMachine Client
retrieveMachine Client
updateMachine Client
deactivateMachine Client
listAllMachines Client
checkOutMachine
pingHeartbeat Client
resetHeartbeat Client
changeOwner 🐛 🐛 Server
changeGroup
Miscellaneous Actions Implemented Tests Client / Server
whoAmI 🐛 🐛 Client / Server
forgotPassword 📧

Legend

🐛 = bug in generated OpenAPI code from runtime_keygen_openapi package

whoAmI currently throws exception when called with license token or product token. license attachUsers is currently missing from OpenAPI .yml file from runtime_keygen_openapi package. license detachUsers is currently missing from OpenAPI .yml file from runtime_keygen_openapi package. license listUsers is currently missing from OpenAPI .yml file from runtime_keygen_openapi package. machine changeOwner is currently missing from OpenAPI .yml file from runtime_keygen_openapi package.

❌ = not planned to be supported (but may be re-evaluated)

📧 = requires reading email

The remaining groups of actions are not supported:

  • Components: not yet specified in OpenAPI spec file
  • Environments: not planned to be supported (but may be re-evaluated)
  • Processes: not planned to be supported (but may be re-evaluated)
  • Distribution: not planned to be supported (but may be re-evaluated)

Workflows

There are several common scenarios when using Keygen. These involve interacting with the client application, possibly some intermediary server, possibly the user's email, and Keygen itself.

One time:

Various actions may only need to be done once, and may be done from the Keygen dashboard.

Some of these actions are:

  • Create a product
  • Create entitlements
  • Create a policy

When creating a policy, you may want to specify the strategy to use for matching components. You can do that with these attributes:

requireComponentsScope = true
componentMatchingStrategy = MATCH_ANY, MATCH_TWO, MATCH_MOST, MATCH_ALL

Workflow: Creating a license

create license

Pseudocode for the client application would be:

1. start the application with no license key
2. send user credentials to intermediary server, and ask to create a new license
3. application receives license key from intermediary server
4. application stores license key

Pseudocode for the intermediary server would be:

1. receive request from application to create license
2. contact Auth0 to obtain user_id
3. send /createLicense request to Keygen
4. receive response from Keygen with license key and license id
5. send /updateLicense request to Keygen, with license id and Auth0 user_id to store in metadata
6. send license key to application

Workflow: Activating a machine

activate machine

Pseudocode for the client application would be:

1. get machine id and other component fingerprints
2. ask intermediary server to activate the machine, and send license key and component fingerprints
3. receive response from intermediary server that machine is activated

Pseudocode for the intermediary server would be:

1. receive request from application to activate machine with various fingerprints
2. send /whoAmI to Keygen with license key
3. receive license id
4. send /activateMachine to Keygen with license id and machine fingerprint
5. receive machine id
6. send /addComponent to Keygen with machine id and component fingerprints
7. send machine activation to application

Workflow: Validating a license

validate license

Pseudocode for the client application would be:

1. determine that it is time to validate license
2. send request to validate to intermediary server, with license key and fingerprints
3. receive validation from server

Pseudocode for the intermediary server would be:

1. receive request for validation from application, with license key and fingerprints
2. send /validateLicenseKey to Keygen, with license key and fingerprints
3. receive validation from Keygen
4. send validation to application

Caveats

Updating metadata

Since the metadata attribute is simply a key-value store (object), all write operations will overwrite the entire object, so be sure to merge existing data on your end when performing updates.

Metadata is not inheritable.

https://keygen.sh/docs/api/metadata/

https://github.com/orgs/keygen-sh/discussions/106

Implementation details

Modifications from generated OpenAPI code

This package uses runtime_keygen_openapi, which includes generated OpenAPI code.

OpenAPI defines some parameters as Object? metadata, but these have been modified to be Map<String, String>? metadata.

OpenAPI defines some parameters as Object? page, but these have been modified to be Map<String, int>? page.

Libraries

runtime_keygen_sdk
runtime_keygen_sdk