10Duke Scale C++ SDK
Loading...
Searching...
No Matches
Use identity-based licensing

10Duke Scale supports identity-based licensing for users: users authenticate themselves and every operation tracks the users' identity. For authentication, 10Duke Scale uses Open ID Connect (OIDC), an authentication framework built on OAuth 2.0. To use identity-based licensing, you need an OIDC-based identity provider service. If you don't have one configured, contact 10Duke sales for options.

The 10Duke Scale C++ SDK provides two types of OIDC authentication: browser-based authentication and OAuth 2.0 "Device Authorization Grant" authentication. Device authentication is primarily intended for scenarios where the device lacks a browser: the user authenticates out-of-band using a browser on another platform. For browser-based authentication, the client uses the default OS browser to perform the login.

In both cases, the client automatically manages the login session whenever an API method requiring authorization is used:

  • If a user has not yet logged in, the first API call automatically triggers a login.
  • If a user has logged in, but the access token has expired, the client automatically performs an access token refresh and executes the API call.
  • If the refresh fails, the client performs a re-login for the user.
  • If an HTTP request fails because the access token is no longer valid (for example, it has expired or has been invalidated in the backend), the client automatically tries to refresh the token. If the refresh is successful, the client re-executes the original request. If the refresh fails, the client performs a re-login for the user.

You do not need to explicitly start login or refresh the session. You can register a callback that gets notified when certain login-related events (for example, login is starting) occur, see Register session event listener for details.

The service to use with identity-based licensing is tenduke::se::TendukeClientWithOIDCSession. See also the client concepts.

To create the client for browser-based authentication:

To create the client for device authentication, use the factory functions:

Note the following client limitations:

  • Only OIDC ID tokens signed with RSA-keys using (RS256) are supported.
  • Only OAuth "Authorization Code Grant with PKCE" is supported (see RFC 7636).
  • Only a single OIDC ID token verification key is supported.

Browser-based authentication

The 10Duke Scale C++ SDK provides browser-based OIDC authentication by default using the operating system default browser and "loopback interface redirection". When a user is authenticated, the client opens the default OS browser and starts the login flow with the OIDC provider. The client simultaneously opens a simple local HTTP server used to detect when the login is complete. After completing the login, the OIDC server issues an HTTP redirect to a preconfigured URL (the redirect URI) in the browser. For details see RFC 8252: 7.3 Loopback Interface Redirection.

The client must be configured with an HTTP message served by the HTTP server after the login is complete. The HTTP message is a full HTTP response with a status line, headers, and a response message entity. This allows, for example, using an HTTP redirect to navigate to an external site after login.

To create a client for browser-based authentication, see the below example:

std::string httpMessageAfterLogin =""
"HTTP/1.1 200 OK\n"
"Content-Type: text/html; charset=utf-8\n"
"\n"
"<html>\n"
"<head>\n"
" <title>Login successful, you can close the tab</title>\n"
"</head>\n"
"<body>\n"
" <H1>Login successful</H1>\n"
"</body>\n"
"</html>";
auto tendukeClient = ::tenduke::se::createTendukeClientForBrowserUsingAutodiscovery(
"browser-authentication-client-demo/0.0.1-alpha-1/mac",
::tenduke::se::BackendConfiguration("https://scale.10duke.com"), // ask proper URL from our support
::tenduke::se::ClientPropertiesBuilder()
.hwId("simulated-hardware-id")
.version("0.0.1-alpha-1")
.build(),
"https://genco.10duke.net/.well-known/openid-configuration", // URL for OIDC autodiscovery document
tenduke::oidc::osbrowser::BrowserAuthenticationConfig(
"demo-client", // oauthClientId, this value must also be configured in OIDC backend
"http://localhost/oidc/login", // oauthRedirectUri, this value must also be configured in OIDC backend
httpMessageAfterLogin // the HTTP message
)
);

Note that the redirect URI must start with http://localhost.

Device authentication

If the device running the client lacks a browser, or using the browser is awkward, authentication can be performed using the Device Authorization Grant (or device flow). To start the user login, the device shows an authentication URL and a user code to the user. Using another device (for example, a computer or tablet), the user starts authentication by navigating to the given URL and entering the user code. Once the authentication is complete, the client is notified and a user session is set up.

For details of the Device Authorization Grant, see RFC 8628.

When authenticating with device flow, the client must be configured with a callback that is called when the client needs to display the URL for the user. You must implement the display of the relevant information.

Example:

class Callback : public ::tenduke::oauth::device::OAuthDeviceAuthorizationResponseReceived {
void callback(const tenduke::oauth::device::DeviceAuthorizationResponse &response) override {
std::cout << std::endl
<< "Please forward your browser to address:" << std::endl
<< " " << response.verificationUri << std::endl
<< std::endl
<< "Authenticate yourself and enter following code when prompted:" << std::endl
<< " " << response.userCode << std::endl
<< std::endl
<< "Full URL for copy-pasting:" << std::endl
<< " " << response.verificationUriComplete << std::endl
;
}
};
// later in code:
auto callback = std::make_shared<Callback>(); // Keep handle of this for the duration of the client!
auto tendukeClient = ::tenduke::se::createTendukeClientForDeviceClientUsingAutodiscovery(
"device-flow-client-demo/0.0.1-alpha-1/mac",
::tenduke::se::BackendConfiguration("https://scale.10duke.com"), // you can find the API url from 10Duke Scale console
::tenduke::se::ClientPropertiesBuilder()
.hwId("simulated-hardware-id")
.version("0.0.1-alpha-1")
.build(),
"https://genco.10duke.net/.well-known/openid-configuration", // URL for OIDC autodiscovery document
::tenduke::oidc::device::DeviceAuthenticationConfig(
"demo-client", // oauthClientId, this value must also be configured in OIDC backend
*callback // The callback
)
);

