Login-Based Usage Rights Validation

Use the login validation endpoint when your application authenticates users with their Distancer.ai account. The user’s identity is derived from the Bearer token, and the API returns the usage rights associated with that account.

This endpoint works for external applications in any programming language. Once your app has obtained a Distancer.ai access token, the integration is just an authenticated HTTP POST that sends product keys and reads back usage rights.

When to Use Login Validation

Use this endpoint when:

  • Your application already has a sign-in flow for the customer
  • You want the customer’s Distancer.ai account to determine which products they can use
  • You are building a desktop app, mobile app, web app, Electron app, CLI, plugin, or other external client that can obtain a Bearer token

If your app does not authenticate the user, use Secret Key Validation instead. If you want to check whether a newer release is available, use Product Version Check separately from license validation.

Endpoint

POST api/v1/login

Authentication: Required — include a valid Bearer token in the Authorization header.

Generic Integration Flow

  1. Authenticate the user with your preferred client library for your platform.
  2. Obtain a Distancer.ai Bearer token.
  3. Send the product keys your app wants to validate to api/v1/login.
  4. Read the returned UsageRights records.
  5. Enable the correct product edition or features inside your application.

The Distancer.ai API returns rights data. Your application is responsible for interpreting that data and deciding what behavior to unlock.

Suggested Authentication Libraries

The .NET examples below use Microsoft.Identity.Client because it is the MSAL package for .NET. If you are building in another language, use the equivalent client library for your platform, or any OAuth 2.0 / OpenID Connect client that supports the authority and scope values provided for your Distancer.ai app registration.

Common Replacements for Microsoft.Identity.Client

Platform Suggested Library Notes
JavaScript / TypeScript in browser or Electron renderer @azure/msal-browser Good for interactive sign-in with authorization code flow and PKCE.
Node.js, Electron main process, or CLI @azure/msal-node Supports interactive, device code, and silent token flows.
Python msal Good choice for desktop apps, CLIs, or automation tools that need device code or interactive login.
Java msal4j Java equivalent of the .NET MSAL package.
Android / Kotlin com.microsoft.identity.client Native MSAL client for Android apps.
iOS / macOS / Swift MSAL Native MSAL client for Apple platforms.

If your platform does not have an MSAL package, use a standards-based OAuth 2.0 / OpenID Connect client library that supports authorization code flow with PKCE for interactive apps or device code flow for CLIs.

How to Obtain a Distancer.ai Bearer Token

No matter which language you use, the token acquisition flow is the same at a high level:

  1. Register your external app and get the Client ID, Authority, and Scope values.
  2. Start an interactive sign-in flow for the user.
  3. Request an access token for the Distancer.ai API scope.
  4. Cache the returned account/session information when your client library supports it.
  5. Attempt silent token refresh on later launches before falling back to interactive sign-in.

For desktop and mobile apps, the usual choice is authorization code flow with PKCE. For CLIs and headless tools, device code flow is often the simplest option.

JavaScript / TypeScript Guidance

  • Browser or Electron renderer apps: use @azure/msal-browser with authorization code flow and PKCE.
  • Node.js or Electron main process apps: use @azure/msal-node.
  • For interactive apps, open the user’s browser and complete the sign-in flow.
  • For CLIs, prefer device code flow when your runtime and UX allow it.

Example package install commands:

npm install @azure/msal-browser
npm install @azure/msal-node

Python Guidance

  • Use the msal package to acquire tokens.
  • For desktop tools and CLIs, device code flow is usually the most practical starting point.
  • For GUI applications, use an interactive browser-based login flow when available.
  • Cache the account or token information according to the library guidance so you can attempt silent refresh later.

Example package install command:

pip install msal

Request

Headers

Header Value
Authorization Bearer <access_token>
Content-Type application/json

Body — ProductRightsAuthenticatedRequest

Property Type Required Constraints Description
ProductKeys string[] Yes Max 10 items, each up to 150 characters Array of product keys to validate. Each key uniquely identifies a product on the Distancer.ai platform.
{
  "ProductKeys": ["your-product-key-here"]
}

Product Key is the unique identifier assigned to your product when you create an App Licensing Branch. It is hardcoded into your application at build time.

Raw HTTP Example

The request format is the same regardless of language. If you already have an access token, any HTTP client can call the endpoint:

curl -X POST "https://appstore.distancer.ai/api/v1/login" \
    -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
        "ProductKeys": ["your-product-key-here"]
    }'

Works with Any Stack

