# Offline Licensing

In certain scenarios, some computers may **lack an active internet connection** or be behind a restrictive firewall, hindering online activation.

To address this, we have developed a mechanism that enables the activation and deactivation of licenses in an offline environment.

### Prerequisites

* Completed the [**Getting Started**](/sdks/tutorials/getting-started.md) tutorial, specifically:
  * Initialized `LicenseManager` (or `LicenseHandler`) with your configuration using the appropriate settings.
  * Created a `LicenseID` using either `LicenseID::fromKey` or `LicenseID::fromUser` function, depending on the activation method you prefer.

Performing offline license activation using the LicenseSpring SDKs is a straightforward process. Follow the steps below to activate and deactivate licenses.

{% stepper %}
{% step %}

### License Activation Process

* Generate a request file by calling the `createOfflineActivationFile` function of `LicenseManager`. Provide the `LicenseID` you created earlier and specify the path where you want to save the request file. The function will return the path to the created file.
* Open the [**offline activation page**](https://offline.licensespring.com/) and upload the request file you generated in the previous step. Once uploaded, download the offline activation file from this page.
* Activate the license by calling the `activateLicenseOffline` function of `LicenseManager` and passing the path to the offline activation file received from the website. The function will return a `License` object upon successful activation, or null if the activation process fails.
  {% endstep %}

{% step %}

### License Deactivation Process

* Call the `deactivateOffline` function of the `License` object, and optionally provide the path where you want to save the deactivation request file. The function will return the path to the created file.
* Call the `clearLocalStorage` function of `LicenseManager` to delete the files created by the SDK to store license data.
* Open the [**offline activation page**](https://offline.licensespring.com/) and upload the deactivation request file you generated in the previous step. Once uploaded, you will receive a message confirming that the license has been deactivated.
  {% endstep %}
  {% endstepper %}

Let's get started!

### Offline Activation Process

#### Request File Creation

To initiate offline license activation, the first step involves creating an offline activation request file.

You can accomplish this by utilizing the following function:

{% tabs %}
{% tab title="C++" %}

```cpp
auto filePath = licenseManager->createOfflineActivationFile(licenseId);
```

{% endtab %}

{% tab title="C#" %}

```csharp
string filePath = licenseManager.GetOfflineActivationFile(licenseId);
```

{% endtab %}

{% tab title="Swift" %}

```swift
let fileLocation = try manager.createOfflineActivationFile(licenseKey: "XXXX-XXXX-XXXX-XXXX")
```

{% endtab %}

{% tab title="Python" %}

```python
req_file_path = manager.create_offline_activation_file(license_id,'offline_files/test')

print(req_file_path)
```

{% endtab %}

{% tab title="Go" %}

```go
// Go SDK v2
offlineActivationData, path, err := license_handler.CreateOfflineActivationFile("path")
```

{% endtab %}

{% tab title="Java" %}

```java
LicenseIdentity identity = LicenseIdentity.fromKey("<LICENSE-KEY>");
String filePath = licenseManager.offlineActivationFile(identity, "pathToOutput");
```

{% endtab %}
{% endtabs %}

The provided function generates an offline activation request file for the specified key-based/user-based license, identified by `LicenseID`.

If you include a 'path' parameter, the file will be created with the specified path and name.

{% hint style="info" %}
Note: If 'path' is not provided, the default location for the offline activation request file will be the desktop, and the default name will be "ls\_activation.req": C:\Users%USERNAME%\Desktop\ls\_activation.req.
{% endhint %}

#### Uploading the Request to the Offline Activation Portal

Once you have generated your offline activation request file, the next step is to upload it to the [**LicenseSpring offline portal**](https://offline.licensespring.com/). It's essential to note that this process requires a computer with online access.

![LicenseSpring Offline Portal](/files/895c5dee705f8acd9c73d420256431d96b8a2900)

Upon uploading the request file, you will receive an offline activation response file named "ls\_activation.lic." This response file will enable you to proceed with the offline activation process successfully.

#### Activation With a Response File

To complete the offline activation process on a device, utilize the function `activateLicenseOffline(path)` as demonstrated below:

{% tabs %}
{% tab title="C++" %}

```cpp
license = licenseManager->activateLicenseOffline(path);
```

{% endtab %}

{% tab title="C#" %}

```csharp
ILicense license = licenseManager.ActivateLicenseOffline(filePath);
```

{% endtab %}

{% tab title="Swift" %}

```swift
let responseFile: URL = ...
let license = try manager.activateLicenseOffline(responseFile)
```

{% endtab %}

{% tab title="Python" %}

```python
license = manager.activate_license_offline('path_to_lic_file/ls_activation.lic')
```

{% endtab %}

{% tab title="Go" %}

```go
// Go SDK v2
ld, err := license_handler.ActivateLicenseOffline(path)
```

{% endtab %}

{% tab title="Java" %}

```java
LicenseIdentity identity = LicenseIdentity.fromKey("<LICENSE-KEY>");
License license = licenseManager.activateOfflineResponse(identity, "pathToFile");
```

{% endtab %}
{% endtabs %}

In the 'path' parameter, provide the name and path of the offline activation response file.

If 'path' is left empty, the function will search for the response file named "ls\_activation.lic" on the desktop by default: C:\Users%USERNAME%\Desktop\ls\_activation.lic.

By running this, you will have successfully activated the license offline on the device.

There is a checker for if the license is offline activated also, as shown below:

{% tabs %}
{% tab title="C++" %}

```cpp
bool offlineActivatedTest = license.isOfflineActivated();
```

{% endtab %}

{% tab title="C#" %}

```csharp
bool offlineActivatedTest = license.IsOfflineActivated();
```

{% endtab %}

{% tab title="Swift" %}

```swift
license.isOfflineActivated
```

{% endtab %}

{% tab title="Java" %}

```java
boolean isOfflineActivated = license.isOfflineActivated();
```

{% endtab %}
{% endtabs %}

This Boolean checker returns true if offline activated, and false if not.

#### How to Secure Offline Activation With ExtendedOptions::enableGuardFile

Guard files **prevent users from using the same license request file** and are automatically enabled within `ExtendedOptions`.

Guard files can be disabled in the `ExtendedOptions` constructor or by running the following method, where options is an `ExtendedOptions` object:

{% tabs %}
{% tab title="C++" %}

```cpp
options.enableGuardFile(false);
```

{% endtab %}

{% tab title="C#" %}

```csharp
extendedOptions.ProtectOfflineActivation = false;
```

{% endtab %}

{% tab title="Python" %}

```python
from licensespring.licensefile.config import Configuration

conf = Configuration(product="your_product_key",api_key="your_api_key",
                   shared_key="your_shared_key",file_key=key,file_iv=iv,
                   is_guard_file_enabled=True)
```

{% endtab %}
{% endtabs %}

Once enabled, LicenseSpring generates a guard file in the same folder where the license and SDK logs are stored.

This guard file remains hidden and plays a crucial role in safeguarding the licensing process. Should anyone attempt to remove or alter this file, the license activation will fail as a protective measure.

The guard file is automatically created when you generate the request file for offline activation.

It must remain valid and present when a user submits the response file to ensure a successful activation process. The presence of the guard file is pivotal for maintaining the integrity and security of the offline activation procedure.

To determine the current status of the guard file, you can utilize the following method (where `options` is an `ExtendedOptions` object):

{% tabs %}
{% tab title="C++" %}

```cpp
bool guardFileTest = options.isGuardFileEnabled();
```

{% endtab %}

{% tab title="C#" %}

```csharp
bool guardFileTest = options.ProtectOfflineActivation;
```

{% endtab %}
{% endtabs %}

This method also exists within the `Configuration` object, so using the following (where `config` is a `Configuration` object) will yield an identical result:

```cpp
bool guardFileTest = config.isGuardFileEnabled();
```

If a guard file is actively protecting the offline activation process `guardFileTest` returns true.

### Offline License Update

#### How to Get a Refresh File

Offline license refresh allows users to update their activated offline license, which is typically used when certain aspects of the license have been modified on the backend.

For instance, you might want to add a new feature without requiring license reactivation.

To perform an offline license refresh, locate the license on the LicenseSpring Platform and access the `Devices` tab.

![Downloading License Refresh File](/files/01e2dcf7b282ec25f4a8c559e1948ad3764f18a8)

#### UpdateOffline Method Usage

The line of code responsible for the update is as follows:

{% tabs %}
{% tab title="C++" %}

```cpp
license->updateOffline( path );
```

{% endtab %}

{% tab title="C#" %}

```csharp
bool isUpdated = license.UpdateOffline(filePath);
```

{% endtab %}

{% tab title="Swift" %}

```swift
let updateFile: URL = ...
try license.updateOffline(with: updateFile)
```

{% endtab %}

{% tab title="Python" %}

```python
license.update_offline('offline_files/license_refresh.lic',False) 
```

{% endtab %}
{% endtabs %}

In the `path` parameter, you should provide the path to the `license_refresh.lic` file, which you downloaded from the page, as shown above.

`updateOffline` also has a secondary optional parameter, `resetConsumpion`, which is disabled by default. If this parameter is set to true, then this update also resets the consumption.

This method returns true if the license is successfully updated, and false otherwise.

### Offline Deactivation

#### How to Create Deactivation Request File

Once a license has been activated, it can also be deactivated through a similar process. The function responsible for creating the offline deactivation request file is as follows:

{% tabs %}
{% tab title="C++" %}

```cpp
auto filePath = license->deactivateOffline();
```

{% endtab %}

{% tab title="C#" %}

```csharp
string filePath = license.DeactivateOffline(deactivationRequestFilePath);
```

{% endtab %}

{% tab title="Swift" %}

```swift
let request = try license.deactivateOffline()
```

{% endtab %}

{% tab title="Python" %}

```python
license.deactivate_offline('path_where_to_create_req_file')
```

{% endtab %}

{% tab title="Go" %}

```go
// Go SDK v2
ld, deactivationReq, path, err := license_handler.DeactivateOffline("path")
```

{% endtab %}
{% endtabs %}

Additionally, you have the option to provide an alternative path as an optional parameter to the `deactivateOffline` function, where the deactivation request file will be created and stored.

{% hint style="info" %}
Note: If you leave this parameter empty, the default file path will be used, saving the file to the desktop.
{% endhint %}

#### Deleting Local License File

To eliminate any local license data, after creating the deactivation file, execute the following:

{% tabs %}
{% tab title="C++" %}

```cpp
licenseManager->clearLocalStorage();
```

{% endtab %}

{% tab title="C#" %}

```csharp
licenseManager.ClearLocalStorage();
```

{% endtab %}

{% tab title="Swift" %}

```swift
try manager.clearLocalStorage()
```

{% endtab %}

{% tab title="Python" %}

```python
manager.clear_local_storage()
```

{% endtab %}
{% endtabs %}

By performing these steps, you can efficiently deactivate the license and clear any associated local data.

### Code Sample

#### Loading Local License and Creating Offline Activation Request File

First we load the local license, using `getCurrentLicense` whilst checking for any local license errors:

{% tabs %}
{% tab title="C++" %}

```cpp
try
{
    license = licenseManager->getCurrentLicense();
}
catch ( LocalLicenseException )
{ 
    //Cannot read the local license or the local license file is corrupt
}
auto filePath = licenseManager->createOfflineActivationFile(licenseId);
//Request file created at filePath
//Upload request file to the LicenseSpring portal to get response file.
```

{% endtab %}

{% tab title="C#" %}

```csharp
try
{
    license = licenseManager.CurrentLicense();
}
catch ( LocalLicenseException ex )
{ 
    //Cannot read the local license or the local license file is corrupt
}
string filePath = licenseManager.GetOfflineActivationFile(licenseId);
//Request file created at filePath
//Upload request file to the LicenseSpring portal to get response file.
```

{% endtab %}

{% tab title="Python" %}

```python
manager = LicenseManager(conf)
    
try:
    license = manager.load_license()

except Exception as ex:
    print(ex)
```

{% endtab %}
{% endtabs %}

Similarly to creating a deactivation request file, we can select a path for this file to be created or use the default path by leaving the path as null in `createOfflineActivationFile( licenseID, path )`.

{% hint style="info" %}
Note: If the `path` parameter is null, then the default path that `createOfflineActivationFile( licenseID )` creates the file at is: C:\Users%USERNAME%\Desktop\ls\_deactivation.req.
{% endhint %}

#### Activating Offline License Using Activation Response File

Now that we have submitted the offline activation file to the portal and downloaded the activation response file, we must activate our device locally.

After checking that our local license is not active, we run `activateLicenseOffline( path )`.

{% tabs %}
{% tab title="C++" %}

```cpp
if ( license == nullptr ) 
{
    try
    {
        license = licenseManager->activateLicenseOffline(path);
    }
    catch (SignatureMismatchException)
    {
        //Signature inside activation file is not valid.
    }
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
if(licenseManager.CurrentLicense() == null)
{
    try
    {
       License license = licenseManager.ActivateLicenseOffline(licenseFilePath);
    }
    catch (SignatureMismatchException ex)
    {
       // handle exceptions
    }
}
```

{% endtab %}

{% tab title="Python" %}

```python
manager = LicenseManager(conf)
try:
    license = manager.activate_license_offline('path_to_lic_file/ls_activation.lic')
except Exception as ex:
    print(ex)
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Note: If the `path` parameter is null, then the default path that `activateLicenseOffline` searches for the file is: C:\Users%USERNAME%\Desktop\ls\_deactivation.req.
{% endhint %}

#### Local License Check

To verify the license locally on the device without connecting to the backend, we utilize the `localCheck` function in the following manner:

{% tabs %}
{% tab title="C++" %}

```cpp
if (license != nullptr)
{
    license->localCheck();
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
if(license != null)
{
   license.LocalCheck();
}
```

{% endtab %}

{% tab title="Python" %}

```python
license.local_check()
```

{% endtab %}
{% endtabs %}

If `license` is a `nullptr`, it indicates that a local license **does not exist**. Therefore, it is crucial to first confirm the existence of a local license before proceeding with any operations related to the license.

The `localCheck` function also throws exceptions to indicate any encountered issues.

This feature proves beneficial in assessing the license's validity and detecting potential license tampering. If any irregularities are detected, the function will throw an exception.

To ensure license integrity, it is considered best practice to perform a `localCheck` (offline check) during each startup.

This practice helps confirm that the license file has not been copied from another computer and that the license is in a valid state.

A code sample that handles all potential exceptions is also provided below:

{% tabs %}
{% tab title="C++" %}

```cpp
if (license != nullptr)
{
    try
    {
        license->localCheck();
    }
    catch (LicenseStateException)
    {
        //License Invalid
        if (!license->isActive())
            //License Inactive
        if (license->isExpired())
            //License Expired
        if (!license->isEnabled())
            //License Disabled
    }
    catch (ProductMismatchException)
    {
        //License does not belong to configured product.
    }
    catch (DeviceNotLicensedException)
    {
        //License does not belong to current computer.
    }
    catch (VMIsNotAllowedException)
    {
        //Currently running on VM, when VM is not allowed.
    }
    catch (ClockTamperedException)
    {
        //Detected cheating with system clock.
    }
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
if (license != null)
{
   try
   {
      license.LocalCheck();
   }
   catch (LicenseInactiveException)
   {
      // License is inactive.
   }
   catch (LicenseDisabledException)
   {
      //License is disabled.
   }
   catch (LicenseExpiredException)
   {
      // License is expired.
   }
   catch (ProductMismatchException)
   {
      //License does not belong to configured product.
   }
   catch (DeviceNotLicensedException)
   {
      //License does not belong to current computer.
   }
   catch (VMIsNotAllowedException)
   {
       //Currently running on VM, when VM is not allowed.
   }
   catch (ClockTamperedException)
   {
      //Detected cheating with system clock.
   }
}
```

{% endtab %}

{% tab title="Python" %}

```python
try:
    license.local_check()
except Exception as ex:
    print(ex)
```

{% endtab %}
{% endtabs %}

#### Offline Update

To update licenses offline, we use the `updateOffline( path )` method as shown below, where `license` is a `license` object.

{% tabs %}
{% tab title="C++" %}

```cpp
if (license->updateOffline(path))
{
    //License succesfully updated.
}
else
    //License could not be updated.
```

{% endtab %}

{% tab title="C#" %}

```csharp
if (license.UpdateOffline(path))
{
  //License succesfully updated.
}
else
  //License could not be updated.
```

{% endtab %}

{% tab title="Python" %}

```python
if license.update_offline('offline_files/license_refresh.lic',False):
    pass
else:
    pass
```

{% endtab %}
{% endtabs %}

Just like the previous code sample, we can also first check whether `license` is a `nullptr` to confirm the local license exists before we attempt to update it.

We can also handle the specific exceptions thrown by `updateOffline( path )` as shown below:

{% tabs %}
{% tab title="C++" %}

```cpp
if (license == nullptr) 
    //Cannot refresh an inactive license.
else
{
    try 
    {
        if (license->updateOffline(path))
        {
            //License succesfully updated.
        }
        else
        {
            //License could not be updated.
        }
    }
    catch (SignatureMismatchException)
    {
        //Device does not match update file's device signature.
    }
    catch (LocalLicenseException)
    {
        //Local license file cannot be read, may be corrupted.
    }
    catch (ProductMismatchException)
    {
        //License product code doesn't match configuration product code.
    }
    catch (DeviceNotLicensedException)
    {
        //License refresh file does not correspond to current device.
    }
} 
```

{% endtab %}

{% tab title="C#" %}

```csharp
if (license != nullptr)
{
  try 
  {
    if (license.UpdateOffline(path))
    {
      //License succesfully updated.
    }
    else
    {
      //License could not be updated.
    }
  }
  catch (FileNotFoundException)
  {
    //If file cannot be found.
  }
  catch (InvalidDataException)
  {
    //If file content is empty or invalid.
  }
  catch (SignatureMismatchException)
  {
    //In case signature in the file is not valid or missing.
  }
} 
```

{% endtab %}

{% tab title="Python" %}

```python
try:
    if license.update_offline('offline_files/license_refresh.lic',False):
        pass
        # license updated!
    else:
        pass
except Exception as ex:
    print(ex)
```

{% endtab %}
{% endtabs %}

#### License Deactivation

To deactivate a license offline, first ensure that the license is currently active. Then an offline deactivation request file will be created at the path defined in the parameter of `deactivateOffline( path )`.

{% hint style="info" %}
Note: If the `path` parameter is null, then the default path that `deactivateOffline()` creates the file at is: C:\Users%USERNAME%\Desktop\ls\_deactivation.req.
{% endhint %}

After creating the offline deactivation request file, it is vital that the local license file is deleted, using `clearLocalStorage`.

A full code sample is as follows:

{% tabs %}
{% tab title="C++" %}

```cpp
if (license != nullptr && license->isActive())
{
    try
    {
        auto filePath = license->deactivateOffline(path);
    }
    catch (LocalLicenseException)
    {
        //Local license file cannot be read, may be corrupted.
    }
    licenseManager->clearLocalStorage();
    //To finish deactivation process, upload deactivation request file to the LicenseSpring portal.
}
else
    //License is already deactivated.
```

{% endtab %}

{% tab title="C#" %}

```csharp
if (license != null && license.Status().IsActive())
{
    try
    {
        string filePath = license.DeactivateOffline(path);
    }
    catch (LocalLicenseException)
    {
        //Local license file cannot be read, may be corrupted.
    }
    licenseManager.ClearLocalStorage();
    //To finish deactivation process, upload deactivation request file to the LicenseSpring portal.
}
else
    //License is already deactivated.
```

{% endtab %}

{% tab title="Python" %}

```python
if license.license_active():
    try:
        path = license.deactivate_offline("path")
    except Exception as ex:
        print(ex)
    manager.clear_local_storage()
```

{% endtab %}
{% endtabs %}

### Automating Offline Licensing

Automating the offline activation process becomes achievable by utilizing the following API endpoints:

* [**Activate License (Offline Method)**](/license-api/license-activation-and-deactivation/activate-license-offline-method.md)
* [**Deactivate License (Offline Method)**](/license-api/license-activation-and-deactivation/deactivate-license-offline-method.md)

These endpoints provide developers with the necessary tools to seamlessly manage offline license activation, enabling users to access and activate licenses without relying on a constant internet connection.

{% hint style="info" %}
Note: If you want to use these API endpoints directly, instead of using the SDK, please [**contact us**](https://licensespring.zendesk.com/hc/en-us/requests/new) for additional instructions.
{% endhint %}

### Troubleshooting

#### Exceptions Related to Offline Activation

Exceptions that might occur when performing offline license activation:

* SignatureMismatchException: Signature inside activation file is not valid.
* LocalLicenseException: Invalid activation file provided.
* ProductMismatchException: License product code does not correspond to configuration product code.
* DeviceNotLicensedException: License file does not belong to the current device.

#### Why is the License File Not Generated After Uploading the Request File?

The offline license activation process may encounter failure due to any of the following reasons:

1. Total activations on the account have reached the maximum limit.
2. The license is expired or disabled.
3. Incorrect license credentials provided during activation.
4. Incorrect product configuration, such as using a shared key, API key, or product code.
5. The license is associated with an account using the free tier.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.licensespring.com/sdks/tutorials/licensing-scenarios/offline-licensing.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