Register session event listener

The client automatically manages the user login session. It performs the login (either by opening a browser or by device flow callback) when you use an API that requires a valid user login session, and the user has not yet logged in, or when the login session has expired. The client also refreshes the login session automatically when required. You do not need to trigger the login manually.

You can be notified when the login is starting or complete, or the user session has been refreshed. For example, when the login is complete, you can bring your application to the foreground. This is useful as the browser usually opens on top during the login.

To get the notifications, register the instance of tenduke::oidc::OIDCSessionEventListener when creating the client. You can also inherit from tenduke::oidc::DefaultOIDCSessionEventListener, which has empty default implementations for all the callback methods. To configure the listener, use the oidcSessionConfiguration parameter in the OIDC authentication configuration, see below for examples.

A sample listener:

class SampleSessionEventListener : public ::tenduke::oidc::DefaultOIDCSessionEventListener {
public:
void loginStarting() override {
std::cout << "LOGIN STARTING" << std::endl;
}
void loginComplete(const ::OIDCState &state) override {
std::cout << "LOGIN COMPLETE (access-token: " << state.getAccessToken() << ")" << std::endl;
}
};

When creating a client for browser-based authentication, register the listener using the oidcSessionConfiguration parameter of tenduke::oidc::osbrowser::BrowserAuthenticationConfig:

auto sessionEventListener = std::make_shared<::SampleSessionEventListener<>();
auto tendukeClient = ::tenduke::se::createTendukeClientForBrowserUsingAutodiscovery(
"browser-based-client-demo/0.0.1-alpha-1/mac",
::tenduke::se::BackendConfiguration("https://scale.10duke.com"),
::tenduke::se::ClientPropertiesBuilder().version("0.0.1-alpha-1").build(),
"https://genco.10duke.net/.well-known/openid-configuration",
::tenduke::oidc::osbrowser::BrowserAuthenticationConfig(
"demo-client",
"http://localhost/oidc/login",
httpMessageAfterLogin,
::OIDCSessionConfiguration::Builder() // use builder to configure oidcSessionConfiguration
.listenEventsWith(sessionEventListener) // register the custom session event listener
.build()
)
);

When creating a client for device authentication, register the listener using the oidcSessionConfiguration parameter of tenduke::oidc::device::DeviceAuthenticationConfig:

auto tendukeClient = ::tenduke::se::createTendukeClientForDeviceClientUsingAutodiscovery(
"browser-based-client-demo/0.0.1-alpha-1/mac",
::tenduke::se::BackendConfiguration("https://scale.10duke.com"),
::tenduke::se::ClientPropertiesBuilder().version("0.0.1-alpha-1").build(),
"https://genco.10duke.net/.well-known/openid-configuration",
::tenduke::oidc::device::DeviceAuthenticationConfig(
"demo-client",
*callback,
::OIDCSessionConfiguration::Builder() // use builder to configure oidcSessionConfiguration
.listenEventsWith(sessionEventListener) // register the custom session event listener
.build()
)
);

For fully working sample, see identity_based_client_example.cpp under examples.

Examples

Here are some examples on how to work with the client. For further details and complete API documentation, see tenduke::se::TendukeClientWithOIDCSession.

Licensing operations

Check out licenses:

auto checkoutResponse = tendukeClient->licensing->checkoutLicenses()
.version("1.0.0") // Set default version to check out, this applies to all following licenses,
// unless overridden at license level. Version is optional.
.seat("sample-product") // Check out one seat of "sample-product" with default version
.execute();
// Check for failures:
if (checkoutResponse.hasErrors()) {
// handle errors
}

Send a heartbeat for a lease:

auto heartbeatResponse = tendukeClient->licensing->heartbeatLicenses()
.leases(checkoutResponse.leases)
.execute();
// Check for failures:
if (heartbearResponse.hasErrors()) {
// handle errors
}

NOTE: A heartbeat generates new IDs for the leases.

To release the licenses after a heartbeat:

auto releaseResponse = tendukeClient->licensing->releaseLicenses()
.leases(heartbeatResponse.leases)
.execute();

The licensing client has a memory cache, which holds leases of checked-out licenses and automatically manages the leases after a heartbeat or release request. To list all leases currently in cache:

auto allLeases = tendukeClient->leases->getAllLeases();

The client allows you to only operate (heartbeat or release) on leases that are present in the cache. If you want to release all checked-out licenses, you can do:

auto releaseResponse = tendukeClient->licenses->releaseLicenses()
.leases(tendukeClient->leases->getAllLeases()
.execute();

Query information on available licenses:

You can use the tenduke::se::licensing::LicenseConsumers service to query information, for example, on available licenses. To query licensees whose licenses a user can consume, use tenduke::se::licensing::LicenseConsumers.describeLicenseConsumerLicensees():

auto licensees = tendukeClient->licenseConsumers->describeLicenseConsumerLicensees();

To list licenses owned by a licensee that a user can consume:

// Use first of the licensees queried above:
::tenduke::se::licensing::Licensee chosenLicensee = licensees.at(0);
auto licenses = tendukeClient->licenseConsumers->describeLicenseConsumerLicenses(chosenLicensee.id);

To list licenses that a user has currently checked out:

// Use first of the licensees queried above:
::tenduke::se::licensing::Licensee chosenLicensee = licensees.at(0);
auto checkedOutLicenses = tendukeClient->licenseConsumers->describeLicenseConsumerClientBindings(chosenLicensee.id);

Work with OIDC session

Get current OIDC ID token for authenticating requests to other 10Duke Scale APIs:

auto idtoken = tendukeClient->oidcSession->getOIDCState->getIdToken();