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.

Dragon Guard Handheld uses a two-path authentication strategy designed for warehouses operating inside the Grupo MAS ecosystem. Every login attempt starts with the delegated Grupo MAS endpoint; the app automatically retries against the standard local endpoint only when the backend signals that the tenant or user is not configured for Grupo MAS delegated login. Both paths carry the device’s identity so the backend can enforce the hardware allow-list before granting a session.

Login endpoints

PathPurpose
POST api/auth/grupomas-handheld-loginPrimary — Grupo MAS delegated authentication
POST api/auth/loginFallback — local credential authentication
GET api/auth/registration-optionsFetch available companies and default role before registering
POST api/auth/registerCreate a new local user account

Login sequence

1

Collect credentials and device identity

The LoginViewModel reads the email and password entered by the operator and calls DeviceIdentityService to resolve DeviceId and DeviceName before building the request.
var response = await _authService.LoginAsync(new AuthLoginRequestDto
{
    Email    = Email.Trim(),
    Password = Password,
    DeviceId   = _deviceIdentityService.DeviceId,
    DeviceName = _deviceIdentityService.DeviceName,
    IsHandheld = true
});
2

POST to Grupo MAS handheld login

AuthService.LoginAsync serialises the request and posts to the primary endpoint. The payload differs slightly from the local endpoint — it uses username instead of email:
POST api/auth/grupomas-handheld-login
{
  "username":   "[email protected]",
  "password":   "••••••••",
  "deviceId":   "AA:BB:CC:DD:EE:FF",
  "deviceName": "TC57 Warehouse 3"
}
response = await _httpClient.PostAsJsonAsync("api/auth/grupomas-handheld-login", new
{
    username   = request.Email,
    password   = request.Password,
    deviceId   = request.DeviceId,
    deviceName = request.DeviceName
});
3

Evaluate response — fallback if required

If the primary endpoint returns a non-success status code, AuthService inspects the JSON messageKey field. When the key is auth.localEmailConflict or auth.grupoMasNativeRequired, the app transparently retries using the local login endpoint with the full payload including isHandheld: true.
if (!response.IsSuccessStatusCode && ShouldFallbackToLocalLogin(payload))
    return await LocalLoginAsync(request);
The local fallback payload:
POST api/auth/login
{
  "email":      "[email protected]",
  "password":   "••••••••",
  "deviceId":   "AA:BB:CC:DD:EE:FF",
  "deviceName": "TC57 Warehouse 3",
  "isHandheld": true
}
4

Deserialise AuthSessionResponseDto

A successful 2xx response is deserialised into AuthSessionResponseDto. A parse failure (malformed JSON, missing company or role data) surfaces a user-facing message rather than a raw exception.
public class AuthSessionResponseDto
{
    public string Token    { get; set; } = string.Empty;
    public Guid   UserId   { get; set; }
    public string Email    { get; set; } = string.Empty;
    public string FullName { get; set; } = string.Empty;
    public List<AuthCompanyAccessDto> Companies { get; set; } = new();
}
Each entry in Companies carries the company GUID and the role code granted to the user inside that company:
public class AuthCompanyAccessDto
{
    public Guid   CompanyId { get; set; }
    public string RoleCode  { get; set; } = string.Empty;
}
5

Company context enforcement in SessionService

SessionService.SignInAsync filters out entries where CompanyId is an empty GUID and then enforces the single-company rule before writing anything to storage. Login is blocked in both the zero-company and multi-company cases.
if (companies.Count == 0)
    throw new InvalidOperationException(
        "El usuario no tiene una empresa operativa asignada para handheld.");

if (companies.Count > 1)
    throw new InvalidOperationException(
        "Este usuario tiene acceso a varias empresas. " +
        "Para Dragon Guard Handheld se requiere una sola empresa operativa por usuario.");
If SignInAsync throws, the session is not written to storage. The operator sees the error message on the login screen and must contact their administrator to resolve the company assignment before trying again.
6

Post-login routing

After SessionService.SignInAsync succeeds, LoginViewModel checks whether the operator’s company catalog has been synced. First-time users are sent to the welcome setup flow; returning users trigger a background catalog sync and navigate directly to the home screen.
var alreadySynced = companyId.HasValue &&
                    Preferences.Get($"catalog_synced_{companyId}", false);

