Skip to content

Latest commit

Β 

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ›‘οΈ AppShield β€” Licensing & Activation System for Flutter

πŸ“– Full Usage Guide & API Reference β†’

pub.dev Flutter License: MIT Platforms

A professional, enterprise-grade licensing and activation package for Flutter applications.
Supports Windows Β· macOS Β· Linux Β· Android Β· iOS Β· Web.

appshield_comparison appshield_demo appshield_activation ---

✨ Features at a Glance

Feature Description
πŸ”‘ License Keys XXXX-XXXX-XXXX-XXXX format with auto-format & validation
🌐 Online Validation JWT-signed server validation with retry + back-off
πŸ“΄ Offline Mode Configurable grace period (default 7 days)
πŸ–₯️ Device Binding Per-platform hardware fingerprint
πŸ”’ AES-256-GCM Triple-layer local storage encryption
✍️ RSA-2048 Server signature verification on every license token
⏱️ Clock Tamper Server-time + last-known-time rollback detection
πŸ›‘οΈ Anti-Tamper Root/jailbreak, emulator, debugger, storage integrity
🎁 Trial Period Per-device trial with optional server registration
πŸ”€ Feature Flags Per-feature licensing (FeatureGuard widget)
πŸ”„ Subscriptions Perpetual Β· Subscription Β· Trial Β· Educational Β· OEM
πŸ”” Notifications In-app expiry / grace / revoke alerts via listener
🎨 Custom UI Light/dark themes, branding, RTL/LTR locale support
πŸ“± Ready UI ActivationScreen, ExpiredScreen, TrialScreen, LicenseInfoScreen

πŸ“¦ Installation

dependencies:
  app_shield: ^1.0.0

πŸš€ Quick Start

1. Initialize in main.dart

import 'package:flutter/material.dart';
import 'package:app_shield/app_shield.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await AppShield.initialize(
    config: AppShieldConfig(
      appId:      'com.mycompany.erp',
      apiBaseUrl: 'https://license.mycompany.com/api',
      apiKey:     'pk_live_your_api_key_here',
      appSecret:  'strong-random-secret-for-local-encryption',
      publicKey:  '-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----',

      enableTrial:          true,
      trialDays:            15,
      enableOfflineMode:    true,
      offlineGraceDays:     7,
      enableDeviceBinding:  true,
      maxDevicesPerLicense: 1,
      enableClockCheck:     true,
      validationIntervalHr: 24,

      theme: AppShieldTheme.light(
        primaryColor: Colors.blue,
        brandName:    'My ERP System',
      ),
    ),
  );

  runApp(const MyApp());
}

2. Wrap your root widget with AppShieldGuard

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: AppShieldGuard(
        child:            const HomePage(),
        onLicenseInvalid: () => const ActivationScreen(),
        onTrialExpired:   () => const ExpiredScreen(),
      ),
    );
  }
}

AppShieldGuard automatically:

  • Checks license on startup
  • Re-checks on every foreground resume
  • Runs periodic background validation
  • Listens to revocation / status push from server

3. Guard individual features with FeatureGuard

FeatureGuard(
  feature:  AppShieldConstants.featureAdvancedReports,
  child:    const AdvancedReportsPage(),
  fallback: const UpgradePromptWidget(), // optional
)

4. Protect routes with RouteGuard

// GoRouter
GoRoute(
  path: '/reports',
  redirect: (ctx, state) =>
      RouteGuard.checkFeature('advanced_reports', '/upgrade'),
  builder: (ctx, state) => const ReportsPage(),
)

// Navigator
Navigator.push(
  context,
  RouteGuard.protectedRoute(
    feature: 'advanced_reports',
    builder: (_) => const ReportsPage(),
  ),
);

5. Programmatic API

final shield = AppShield.instance;

// Check status
final status = await shield.checkLicense();

// Activate a key
final result = await shield.activate('ABCD-1234-EFGH-5678');
if (result.success) { /* navigate to app */ }

// Validate online
final resp = await shield.validateOnline();

// Restore license to new device
await shield.restore(licenseKey: 'ABCD-...', email: 'user@example.com');

// Start trial
await shield.startTrial();