You can build this integration from any external application stack, including:

  • .NET desktop, web, MAUI, WPF, WinForms, or console apps
  • JavaScript or TypeScript applications using Electron, Node.js, or a browser-assisted login flow
  • Python desktop apps, automation tools, or CLIs
  • Java, Go, Rust, Swift, Kotlin, or any environment that can send authenticated HTTPS requests

The only Distancer-specific requirement is a valid user access token. Token acquisition depends on the authentication libraries you choose for your platform.

Response — ProductRightsResponse

Property Type Description
UsageRights UsageRightsRecord[] List of usage rights matching the authenticated user and requested product keys. Empty array if no rights found.

UsageRightsRecord

Property Type Description
ProductKey string? The product key this record applies to.
LicensePayload string? Optional payload data your application can interpret (for example, tier metadata or other app-specific context).
TestLicense bool true if this is a sandbox/test license, false for production licenses.

Example Response

{
  "UsageRights": [
    {
      "ProductKey": "your-product-key",
      "LicensePayload": "pro-tier",
      "TestLicense": false
    }
  ]
}

If the user has no active license for the requested product keys, the response contains an empty array:

{
  "UsageRights": []
}

Interpreting the Response in Your App

The API does not directly switch features on or off inside your application. Your code should inspect the returned rights and decide what to enable.

  • Use ProductKey to identify which product or variation the customer owns.
  • Use LicensePayload only as optional metadata that your application understands.
  • For materially different editions or feature sets, model them as separate products in your Business Project.
  • The recommended structure is a root product with child variation products for each app edition or feature combination.

This approach keeps product modeling explicit and avoids relying on Distancer.ai to manage your in-app feature flags.

JavaScript Example

This example assumes your app has already obtained an access token through your chosen auth flow. For JavaScript and TypeScript apps, common choices are @azure/msal-browser and @azure/msal-node.

async function validateByLogin(accessToken, productKeys) {
    const response = await fetch("https://appstore.distancer.ai/api/v1/login", {
        method: "POST",
        headers: {
            Authorization: `Bearer ${accessToken}`,
            "Content-Type": "application/json"
        },
        body: JSON.stringify({ ProductKeys: productKeys })
    });

    if (!response.ok) {
        throw new Error(`Validation failed: ${response.status}`);
    }

    return await response.json();
}

const rights = await validateByLogin("your-access-token", ["your-product-key"]);

if (rights.UsageRights.length > 0) {
    const license = rights.UsageRights[0];
    console.log("Licensed product:", license.ProductKey);
    console.log("Optional payload:", license.LicensePayload);
}

Python Example

This example focuses only on calling the API after your app has acquired an access token. For Python apps, msal is the usual replacement for the .NET Microsoft.Identity.Client package.

import requests


def validate_by_login(access_token: str, product_keys: list[str]) -> dict:
    response = requests.post(
        "https://appstore.distancer.ai/api/v1/login",
        headers={
            "Authorization": f"Bearer {access_token}",
            "Content-Type": "application/json",
        },
        json={"ProductKeys": product_keys},
        timeout=30,
    )
    response.raise_for_status()
    return response.json()


rights = validate_by_login("your-access-token", ["your-product-key"])

if rights["UsageRights"]:
    license_record = rights["UsageRights"][0]
    print("Licensed product:", license_record.get("ProductKey"))
    print("Optional payload:", license_record.get("LicensePayload"))
else:
    print("No active license found.")

.NET Integration Example

Install Dependencies

Your application needs an HTTP client, JSON serialization, and the Microsoft Authentication Library (MSAL) to obtain access tokens. Install the MSAL package:

dotnet add package Microsoft.Identity.Client

No additional Distancer-specific NuGet packages are required.

Define the Models

public class ProductRightsAuthenticatedRequest
{
    public string[] ProductKeys { get; set; } = [];
}

public class ProductRightsResponse
{
    public List<UsageRightsRecord> UsageRights { get; set; } = [];
}

public class UsageRightsRecord
{
    public string? ProductKey { get; set; }
    public string? LicensePayload { get; set; }
    public bool TestLicense { get; set; }
}

Obtain an Access Token

Before calling the Distancer.ai API, your application must authenticate the user and obtain a Bearer token. Use the Microsoft.Identity.Client library (MSAL) to acquire tokens through Distancer.ai’s identity provider.

Register your application in the Distancer.ai developer portal to get your Client ID, Authority, and Scope values.

using Microsoft.Identity.Client;

public class DistancerAuthClient
{
    private readonly IPublicClientApplication _app;
    private readonly string _scope;

    public DistancerAuthClient(string clientId, string authority, string scope)
    {
        _scope = scope;
        _app = PublicClientApplicationBuilder
            .Create(clientId)
            .WithAuthority(authority)
            .WithDefaultRedirectUri()
            .Build();
    }

    /// <summary>
    /// Acquires a token interactively (first login) or silently (returning user).
    /// </summary>
    public async Task<AuthenticationResult?> AcquireTokenAsync(string? userId = null)
    {
        if (string.IsNullOrWhiteSpace(userId))
        {
            // First-time login — opens a browser window for the user to sign in
            return await _app.AcquireTokenInteractive([_scope]).ExecuteAsync();
        }

        var accounts = await _app.GetAccountsAsync();
        var account = accounts.FirstOrDefault(
            a => a.HomeAccountId?.Identifier == userId);

        try
        {
            // Returning user — attempt silent token refresh
            return await _app.AcquireTokenSilent([_scope], account).ExecuteAsync();
        }
        catch (MsalUiRequiredException)
        {
            // Silent refresh failed (e.g., token expired) — fall back to interactive
            return await _app.AcquireTokenInteractive([_scope]).ExecuteAsync();
        }
    }
}
Parameter Description
clientId Your application’s Client ID from the Distancer.ai developer portal.
authority The identity authority URL provided during app registration.
scope The API scope your application requests (provided during app registration).
userId The HomeAccountId.Identifier from a previous login. Pass null for first-time users.

The method returns an AuthenticationResult containing the AccessToken you pass to the Distancer.ai API, along with ExpiresOn (token expiry) and Account (user identity for future silent logins).

Call the Endpoint

using System.Net.Http.Headers;
using System.Net.Http.Json;

public class DistancerLicenseClient
{
    private readonly HttpClient _httpClient;
    private const string BaseUrl = "https://appstore.distancer.ai/";

    public DistancerLicenseClient(HttpClient httpClient)
    {
        _httpClient = httpClient;
        _httpClient.BaseAddress = new Uri(BaseUrl);
    }

    public async Task<ProductRightsResponse?> ValidateByLoginAsync(
        string accessToken, string[] productKeys, CancellationToken ct = default)
    {
        _httpClient.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Bearer", accessToken);

        var request = new ProductRightsAuthenticatedRequest
        {
            ProductKeys = productKeys
        };

        var response = await _httpClient.PostAsJsonAsync("api/v1/login", request, ct);
        response.EnsureSuccessStatusCode();

        return await response.Content.ReadFromJsonAsync<ProductRightsResponse>(ct);
    }
}

Use in Your Application

Combine token acquisition with license validation for a complete login flow:

// 1. Authenticate the user and obtain an access token
var auth = new DistancerAuthClient(
    clientId: "your-client-id",
    authority: "your-authority-url",
    scope: "your-api-scope"
);

// Pass null for first login, or a stored userId for returning users
var authResult = await auth.AcquireTokenAsync(userId: null);
if (string.IsNullOrWhiteSpace(authResult?.AccessToken))
{
    Console.WriteLine("Login failed.");
    return;
}

// 2. Validate the license with the Distancer.ai App Store
var licenseClient = new DistancerLicenseClient(new HttpClient());
var rights = await licenseClient.ValidateByLoginAsync(
    accessToken: authResult.AccessToken,
    productKeys: ["your-product-key"]
);

if (rights?.UsageRights?.Any(r => !string.IsNullOrWhiteSpace(r.ProductKey)) == true)
{
    var license = rights.UsageRights[0];
    var isTest = license.TestLicense ? " (test)" : "";
    Console.WriteLine($"License active{isTest} — Payload: {license.LicensePayload}");

    // Store the userId for silent token refresh on next launch
    var userId = authResult.Account?.HomeAccountId?.Identifier;
    Console.WriteLine($"User ID: {userId}");
    Console.WriteLine($"Token expires: {authResult.ExpiresOn}");
}
else
{
    Console.WriteLine("No active license found for this user.");
}

Token Lifecycle

Event Action
First launch Call AcquireTokenAsync(null) — opens interactive login.
Returning user Call AcquireTokenAsync(storedUserId) — attempts silent refresh.
Silent refresh fails MSAL automatically falls back to interactive login via MsalUiRequiredException.
Token expiry Check AuthenticationResult.ExpiresOn and re-acquire before it expires.

Store the Account.HomeAccountId.Identifier after a successful login so you can pass it on subsequent launches for silent token refresh.

Error Handling

Scenario Behavior
Invalid or expired Bearer token 401 Unauthorized
Valid token, no matching licenses 200 OK with empty UsageRights array
MsalUiRequiredException during silent refresh Fall back to interactive login
Server error null response — retry with exponential backoff

Related