if (!alreadySynced)
    await app.ShowWelcomeSetupAsync();
else
{
    _ = _itemService.TriggerCatalogSyncAsync();
    await app.ShowHomeAsync();
}
The login page blocks hardware back navigation. Once SessionService clears a session — whether by sign-out or a failed sign-in — Android’s back stack cannot navigate to protected pages. Pressing the physical back button on the login screen does nothing; this is intentional and required to prevent an operator from accessing warehouse data after their session ends.

Error message keys

The backend returns structured JSON errors with a messageKey field. AuthService.TryReadJsonLoginError maps each key to a localised, operator-readable message. The table below documents every key and the action required to resolve it.
messageKeyUser-facing messageResolution
auth.externalLoginFailedMAS rechazó el usuario o password (or the server’s message field)Verify Grupo MAS credentials are correct
auth.localEmailConflictExiste un usuario local con el mismo identificadorApp falls back to local login automatically
auth.grupoMasNativeRequiredEste tenant no está configurado para login handheld de Grupo MASApp falls back to local login automatically
auth.companyInactiveEste tenant está inactivoContact system administrator to reactivate the tenant
integration.adapterNotConfiguredLa integración Grupo MAS no está configurada para este tenantSupreme Admin must configure the Grupo MAS adapter
integration.remoteUnavailableMAS no está disponible o rechazó la conexión de loginCheck Grupo MAS service availability
integration.companyCodeRequiredFalta el código de empresa requerido para Grupo MASSupreme Admin must set the company code in the integration settings
plans.deviceNotAuthorizedEste handheld no está autorizado — MAC/ID shown if returnedRegister the device’s MAC or ANDROID_ID via Supreme Admin
plans.deviceInactiveEste handheld está inactivo — MAC/ID shown if returnedReactivate the device via Supreme Admin
plans.deviceLimitExceededEl tenant superó el límite de handhelds activos del planUpgrade plan or deactivate an unused device
Non-JSON error strings (plain text responses) are normalised through a secondary switch:
Raw stringUser-facing message
EMAIL_AND_PASSWORD_REQUIREDIngresa usuario y password
USERNAME_AND_PASSWORD_REQUIREDIngresa usuario y password
DEVICE_ID_REQUIREDNo se pudo identificar este handheld. Revisa la configuración del dispositivo
USER_NOT_FOUNDUsuario no encontrado
INVALID_USERUsuario invalido
INVALID_PASSWORDPassword incorrecto
LOGIN_TEMPORARILY_UNAVAILABLELogin no disponible temporalmente. Revisa la conexión del sistema
plans.deviceNotAuthorizedEste handheld no está autorizado para este tenant. Solicita al admin supremo registrar la MAC/ID del dispositivo
plans.deviceInactiveEste handheld está inactivo para este tenant. Solicita reactivación al admin supremo
plans.deviceLimitExceededEl tenant superó el límite de handhelds activos del plan
Responses that contain SQL or stack-trace keywords are replaced with a generic technical error message rather than exposing server internals to the operator.

User registration

New local users can be created from within the app. The registration flow first fetches available companies and the default role, then posts the new account.
1

Fetch registration options

GET api/auth/registration-options
Response:
public class AuthRegistrationOptionsDto
{
    public List<AuthRegistrationCompanyDto> Companies { get; set; } = new();
    public string DefaultRoleCode { get; set; } = "OPERATOR";
}

public class AuthRegistrationCompanyDto
{
    public Guid   Id   { get; set; }
    public string Code { get; set; } = string.Empty;
    public string Name { get; set; } = string.Empty;
}
2

Submit registration request

POST api/auth/register
Request body maps to RegisterUserRequestDto:
public class RegisterUserRequestDto
{
    public string Email    { get; set; } = string.Empty;
    public string Password { get; set; } = string.Empty;
    public string FullName { get; set; } = string.Empty;
    public Guid?  CompanyId { get; set; }
}
A non-success response surfaces the raw server message to the user since registration errors are always admin-level issues rather than transient failures.
Registration creates a local account. Users who authenticate via Grupo MAS delegated login do not need a separate registration step — their identity is managed entirely by the Grupo MAS system.

Build docs developers (and LLMs) love