// Check feature
if (shield.hasFeature(AppShieldConstants.featureCloudBackup)) { /* ... */ }

// Days remaining
print(shield.daysRemaining);

// Deactivate
await shield.deactivate();

🧩 Package Structure

app_shield/
β”œβ”€β”€ lib/
β”‚   β”œβ”€β”€ app_shield.dart              # Main entry point + exports
β”‚   β”œβ”€β”€ app_shield_config.dart       # AppShieldConfig class
β”‚   └── src/
β”‚       β”œβ”€β”€ core/
β”‚       β”‚   β”œβ”€β”€ license_manager.dart     # Lifecycle engine
β”‚       β”‚   β”œβ”€β”€ device_fingerprint.dart  # Per-platform fingerprinting
β”‚       β”‚   β”œβ”€β”€ encryption_service.dart  # AES-256-GCM + RSA + HMAC
β”‚       β”‚   β”œβ”€β”€ api_client.dart          # HTTP client with retry
β”‚       β”‚   β”œβ”€β”€ clock_verifier.dart      # Anti-clock-tamper
β”‚       β”‚   └── tamper_detector.dart     # Root/debug/emulator checks
β”‚       β”œβ”€β”€ models/
β”‚       β”‚   β”œβ”€β”€ license_model.dart
β”‚       β”‚   β”œβ”€β”€ device_model.dart
β”‚       β”‚   β”œβ”€β”€ feature_model.dart
β”‚       β”‚   β”œβ”€β”€ activation_result.dart
β”‚       β”‚   β”œβ”€β”€ validation_response.dart
β”‚       β”‚   β”œβ”€β”€ activity_log_entry.dart
β”‚       β”‚   └── tamper_record.dart
β”‚       β”œβ”€β”€ services/
β”‚       β”‚   β”œβ”€β”€ validation_service.dart  # Online activate/validate/restore
β”‚       β”‚   β”œβ”€β”€ storage_service.dart     # Triple-layer encrypted storage
β”‚       β”‚   β”œβ”€β”€ trial_service.dart       # Trial lifecycle
β”‚       β”‚   β”œβ”€β”€ sync_service.dart        # Telemetry + tamper reports
β”‚       β”‚   └── notification_service.dart
β”‚       β”œβ”€β”€ guards/
β”‚       β”‚   β”œβ”€β”€ app_shield_guard.dart    # Root widget gate
β”‚       β”‚   β”œβ”€β”€ feature_guard.dart       # Per-feature widget gate
β”‚       β”‚   └── route_guard.dart         # Navigator/GoRouter helpers
β”‚       β”œβ”€β”€ ui/
β”‚       β”‚   β”œβ”€β”€ screens/
β”‚       β”‚   β”‚   β”œβ”€β”€ activation_screen.dart
β”‚       β”‚   β”‚   β”œβ”€β”€ trial_screen.dart
β”‚       β”‚   β”‚   β”œβ”€β”€ expired_screen.dart
β”‚       β”‚   β”‚   β”œβ”€β”€ license_info_screen.dart
β”‚       β”‚   β”‚   β”œβ”€β”€ offline_mode_screen.dart
β”‚       β”‚   β”‚   └── tamper_detected_screen.dart
β”‚       β”‚   β”œβ”€β”€ widgets/
β”‚       β”‚   β”‚   β”œβ”€β”€ license_input.dart
β”‚       β”‚   β”‚   β”œβ”€β”€ status_card.dart
β”‚       β”‚   β”‚   β”œβ”€β”€ expiry_badge.dart
β”‚       β”‚   β”‚   β”œβ”€β”€ device_info_widget.dart
β”‚       β”‚   β”‚   └── feature_list_widget.dart
β”‚       β”‚   └── themes/
β”‚       β”‚       β”œβ”€β”€ app_shield_theme.dart
β”‚       β”‚       └── app_shield_colors.dart
β”‚       └── utils/
β”‚           β”œβ”€β”€ constants.dart
β”‚           β”œβ”€β”€ helpers.dart
β”‚           β”œβ”€β”€ logger.dart
β”‚           └── exceptions.dart
β”œβ”€β”€ example/                         # Complete demo app
β”œβ”€β”€ test/                            # Unit tests
β”œβ”€β”€ pubspec.yaml
β”œβ”€β”€ README.md
└── CHANGELOG.md

