3rd party library support🔗
Modelon Impact strives to be an open platform that makes it easy to integrate with other systems and vendors. As part of this vision we also want to offer a simple and flexible way for 3rd party library vendors to make their product available in the Impact ecosystem.
This article serves as an developers guide on how to integrate licensing to an encrypted Modelica library.
Content delivery🔗
Modelon Impact supports 3rd party libraries distributed as .mol files. A Modelon Impact user can import a library via Workspace Management app or receive it as a part of exported or published Workspace prepared by a different user. Modelon Impact currently does not have a centralized service collecting all available libraries, therefore initial configuration requires upload of the library from a local drive.
Licensing🔗
semla-license-manager-impact-example is an example of how to implement a licensing solution.
Modelon Impact facilitates licensing solution implementation by providing:
- A mechanism for fetching a cryptographically verifiable data structure that includes the user's identity.
- Enabling remote server connections from the library vendor executable to the vendors servers (rate limits may be applied).
Modelon Impact does not facilitate collection of execution node specific hardware information. Any executable code related to license checking must be distributed as a part of the library package and should not require pre-installation of a license checking run-time. Furthermore, license validation code is executed on behalf of the user and can't utilize functions that require elevated (root) permissions.

Use cases🔗
There are a number of use cases that can be implemented using this mechanism.
| Use case | Supported | Comment |
|---|---|---|
| Modeling & execution inside Modelon Impact | Yes | Full support if implementing Modelon licensing mechanism |
| FMU export | No | Mechanism is not available outside of Impact |
| FMU import | Partial | Yes, if it implements Modelon licensing mechanism |
Identity validation🔗
The verification is done using public-private key cryptography. Modelon provides the identity as a JSON Web Token (JWT) and the corresponding validation keys are published on Modelon.cloud. The JWT header will indicate an appropriate JSON Web Key (JWK) resource that can be used for validation. See appendix for example.
Special consideration must be taken for customer managed systems that are deployed in an air gapped environment. For such systems it is recommended to download a local reference to the JWK resources as they can not be fetched from Modelon servers at runtime.
Identity injection🔗
Modelon reserves the right to provide the JWT token using one of two methods:
- Directly available via environment variables or,
- Providing a URL and credentials via environment variables for fetching the JWT over HTTP.
Note: Direct injection should take precedence if both methods are available.
The implementation is expected to inspect the following environment variables for configurations passed to the library.
| Variable | Description | Example value |
|---|---|---|
| MODELON_LICENSE_USER_JWT | Complete JWT object. This configuration takes precedence over URL. | eyJhbGciOiJIUzI1NiIsInR5cCI6I... |
| MODELON_LICENSE_USER_JWT_URL | URL where the implementation is expected to fetch the current JWT. Supported protocols are file, http and https. | /api/license/entitlement |
| MODELON_LICENSE_USER_JWT_URL_CONNECTION_TIMEOUT | Timeout for fetching the current JWT. The unit is seconds. The recommended default if not set is 10 seconds. Typical response times in production is 10 milliseconds. This is used to support longer response times in test environments. | 10 |
| MODELON_LICENSE_HTTP_CREDENTIAL_HEADER | The HTTP header where credentials are expected to be sent | impact-authentication |
| MODELON_LICENSE_HTTP_CREDENTIAL_VALUE | Credential value expected in the appropriate header field | \<system generated credential> |
The implementation may need to fetch a new JWT if it expires. Detailed on expiration data can be found in the specification. The platform takes liberty to adjust the lifetime of the JWT as it sees fit, and the implementation is expected to handle expiration and renewal gracefully without adverse effects to the user.
The implementation should inspect HTTP status codes and payload for further information in case of error on the platform side. The platform will return error codes and descriptions in JSON format (se reference) if errors occur. The endpoints return HTTP status code 200 for successful operations but the implementation is expected to handle 4xx and 5xx errors gracefully.
Lifecycle hooks🔗
There are a number of places where one can choose to hook in licensing:
| Lifecycle hook | Supported | Mechanism |
|---|---|---|
| Loaded and made available to the user-interface | Yes | SEMLA |
| Compilation | Yes | SEMLA |
| Runtime | Yes | ModelicaReference.Classes.ExternalObject |
If the library is packaged with SEMLA there are convenient hooks for adding license validation when the library is loaded ether for compilation or for editing of other models.
A Runtime license check can be implemented in a constructor of an ModelicaReference.Classes.ExternalObject. The Modelica library must be designed so that any use of the licensed library components require that external object to be present in the model. This is typically achieved by means of inner/outer Modelica constructs.
Algorithm🔗
A reasonable logic implementation is described below. The vendor may choose to implement parts of this logic in a external license server instead of inside the library if applicable.

Example🔗
This is a example implementation of the algorithm used to validate user identity. It is expected that some of this logic might be written in C and included in the library but parts may reside in a external licensing server if so desired.
```python
!/usr/bin/env python3🔗
import os, requests, jwt, json
provided by Modelon to retrieve its public keys🔗
JWKS_URL = "https://wellknown.modelon.cloud/.well-known/jwks.json" entitlement_jwt = os.getenv("MODELON_LICENSE_USER_JWT")
no entitlement provided as an env var, so retrieving it🔗
if not entitlement_jwt: entitlement_url = os.getenv("MODELON_LICENSE_USER_JWT_URL") timeout = float(os.getenv("MODELON_LICENSE_USER_JWT_URL_CONNECTION_TIMEOUT","10")) credentials_header = os.getenv("MODELON_LICENSE_HTTP_CREDENTIAL_HEADER") credentials_value = os.getenv("MODELON_LICENSE_HTTP_CREDENTIAL_VALUE") if not all([entitlement_url, credentials_header, credentials_value]): raise Exception( "Configuration error: missing environment variables for entitlement retrieval" ) response = requests.get( entitlement_url, headers={credentials_header: credentials_value}, timeout=timeout, ) if response.status_code == 409: raise Exception(f"Modelon license invalid: {response['error']['message']}") response.raise_for_status() entitlement_jwt = response.json()["data"]["entitlement"] jwt_headers = jwt.get_unverified_header(entitlement_jwt) kid = jwt_headers.get("kid") if not kid: raise Exception("KID is not present in the JWT header") response = requests.get(JWKS_URL) response.raise_for_status() jwk_keys = response.json()["keys"]
JWK with specific KID won't change, so can be cached🔗
jwk = next((key for key in jwk_keys if kid == key["kid"]), None) if not jwk: raise Exception(f"JWK for KID {kid} does not exist") public_key = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(jwk))
Verify JWT validity🔗
decoded_jwt = jwt.decode(entitlement_jwt, key=public_key, algorithms=jwt_headers["alg"])
Now we have user information to check🔗
print(f"User ID: {decoded_jwt['user']['id']}") print(f"Username: {decoded_jwt['user']['username']}") print(f"Tenant ID: {decoded_jwt['tenant_id']}") ```
Appendix:🔗
JWS headers🔗
json
{
"alg": "RS256",
"kid": "my-key-id",
"typ": "JWT"
}
JWS payload🔗
```json { "features": [ "OPTIMICA_SS", "OPTIMICA_BASE", "IMPACT_BASE", ], "user": { "id": "784d-sdfkk-sdfhh", "username": "the user"
}, "tenantId": "my-company", "format_version": "1.0.0", "exp": 1675452359, "nbf": 1675430699, "iat": 1675430759 } ```
JWK payload🔗
json
{
"kty": "RSA",
"n": "0vx7ag...",
"e": "AQAB",
"kid":"my-key-id"
}
Error payload🔗
json
{
"error": {
"code": 15001,
"message": "The license for this IMPACT installation has expired."
}
}