10Duke Enterprise C++ Client
Loading...
Searching...
No Matches
Using Identity-Based Licensing

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

10Duke Enterprise C++ client provides following types of OIDC authentication:

  • Browser-based authentication, where the client uses default OS-browser to perform the login.
  • OAuth "Device Authorization Grant" authentication. Device authentication is mostly intended for scenarios, where the device does not have a browser. The user authenticates out-of-band using browser on another platform.
  • OAuth "Client Credentials Grant", which is most suitable for authenticating servers and devices.
  • OAuth "Resource Owner Password Credentials Grant", which is not recommended, but is available for legacy use cases.

In all cases, the client manages the login session automatically whenever API-method needing authorization is used:

  • If the user has not yet logged, the first API call will automatically trigger a login.
  • If the user has logged in, but it looks like the access token has expired, the client automatically performs access token refresh and then executes the API call
  • If the refresh fails, the client re-logins the user
  • If HTTP request fails because the access token no longer functions (e.g. the access token has expired or the access token has been invalidated in the backend), the client automatically tries to refresh the token. If the refresh fails, the client re-logins the user. Once the access token has been refreshed, the client re-executes the request.

Application developer does not need to explicitly start login or refresh the session. You can register a callback, which gets notified, when certain login-related events (e.g. login is starting) happen, see Registering session event listener for details.

See also the client concepts. Our official documentation has an overview of identity-based licensing.

To create the client for browser-based authentication:

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

To create the client for Client Credentials Grant, use methods from factory tenduke::ee::CCGClientFactory

To create the client for Resource Owner Password Credentials grant, use methods from factory tenduke::ee::ROPGClientFactory.

Note the following client limitations:

  • We currently only support OIDC ID-tokens, which are signed with RSA-keys using (RS256)
  • We currently only support OAuth "Authorization Code Grant with PKCE" (see RFC 7636).
  • We currently only support single OIDC ID-token verification key
  • Client Credentials Grant does not provide ID-token

All these work with 10Duke Identity Provider.

Browser-based authentication

10Duke Enterprise C++ client provides default browser-based OIDC authentication using operating system default browser and "loopback interface redirection": When user needs to be authenticated, the client opens default OS-browser and starts the login flow with the OIDC-provider. The client simultaneously opens a simple local HTTP-server, which is used to detect when the login is complete: When the login is complete, the OIDC server will issue HTTP-redirect to a pre-configured URL (the redirect-uri) in the browser. For details see RFC 8252: 7.3 Loopback Interface Redirection.

The client has to be configured with HTTP-message, which is served by the HTTP-server after the login is complete. This HTTP message is full HTTP-response, with status line, headers and response message entity. This allows e.g. using HTTP-redirect to navigate the browser to a external site after login.