πŸ” Security Architecture

╔══════════════════════════════════════════════════════════════╗
β•‘  Layer            Technology          Purpose                β•‘
╠══════════════════════════════════════════════════════════════╣
β•‘  Local Storage    AES-256-GCM         Encrypt license data   β•‘
β•‘  Key Derivation   PBKDF2-HMAC-SHA256  Derive AES key         β•‘
β•‘  Integrity        HMAC-SHA-256        Detect tampering        β•‘
β•‘  Transport        TLS 1.3             Secure API calls        β•‘
β•‘  Server Sig.      RSA-2048            Verify license token    β•‘
β•‘  Sessions         JWT                 Validate auth tokens    β•‘
β•‘  Clock            Server NTP sync     Anti-clock-rollback     β•‘
β•‘  Storage Layers   Keychain + Prefs    Redundant persistence   β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

Triple-layer storage prevents simple file deletion:

  1. FlutterSecureStorage (OS Keychain / Android Keystore) β€” primary
  2. SharedPreferences (encrypted blob) β€” fallback
  3. HMAC tag in secure storage β€” tamper detection

πŸ“± Platform Support & Device Fingerprint Sources

Platform Fingerprint Sources
Windows Device ID (WMI), Computer Name, OS Version
macOS Hardware UUID (IOKit), hostname, OS version
Linux /etc/machine-id, hostname, CPU info
Android ANDROID_ID, Build.MANUFACTURER, Build.MODEL
iOS identifierForVendor, device model
Web User-Agent, browser info, canvas entropy

🎫 License Types

Type Description Example Use Case
perpetual One-time purchase, no expiry Desktop software
subscription Monthly / yearly recurring SaaS apps
trial Free evaluation period Lead generation
educational Discounted student license Schools, universities
enterprise Custom large-scale license Large organizations
oem Bundled / reseller license White-label products

βš™οΈ Configuration Reference

AppShieldConfig(
  // ── Required ────────────────────────────────────────────────
  appId:      'com.myapp',            // Unique app identifier
  apiBaseUrl: 'https://license...',   // License server URL
  apiKey:     'pk_live_...',          // API authentication key

  // ── Security ────────────────────────────────────────────────
  appSecret:  'strong-random-secret', // Local AES encryption key
  publicKey:  '-----BEGIN PUBLIC KEY-----...', // RSA-2048 verification

  // ── Offline ─────────────────────────────────────────────────
  enableOfflineMode:    true,
  offlineGraceDays:     7,            // Days allowed without server

  // ── Trial ────────────────────────────────────────────────────
  enableTrial:          true,
  trialDays:            15,           // Free trial length

  // ── Device ───────────────────────────────────────────────────
  enableDeviceBinding:  true,
  maxDevicesPerLicense: 1,            // Devices per license key

  // ── Validation ───────────────────────────────────────────────
  enableClockCheck:     true,         // Detect clock manipulation
  validationIntervalHr: 24,           // Hours between server checks

  // ── Tamper Detection ─────────────────────────────────────────
  enableTamperDetection:    true,
  enableRootJailbreakCheck: true,
  enableEmulatorCheck:      false,    // Enable in production
  enableDebuggerCheck:      false,    // Enable in release builds

  // ── Subscription ─────────────────────────────────────────────
  enableAutoRenewal:    false,
  buyLicenseUrl:        'https://example.com/buy',
  supportUrl:           'https://example.com/support',

  // ── UI ───────────────────────────────────────────────────────
  theme: AppShieldTheme.light(
    primaryColor: Colors.blue,
    brandName:    'My App',
    logoAsset:    'assets/logo.png',
    locale:       'en',               // 'en', 'fr', 'ar'
  ),

  // ── Telemetry ─────────────────────────────────────────────────
  enableTelemetry: false,             // Opt-in usage analytics

  // ── Logging ──────────────────────────────────────────────────
  enableLogging: true,
)

🌐 License Server API

Your backend must implement these endpoints:

Method Path Description
POST /license/activate Activate a key on a device
POST /license/validate Validate license + device
POST /license/deactivate Remove device binding
POST /license/restore Restore key to new device
POST /license/renew Renew subscription
GET /license/info Get license details
GET /license/features Get available features
POST /trial/start Register trial start
POST /telemetry/send Receive usage events
GET /updates/check Check for app updates
GET /system/time Return server UTC time
POST /auth/otp/send Send OTP email
POST /auth/otp/verify Verify OTP code

Activate Request

POST /license/activate
{
  "license_key": "ABCD-1234-EFGH-5678",
  "device": {
    "fingerprint": "A1B2-C3D4-E5F6-0708",
    "platform":    "windows",
    "hostname":    "USER-PC",
    "os_version":  "Windows 11"
  },
  "app_id":    "com.mycompany.erp",
  "timestamp": 1713696000000
}

Activate Response

{
  "status": "success",
  "data": {
    "license": {
      "key":          "ABCD-1234-EFGH-5678",
      "type":         "subscription",
      "plan":         "professional",
      "activated_at": "2026-04-21T10:30:00Z",
      "expires_at":   "2027-04-21T10:30:00Z",
      "device_id":    "A1B2-C3D4-E5F6-0708",
      "features": {
        "advanced_reports": true,
        "multi_user":       true,
        "api_access":       true,
        "cloud_backup":     false
      },
      "limits": {
        "max_users":   10,
        "max_records": 100000
      },
      "signature": "base64_rsa_signature_here"
    },
    "token": "eyJhbGciOiJIUzI1NiIs..."
  }
}

πŸ”” In-App Notifications

AppShield.instance.notificationService.addListener((notification) {
  switch (notification.type) {
    case AppShieldNotificationType.expiryWarning:
      // Show banner: "License expiring in X days"
      break;
    case AppShieldNotificationType.licenseRevoked:
      // Lock app immediately
      break;
    case AppShieldNotificationType.updateAvailable:
      // Show update dialog
      break;
    default:
      debugPrint(notification.message);
  }
});

πŸ§ͺ Testing

flutter test

Tests cover:

  • License model (serialization, validity, feature checks)
  • AppShieldHelpers (key formatting, date helpers)
  • EncryptionService (encrypt/decrypt, HMAC, SHA-256)

🚨 Common Error Codes

Code Exception Cause
INVALID_KEY InvalidLicenseKeyException Wrong key format or unknown key
LICENSE_ALREADY_USED LicenseAlreadyUsedException Key in use on another device
LICENSE_EXPIRED LicenseExpiredException Subscription ended
LICENSE_REVOKED LicenseRevokedException Revoked by admin
MAX_DEVICES_EXCEEDED MaxDevicesExceededException Too many activations
TRIAL_USED TrialAlreadyUsedException Trial already used on device
CLOCK_TAMPER ClockTamperingException System clock manipulation
DEVICE_MISMATCH DeviceMismatchException License bound to different device
OFFLINE_LIMIT OfflineLimitExceededException Grace period exhausted
API_TIMEOUT ApiTimeoutException Server unreachable
SIGNATURE_INVALID SignatureVerificationException RSA signature mismatch

πŸ”§ Tips for Production

  1. Change appSecret β€” use a strong random string unique per app.
  2. Set publicKey β€” provide your RSA-2048 public key PEM for full signature verification.
  3. Enable tamper checks β€” set enableEmulatorCheck: true and enableDebuggerCheck: true in release builds.
  4. Use HTTPS β€” the API client enforces TLS; never deploy with HTTP in production.
  5. Keep grace period short β€” 7 days is a good default; less for high-security apps.
  6. Rotate appSecret on major versions β€” triggers re-validation for all users.
  7. Monitor tamper reports β€” check your server's tamper log for suspicious patterns.

πŸ’° Licensing

Tier Price Includes
Open Source Free Core + 5 presets
## πŸ’Ž Pro Plan β€” $49 / year $49 / year

βœ” All 20+ presets
βœ” Advanced gradients system
βœ” Cloud sync
βœ” Priority updates |

πŸ‘‰ πŸš€ Get Pro Access

Built with ❀️ for the Flutter community

🀝 TIMSoftDZ

TIMSoftDZ

About

πŸ›‘οΈ AppShield β€” Licensing & Activation System for Flutter

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages