Skip to Content
New release 11.5 available 🎉
LicensingLicense Activation in MAUI

License Activation in MAUI

In this guide, we’ll explore how to implement license activation in a .NET MAUI application using Babel Licensing. This approach allows you to securely bind licenses to specific devices, ensuring your software is only used according to your licensing terms. By following this implementation pattern, you’ll create a robust licensing system that works consistently across multiple platforms while leveraging platform-specific device information.

Prerequisites

Before you begin, ensure you have the following:

  • Visual Studio 2022 with .NET MAUI workload installed
  • Babel.Licensing NuGet package (version 11.0.0 or later)
  • A Babel Licensing Service instance running (either locally or on a server)
  • Basic understanding of .NET MAUI application development
  • A Babel Licensing Server or Data Center edition license

Sample Project

You can download the complete sample project from GitHub using the following command:

git clone https://github.com/babelfornet/maui-licensed-app.git

This repository contains a fully implemented MAUI application with Babel Licensing integration across all supported platforms.

Licensed MAUI Application

Why License Activation in MAUI?

MAUI (Multi-platform App UI) allows developers to build cross-platform applications from a single codebase. However, when implementing licensing, you need to consider the unique characteristics of each platform. License activation is particularly well-suited for MAUI applications because:

  1. Device-Specific Binding: Licenses are linked to the specific device where the application runs, preventing unauthorized use on multiple devices.
  2. Offline Operation: Once activated, applications can operate without continuous server connection, making it ideal for mobile applications.
  3. Cross-Platform Compatibility: The licensing model works consistently across different platforms (Android, iOS, Windows, MacCatalyst).

Setting Up the Project Structure

The MauiLicApp example demonstrates a well-organized approach to implementing Babel Licensing in a MAUI application:

MauiLicensedApp/ ├── Licensing/ # Core licensing implementation │ ├── BabelLicensingExtensions.cs │ ├── ISystemInformation.cs │ └── LicensingService.cs ├── Platforms/ # Platform-specific implementations │ ├── Android/ │ ├── iOS/ │ ├── MacCatalyst/ │ └── Windows/ ├── Reporting/ # Exception reporting functionality │ └── BabelReportingExtensions.cs └── MauiProgram.cs # Application initialization

This structure separates licensing concerns from the rest of your application while maintaining platform-specific implementations where needed.

Implementing the SystemInformation Base Class

The core of our cross-platform licensing system is the SystemInformationBase abstract class, which defines a platform-independent interface for gathering system information required for license activation:

public abstract class SystemInformationBase : ISystemInformation { public abstract string DeviceId { get; } public abstract string DeviceManufacturer { get; } public abstract string DeviceModel { get; } public abstract string OsVersion { get; } // Additional system properties... }

This class implements Babel Licensing’s ISystemInformation interface, ensuring compatibility with the licensing system while providing a consistent abstraction across platforms.

Platform Specific System Information

In a MAUI application, platform-specific implementations of SystemInformationBase are crucial for several key reasons:

  1. Hardware Access Differences: Each platform has unique APIs and methods for accessing hardware information. For example, Android uses Build.SERIAL or Settings.Secure.ANDROID_ID, while iOS uses UIDevice.CurrentDevice.IdentifierForVendor.
  2. Security Restrictions: Mobile platforms have different security models that restrict access to certain system information. Platform-specific implementations can navigate these restrictions appropriately.
  3. Unique Identifiers: The approach to generate reliable, persistent device identifiers varies by platform. What works on Windows won’t work on iOS or Android.
  4. Feature Availability: Some hardware information available on one platform may not be accessible on another, requiring platform-specific alternatives.

Here’s how it works in practice for different platforms:

Android Implementation

The Android implementation leverages Android-specific APIs to gather system information:

public class SystemInformation : SystemInformationBase { public override string DeviceId { get { try { return Settings.Secure.GetString(_context.ContentResolver, Settings.Secure.AndroidId) ?? string.Empty; } catch (Exception) { return string.Empty; } } } public override string DeviceManufacturer => Build.Manufacturer ?? string.Empty; // Other implementations... }

iOS Implementation

The iOS implementation uses iOS-specific APIs:

public class SystemInformation : SystemInformationBase { public override string DeviceId { get { try { return UIDevice.CurrentDevice?.IdentifierForVendor?.AsString() ?? string.Empty; } catch (Exception) { return string.Empty; } } } public override string DeviceManufacturer => "Apple"; // Other implementations... }

Windows Implementation

The Windows implementation uses Windows Management Instrumentation (WMI) to collect system information:

public class SystemInformation : SystemInformationBase { public override string SystemUuid { get { try { using var searcher = new ManagementObjectSearcher( "SELECT UUID FROM Win32_ComputerSystemProduct"); foreach (var obj in searcher.Get()) { return obj["UUID"]?.ToString() ?? string.Empty; } return string.Empty; } catch (Exception) { return string.Empty; } } } // Other implementations... }

Registering Platform-Specific Implementations

Each platform needs to register its specific SystemInformation implementation during application startup. This is typically done in the platform-specific entry point:

For Android (MainApplication.cs):

[Application] public class MainApplication : MauiApplication { public MainApplication(IntPtr handle, JniHandleOwnership ownership) : base(handle, ownership) { // Register the SystemInformation service for licensing LicenseServices.Current.AddService(typeof(ISystemInformation), new SystemInformation()); } protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); }

For iOS (Program.cs):

public class Program { static void Main(string[] args) { // Register the SystemInformation service for licensing LicenseServices.Current.AddService(typeof(ISystemInformation), new SystemInformation()); UIApplication.Main(args, null, typeof(AppDelegate)); } }

For Windows (App.xaml.cs):

public partial class App : MauiWinUIApplication { protected override void OnLaunched(LaunchActivatedEventArgs args) { // Register the SystemInformation service for licensing LicenseServices.Current.AddService(typeof(ISystemInformation), new SystemInformation()); base.OnLaunched(args); } }

Configuring Babel Licensing in MAUI

With platform-specific implementations in place, we need to configure Babel Licensing for the MAUI application. This is done using extension methods that integrate with MAUI’s dependency injection system:

public static MauiAppBuilder UseBabelLicensing( this MauiAppBuilder builder, Action<BabelLicensingConfiguration>? configureClient = null) { // Register platform-specific system information service builder.Services.AddSingleton<SystemInformationBase>(serviceProvider => { return LicenseServices.Current.GetService(typeof(ISystemInformation)) as SystemInformationBase ?? throw new InvalidOperationException("SystemInformation service not registered."); }); // Create the BabelLicensing configuration var config = new BabelLicensingConfiguration { // Set the default client ID ClientId = AppInfo.Current.Name, // Set the machine ID using system UUID and name MachineId = new HardwareId( HardwareComponents.SystemUuid | HardwareComponents.SystemName).ToMachineKey(), // Set the signature provider with public key for license verification SignatureProvider = RSASignature.FromKeys("PUBLIC_KEY_HERE") }; // Apply custom configuration configureClient?.Invoke(config); // Register services builder.Services.AddSingleton<BabelLicensing>(/* ... */); builder.Services.AddSingleton<BabelServiceLicenseProvider>(/* ... */); return builder; }

This extension method is then used in MauiProgram.cs to apply the licensing configuration:

public static MauiApp CreateMauiApp() { // Configure BabelLicensing service URL string babelHttpServiceUrl = "https://your-license-server:5455"; var builder = MauiApp.CreateBuilder(); builder .UseMauiApp<App>() .UseBabelLicensing(config => { config.ServiceUrl = babelHttpServiceUrl; }) .UseBabelReporting(config => { config.ServiceUrl = babelHttpServiceUrl; }) .ConfigureFonts(fonts => { /* ... */ }); // Register the LicensingService for dependency injection builder.Services.AddSingleton<LicensingService>(); return builder.Build(); }

Creating the LicensingService

The LicensingService class provides a simplified interface for licensing operations throughout your application:

public class LicensingService { private readonly BabelLicensing _client; private readonly BabelServiceLicenseProvider _licenseProvider; public LicensingService(BabelLicensing client, BabelServiceLicenseProvider licenseProvider) { _client = client; _licenseProvider = licenseProvider; } public BabelLicensing Client => _client; public bool IsLicensed => BabelLicenseManager.IsLicensed(typeof(BabelLicensingExtensions)); public string? UserKey => _licenseProvider.UserKey; public async Task ActivateLicenseAsync(string userKey) { await _client.ActivateLicenseAsync(userKey, typeof(BabelLicensingExtensions)); } public async Task DeactivateLicenseAsync() { if (!string.IsNullOrEmpty(UserKey)) { await _client.DeactivateLicenseAsync(UserKey); } } public async Task<ILicense> ValidateLicenseAsync() { return await BabelLicenseManager.ValidateAsync(typeof(BabelLicensingExtensions)); } }

This service encapsulates the Babel Licensing API behind a simple, application-specific interface. It can be injected into any page or viewmodel that needs licensing functionality.

Implementing License Activation in the UI

With the licensing infrastructure in place, you can now implement license activation in your UI. Here’s an example from MainPage.xaml.cs:

private async void OnActivateClicked(object sender, EventArgs e) { try { if (_licensingService.IsLicensed) { // If licensed, validate the license var license = await _licensingService.ValidateLicenseAsync(); ActivateBtn.Text = $"License valid: {license.Id}"; // Show system information await DisplayAlert("System Info", $"Device: {_systemInfo.DeviceManufacturer} {_systemInfo.DeviceModel}\n" + $"OS: {_systemInfo.OperatingSystem} {_systemInfo.OsVersion}\n" + $"System: {_systemInfo.SystemName}\n" + $"UUID: {_systemInfo.SystemUuid}", "OK"); } else { // If not licensed, prompt for activation string userKey = await DisplayPromptAsync("License Activation", "Enter your license key:", accept: "Activate", cancel: "Cancel"); if (!string.IsNullOrEmpty(userKey)) { try { await _licensingService.ActivateLicenseAsync(userKey); await DisplayAlert("Success", "License activated successfully!", "OK"); UpdateLicenseStatus(); } catch (Exception ex) { await DisplayAlert("Activation Failed", $"Could not activate license: {ex.Message}", "OK"); } } } } catch (Exception ex) { await DisplayAlert("Error", $"License operation failed: {ex.Message}", "OK"); } }

This implementation allows users to input a license key and activate the application. Once activated, the license is bound to the device and can be validated in subsequent application launches.

Adding Exception Reporting

Babel Licensing also supports exception reporting, which can be integrated into your MAUI application using the BabelReportingExtensions class:

public static void ReportUnhandledException(this Exception ex) { try { var task = Task.Run(async () => { try { await ex.SendReportAsync(); } catch (Exception innerEx) { Debug.WriteLine($"Failed to report exception: {innerEx.Message}"); } }); task.Wait(TimeSpan.FromSeconds(5)); } catch (Exception reportEx) { Debug.WriteLine($"Failed to report exception: {reportEx.Message}"); } }

This functionality can be registered in the application’s unhandled exception handlers:

// In App.xaml.cs private void SetupExceptionHandling() { AppDomain.CurrentDomain.UnhandledException += (sender, args) => { var ex = args.ExceptionObject as Exception; ex?.ReportUnhandledException(); }; }

Exception Report

Best Practices for MAUI License Activation

To ensure a smooth licensing experience in your MAUI application:

  1. Error Handling: Implement comprehensive error handling for network and licensing operations, especially for mobile applications that may have intermittent connectivity.
  2. Graceful Degradation: When license validation fails due to connectivity issues, provide a grace period or limited functionality rather than completely blocking the application.
  3. UI Feedback: Provide clear feedback about licensing status in the UI, making it obvious when a license is active, expired, or invalid.
  4. Testing: Test your licensing implementation on all target platforms to ensure consistent behavior across devices.

Conclusion

Implementing license activation in a .NET MAUI application with Babel Licensing requires a strategic approach to handle platform-specific differences while maintaining a consistent licensing experience. By creating a platform-independent abstraction with platform-specific implementations, you can reliably gather the system information needed for secure license activation.

The MauiLicApp example demonstrates a comprehensive implementation that works across Android, iOS, Windows, and MacCatalyst platforms. By following this pattern and the best practices outlined in this guide, you can effectively protect your MAUI applications with a robust licensing system.

This approach ensures that your applications are only used according to your licensing terms while providing a seamless experience for legitimate users across all supported platforms.

Last updated on