Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/CodexaCP/DG_APK/llms.txt

Use this file to discover all available pages before exploring further.

Every login request sent from Dragon Guard Handheld includes a stable device identifier and device name alongside the operator’s credentials. The backend cross-references this identity against a per-tenant allow-list managed by a Supreme Admin. If the device is not registered, is marked inactive, or would push the tenant over its plan’s handheld limit, the login is rejected before any session token is issued. This hardware-level gate prevents unauthorized devices from accessing warehouse data even when valid credentials are provided.

How the app resolves its device identity

DeviceIdentityService walks a three-step resolution chain every time DeviceId is read. The first non-empty, non-placeholder value found is used.
1

Attempt to read the Wi-Fi MAC address

The service enumerates all network interfaces and looks for one named wlan0 or containing wifi. If found, it reads the physical (MAC) address bytes and formats them as a colon-separated uppercase hex string.
private static string? TryGetWifiMacAddress()
{
    foreach (var networkInterface in
             System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces())
    {
        var name = networkInterface.Name?.ToLowerInvariant() ?? string.Empty;
        if (name != "wlan0" && !name.Contains("wifi"))
            continue;

        var bytes = networkInterface.GetPhysicalAddress()?.GetAddressBytes();
        if (bytes == null || bytes.Length == 0)
            continue;

        var mac = string.Join(":", bytes.Select(x => x.ToString("X2")));
        if (mac != "02:00:00:00:00:00" && mac != "00:00:00:00:00:00")
            return mac;
    }
    return null;
}
The values 02:00:00:00:00:00 and 00:00:00:00:00:00 are Android’s privacy-placeholder MACs and are explicitly rejected so the chain continues.
2

Fall back to ANDROID_ID

If no real MAC is available — which is the common case on Android 10 and later — the service reads Settings.Secure.ANDROID_ID from the device’s content resolver. This is a 64-bit hex value unique to each combination of app-signing key, user, and device. It is returned uppercase.
var context   = Android.App.Application.Context;
var androidId = Android.Provider.Settings.Secure.GetString(
    context.ContentResolver,
    Android.Provider.Settings.Secure.AndroidId);

if (!string.IsNullOrWhiteSpace(androidId))
    return androidId.Trim().ToUpperInvariant();
3

Generate and persist a GUID fallback

If both platform-specific methods fail (non-Android builds, emulators with no stable ID, or permissions issues), a random GUID is generated once, stored in Preferences, and reused on subsequent launches.
var stored = Preferences.Get("handheld_device_id", string.Empty);
if (!string.IsNullOrWhiteSpace(stored))
    return stored;

stored = Guid.NewGuid().ToString("N").ToUpperInvariant();
Preferences.Set("handheld_device_id", stored);
return stored;
The GUID fallback is stored in Preferences, which is not backed by the Android Keystore. Clearing app data or reinstalling the app generates a new GUID, making the old device registration stale. On production handhelds, always ensure a real MAC or ANDROID_ID is being resolved so the registration remains stable across app updates.

Device name resolution

DeviceIdentityService.DeviceName uses DeviceInfo.Current.Name (the friendly device name set in Android settings). If that is blank, it falls back to "{Manufacturer} {Model}".
public string DeviceName
{
    get
    {
        var name = DeviceInfo.Current.Name;
        if (!string.IsNullOrWhiteSpace(name))
            return name;

        return $"{DeviceInfo.Current.Manufacturer} {DeviceInfo.Current.Model}".Trim();
    }
}

Fields sent on every login

Both the Grupo MAS and local login endpoints receive the device identity as part of the standard login payload. The isHandheld flag signals the backend that this is a handheld client and triggers the device allow-list enforcement.
{
  "username":   "[email protected]",
  "password":   "••••••••",
  "deviceId":   "A1B2C3D4E5F6A1B2",
  "deviceName": "Zebra TC57"
}
The Grupo MAS endpoint infers isHandheld from the endpoint path itself (grupomas-handheld-login); the field is not sent explicitly on this path.

Android 10+ and MAC address privacy

Starting with Android 10, the OS returns the randomized placeholder 02:00:00:00:00:00 instead of the real hardware MAC when an app reads Wi-Fi interface addresses. DeviceIdentityService detects this placeholder and discards it, so ANDROID_ID is sent as deviceId on virtually all modern devices.
When a login attempt fails with plans.deviceNotAuthorized, the error message returned by the backend includes the deviceId that was rejected — for example:
Este handheld no está autorizado para este tenant. MAC/ID: A1B2C3D4E5F6A1B2
The Supreme Admin should copy this exact string from the error message and use it when registering the device. Do not attempt to read the ID from Android settings manually — use the value the app itself reports, since it reflects the resolution chain described above.

Backend enforcement rules

The backend validates all four conditions in order before issuing a session token. Failure at any step returns one of the structured JSON error responses shown below.

Valid credentials

The username/email and password must authenticate successfully against the identity provider (Grupo MAS or local). A credential failure returns auth.externalLoginFailed or INVALID_PASSWORD.

Device registered

The deviceId sent on login must exist in the tenant’s authorized device list, managed by a Supreme Admin. An unregistered device returns plans.deviceNotAuthorized.

Device active

The device must be in active status. Devices can be deactivated without being removed from the allow-list. An inactive device returns plans.deviceInactive.

Plan limit not exceeded

The tenant’s active handheld count must be within the limit set by its subscription plan. Exceeding the limit returns plans.deviceLimitExceeded.

Device error codes

Error keyMeaningHow to resolve
plans.deviceNotAuthorizedThe deviceId sent has never been registered for this tenantSupreme Admin must add the MAC/ID (shown in the error message) to the tenant’s device list
plans.deviceInactiveThe device is registered but has been deactivatedSupreme Admin must set the device status back to active
plans.deviceLimitExceededThe tenant has reached the maximum number of concurrent active handhelds allowed by its planUpgrade the plan, or deactivate a device that is no longer in use

Device registration workflow (Supreme Admin)

1

Attempt login on the handheld

Ask the operator to enter their credentials on the device and tap Iniciar sesión. If the device is not yet registered, the login will fail and display an error message containing the device’s deviceId.
2

Record the MAC/ID from the error message

The error message follows the pattern:
Este handheld no está autorizado para este tenant. MAC/ID: <deviceId>
Copy the full deviceId string exactly as shown. This is the value the backend will validate on future login attempts.
3

Open the Supreme Admin panel

Log in to the Dragon Guard web admin interface with Supreme Admin credentials and navigate to the tenant’s Handhelds or Dispositivos autorizados section.
4

Add the device

Create a new device entry using the deviceId copied in Step 2. Set the display name to a recognizable label (location, operator name, or model). Set the status to Active.
5

Verify the plan limit

Confirm that adding this device does not push the tenant over its active handheld plan limit. If it would, either deactivate an unused device or upgrade the plan before proceeding.
6

Retry login on the handheld

Have the operator attempt login again. The device identity will now match the allow-list entry and the authentication flow will proceed to credential and company validation.
For fleet deployments, collect all device IDs before rolling out the app by running a test login on each unit and capturing the plans.deviceNotAuthorized error messages. Register all devices in the admin panel in bulk before operators go live.

Build docs developers (and LLMs) love