For example, to create client for browser-based authentication:

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::ee::createClientUsingAutodiscovery(
"browser-authentication-client-demo/0.0.1-alpha-1/mac",
::tenduke::ee::ClientProperties::Builder()
.hardwareId("simulated-hardware-id")
.build(),
"https://genco.10duke.net",
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, where the client is running, does not have a browser, or using the browser is awkward, the authentication can be done using "Device Authorization Grant", aka. device flow. To start the user login, the device shows an authentication URL and so called "user code" to the user. Using another device (e.g. 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 user session is set up.

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

When authenticating with device flow, the client has to be configured with a callback, which is called when the client needs to display the URL where user must navigate. The application developer has to implement the display of the information.

Example:

class OnDeviceAuthorizationResponseReceived : public ::tenduke::oauth::device::OAuthDeviceAuthorizationResponseReceived {
public:
void callback(const tenduke::oauth::device::DeviceAuthorizationResponse &response) override {
std::cout << std::endl
<< "Please open this address in your browser:" << 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<OnDeviceAuthorizationResponseReceived>(); // Keep handle of this for the duration of the client!
auto tendukeClient = ::tenduke::ee::createClientForDeviceUsingAutodiscovery(
"cpp-licensing-demo/0.0.0", // Identifies this client software
::ClientProperties::Builder()
.hardwareId("simulated-hw-id") // Hardware id
.build(),
"https://your-company-name.10duke.net", // URL of the deployment
::DeviceAuthenticationConfig(
"cpp-device-grant", // OAuth client-id, configured in the deployment
*callback
)
);

Authentication using OAuth Client Credentials Grant

If the entity to be authenticated is device or server, one possibility is to use OAuth Client Credentials Grant. The authenticated actor is the OAuth client, not a user, and because of this the ID-token is not available. This is by design.

Our developer guide has a section describing the Client Credentials Grant. For further details, like use cases or how to configure the backend for Client Credentials Grant, contact 10Duke support.

To set up the client to authenticate with Client Credentials:

auto tendukeClient = ::tenduke::ee::CCGClientFactory("cpp-licensing-demo/0.0.0").createClientUsingAutodiscovery(
::ClientProperties::Builder()
.hardwareId("simulated-hw-id") // Hardware id
.build(),
"https://url-of-the-deployment", // URL of the deployment
::CCGAuthenticationConfig(
"simulated-oauth-client-id", // OAuth client id, configured in the deployment. This is used as a username
"simulated-oauth-secret" // OAuth client secret, configured in the deployment This is used as a password
)
);

Authentication using OAuth Resource Owner Password Credentials Grant

We do not recommend using OAuth Resource Owner Password Credentials Grant and this grant is disallowed in OAuth 2.0 Security Best Current Practice.

To use this authentication method, create implementation of tenduke::oauth::ropg::ResourceOwnerPasswordCredentialsProvider and register instance of it when creating the client (see below for example). As the client maintains the state automatically, when the client determines that the user needs to log in, it executes the callback to provide the authentication credentials.

If the authentication fails because of invalid credentials, the client internally retries the authentication request by first executing the callback again and then re-executing the request with the new credentials. The client has maximum number of retries after which the client throws tenduke::oauth::OAuthInvalidGrant. The maximum number of retries can be configured when creating the client.

Our developer guide has a section about the Resource Owner Password Credentials Grant.

To create client, which authenticates the user with Resource Owner Password Credentials Grant:

// Sample callback:
class SampleCredentialsProvider : public ::tenduke::oauth::ropg::ResourceOwnerPasswordCredentialsProvider
{
public:
::tenduke::oauth::ropg::ResourceOwnerPasswordCredentials getCredentials() const override
{
std::string username;
std::string password;
std::cout << "Enter username:" << std::endl;
std::getline(std::cin, username);
std::cout << "Enter password:" << std::endl;
std::getline(std::cin, password);
return ::tenduke::oauth::ropg::ResourceOwnerPasswordCredentials(username, password);
}
};
SampleCredentialsProvider credentialsProvider; // Keep instance of this alive for the duration of the client
// Create the client:
auto tendukeClient = ::tenduke::ee::ROPGClientFactory("cpp-licensing-demo/0.0.0").createClientUsingAutodiscovery(
::ClientProperties::Builder()
.hardwareId("simulated-hw-id") // Hardware id
.build(),
"https://url-of-the-deployment", // URL of the deployment
::ROPGAuthenticationConfig(
credentialsProvider, // Provides the authentication credentials
"simulated-oauth-client-id", // OAuth client id, configured in the deployment
"simulated-oauth-secret", // OAuth client secret, configured in the deployment
3 // Max login attempts, 0 is "unlimited" (::ROPGAuthenticationConfig::DEFAULT_LOGIN_ATTEMPTS)
)
);

Registering session event listener

The client manages the user login session automatically by performing login (either by opening browser or by device flow callback) when you use API which needs valid user login session and the user has not yet logged in or when th login session has expired. The client also refreshes the login session automatically, when required. The application developer does not need to trigger login manually.

You might want to be notified, when login is starting, login is complete or the user session has been refreshed. For example, when login is complete, you might want to bring your application on top of all other windows: in browser login, the browser usually opens on top and you want to bring the user back to your application.

To get the notifications, register 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 parameter oidcSessionConfiguration 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-authentication, register the listener using parameter oidcSessionConfiguration of tenduke::oidc::osbrowser::BrowserAuthenticationConfig:

auto tendukeClient = ::tenduke::ee::createClientUsingAutodiscovery(
"browser-based-client-demo/0.0.1-alpha-1/mac",
::tenduke::ee::ClientProperties::Builder()
.hardwareId("simulated-hardware-id")
.build(),
"https://genco.10duke.net",
::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()
)
);

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::ee::TendukeClient.

Licensing operations

Checkout 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
}

Renew leases of the checked out licenses: (NOTE: Renewal changes the lease ids. The returned leases replace the original leases.)

auto renewResponse = tenduke->licensing->renewLeases()
.leases(result.leases)
.execute();
// Check for failures:
if (renewResponse.hasErrors()) {
// handle errors
}

To release the licenses (NOTE Renewal has changed the lease ids.)

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

Working with OIDC session

Get current OAuth access-token for authenticating requests to other 10Duke Enterprise APIs:

std::string accessToken = tendukeClient->oidcSession->getAccessToken();