Skip to content
← Todas las publicaciones
· Delta1 Labs ActivationKeyright

Activar una app de escritorio escaneando un código QR

Teclear una clave de licencia de 29 caracteres en un kiosco, un TV-box o una estación bloqueada es propenso a errores y hostil. Así se construye un flujo de activación por emparejamiento de dispositivos para una app de escritorio .NET con Keyright: la app muestra un QR, el usuario aprueba en su teléfono y el escritorio sondea una activación firmada que cachea y valida sin conexión.

Una clave de licencia es una buena credencial y una pésima cosa que teclear. En una estación con teclado es una molestia menor; en las superficies donde cada vez más viven las apps de escritorio —un kiosco de recepción, un reproductor de cartelería digital, una app tipo TV manejada con un mando, una máquina bloqueada donde la política desactiva el pegado— es francamente hostil. Una clave de Keyright es entropía agrupada, separada por guiones y sensible a mayúsculas, normalmente de 25 a 29 caracteres. Pide a alguien que la introduzca con un teclado en pantalla y un D-pad y habrás diseñado un ticket de soporte.

La solución no es una clave más corta —eso la debilitaría—. La solución es sacar la entrada de la clave del dispositivo incómodo por completo. La app de escritorio muestra un código QR; el usuario lo escanea con el teléfono que ya lleva en el bolsillo, introduce (o pega) la clave allí donde teclear es fácil, aprueba, y el escritorio recibe su activación firmada por un canal de retorno. Esta es la misma forma que la concesión de autorización de dispositivo de OAuth 2.0, adaptada al licenciamiento, y el SDK .NET de Keyright incluye las dos llamadas que necesitas para construirla.

Idea clave: empareja, no teclees. Haz que la app de escritorio inicie una activación de dispositivo, renderice el código de emparejamiento devuelto como un QR y sondee. El usuario aprueba en su teléfono con la clave real. La licencia firmada vuelve al escritorio por el canal de retorno —nunca dentro del QR— y se cachea para un Validate() sin conexión en cada arranque posterior. El QR solo lleva un código de emparejamiento de corta duración y un solo uso, así que una pantalla fotografiada no concede nada.

El flujo de un vistazo

Participan tres partes: la app de escritorio que quiere licenciarse, el servicio emisor (tu instancia autoalojada de Keyright) y el teléfono en el que el usuario aprueba. El escritorio nunca ve la clave; el teléfono nunca ve la licencia firmada. Cada uno se queda en el canal en el que es bueno.

App de escritorioServicio emisorTeléfono1. BeginDeviceActivationdevice_code, user_code, verify_urlmostrar QR2. escanear QR, abrir verify_url3. introducir clave + aprobarvincular activación4. PollDeviceActivation (repetir)pendiente… pendiente…5. licencia firmadacachear local

La propiedad crítica está en el paso 5: lo que realmente desbloquea el producto viaja del servicio al escritorio, por un canal que el teléfono del usuario nunca toca. El QR del paso 1 solo contiene el código de emparejamiento y la URL.

Paso 1 — iniciar la activación y renderizar el QR

El escritorio llama a BeginDeviceActivationAsync una vez, en el momento en que el usuario pide activar. Devuelve un DeviceActivationRequest: un DeviceCode que el escritorio guarda en privado para sondear, un UserCode corto y legible por humanos que mostrar como alternativa, una VerificationUrl y una VerificationUrlComplete ya preparada que integra el código en la URL para que el teléfono no necesite introducir nada a mano.

using Delta1Labs.Keyright;

static readonly KeyrightClient License = KeyrightClient.Initialize(new KeyrightOptions
{
    Product         = "lobby-kiosk",
    PublicKeyBase64 = "MIIBIjANBgkq...",                 // verify-only public key, pinned
    ServiceUrl      = "https://keyright.example.com"     // your self-hosted instance
});

DeviceActivationRequest req = await License.BeginDeviceActivationAsync(ct);

// Encode the *complete* verification URL (verify_url + user_code) as a QR.
// Anything that turns a string into a QBitmap works; QRCoder shown here.
using var gen = new QRCoder.QRCodeGenerator();
var data = gen.CreateQrCode(req.VerificationUrlComplete, QRCoder.QRCodeGenerator.ECCLevel.Q);
var png  = new QRCoder.PngByteQRCode(data).GetGraphic(8);
ShowQrOnScreen(png);

// Always show the short code and URL as a fallback for a phone that can't scan.
ShowText($"On your phone, go to {req.VerificationUrl} and enter: {req.UserCode}");

Lo que entra en el QR es exactamente VerificationUrlComplete —una URL más el código de emparejamiento, nada más—. En esa imagen no hay ninguna clave de licencia ni ningún token firmado. El código de emparejamiento es de un solo uso y caduca en req.ExpiresAt (minutos, no horas), así que una pantalla fotografiada lleva solo a una página de aprobación que todavía exige la clave real, y solo hasta que el código caduca.

Paso 2 — sondear la activación firmada

Mientras el QR está en pantalla, el escritorio sondea PollDeviceActivationAsync con el DeviceCode privado. El SDK respeta el intervalo de sondeo del servidor y expone un pequeño enum de estado para que puedas gobernar la interfaz sin capturar excepciones ante los estados ordinarios de «todavía no».

LicenseInfo? activated = null;
var deadline = req.ExpiresAt;

while (DateTimeOffset.UtcNow < deadline)
{
    DeviceActivationPoll poll = await License.PollDeviceActivationAsync(req.DeviceCode, ct);

    switch (poll.Status)
    {
        case DeviceActivationStatus.Pending:        // user hasn't approved yet
            await Task.Delay(poll.Interval, ct);    // respect server backoff
            continue;

        case DeviceActivationStatus.SlowDown:       // we're polling too fast
            await Task.Delay(poll.Interval + TimeSpan.FromSeconds(5), ct);
            continue;

        case DeviceActivationStatus.Approved:
            activated = poll.License;               // signed license, already cached
            break;

        case DeviceActivationStatus.Denied:
        case DeviceActivationStatus.Expired:
            ShowError($"Activation {poll.Status}. Start over to try again.");
            return;
    }

    if (activated is not null) break;
}

if (activated is { IsLicensed: true } info)
{
    ShowLicensedUi(info.Tier);
    logger.LogInformation("Activated {Tier}, bound to {Id}", info.Tier, info.MachineId);
}

Cuando el estado cambia a Approved, el SDK ya ha escrito la licencia firmada en su caché local y ha vinculado la activación a la huella de esta máquina (o a un MachineId que fijaras en KeyrightOptions, si gestionas la identidad tú mismo —útil para kioscos reimagenados—). El LicenseInfo que recuperas es el mismo objeto que devuelve cualquier otra ruta de Keyright, así que el resto de tu app no sabe ni le importa que la activación ocurriera por QR.

Paso 3 — lo que el usuario hace en el teléfono

El lado del teléfono es deliberadamente aburrido, y ese es el punto. El usuario escanea el QR, que abre la página de verificación. Introduce o pega la clave de licencia —en un teclado real, con autocompletado y pegado funcionando con normalidad— y aprueba. La página de aprobación confirma el producto y la máquina que está a punto de licenciar para que el usuario vea qué está autorizando.

Como la clave se introduce en el teléfono y la licencia firmada se entrega al escritorio, la clave nunca pasa por la entrada del kiosco, su portapapeles ni su pantalla. Para un terminal compartido o público eso es una mejora de seguridad real frente a teclear la clave en la caja delante de todo el mundo.

Si un teléfono no puede escanear —una cámara vieja, un MDM corporativo que bloquea el escáner— el UserCode y la VerificationUrl impresos son la alternativa. Un código de usuario agrupado de seis a ocho caracteres es tolerable de teclear incluso en un teclado malo, que es la razón misma por la que BeginDeviceActivationAsync devuelve un código corto distinto de la clave de licencia larga.

Tras el emparejamiento: validar sin conexión, para siempre

El emparejamiento es un apretón de manos en línea de una sola vez. Todo lo que viene después es la ruta sin conexión habitual de Keyright. En cada arranque la app llama a Validate(), que comprueba la licencia firmada cacheada contra la clave pública fijada sin ninguna llamada de red —una verificación de firma y una comprobación de caducidad, nada más—.

LicenseInfo info = License.Validate();   // offline, no I/O, never throws

if (!info.IsLicensed)
{
    // Cache missing or cleared (re-image, wiped profile). Re-pair with the QR flow.
    StartDevicePairing();
    return;
}

bool signage   = info.Entitlements.IsEnabled("digital-signage");
int  maxScreens = (int)info.Entitlements.GetLimit("screens", 1);

Así, el coste total de la red es un emparejamiento por máquina. Un kiosco que se reimagena borra la caché y simplemente se vuelve a emparejar —un escaneo de QR de treinta segundos durante el aprovisionamiento, no una clave tecleada por un técnico—. Si también licencias un nivel gratuito, una máquina sin emparejar cae a Community Edition en lugar de quedarse a oscuras: Validate() devuelve un LicenseInfo de edición Community cuyos derechos gobiernas, y el flujo QR lo actualiza en el sitio cuando alguien empareja una clave de pago.

Endurecer el flujo

Unas pocas reglas mantienen honesto el emparejamiento de dispositivos:

  • Nunca pongas nada firmado en el QR. El QR es un código de emparejamiento y una URL. La licencia —lo que concede los derechos— vuelve solo por el canal de retorno que se sondea. Trata un QR filtrado como inservible por diseño, no por oscuridad.
  • Mantén el código de emparejamiento de corta duración y de un solo uso. ExpiresAt debería ser de minutos. Una vez aprobado o caducado, el DeviceCode nunca debe volver a activar. El SDK lo impone en el servidor; no vuelvas a mostrar un QR obsoleto.
  • Respeta SlowDown. El intervalo de sondeo lo fija el servicio para protegerse. El SDK lo expone; reduce el ritmo cuando te lo indique, y deja de sondear en ExpiresAt en lugar de para siempre.
  • Muestra al usuario qué está aprobando. La página de aprobación que nombra el producto y la máquina es lo que impide que un QR de phishing vincule en silencio una clave al dispositivo de un atacante —el usuario ve un nombre de máquina desconocido y rechaza—.
  • Fija la clave pública. Como en toda integración de Keyright, PublicKeyBase64 es una clave de solo verificación compilada en la app. El Validate() sin conexión solo es tan fiable como esa fijación.

Resumen

Teclear una clave de licencia es la tarea equivocada para un kiosco, una caja de cartelería o una app tipo TV manejada con un mando, así que no le pidas al escritorio que lo haga. Inicia una activación de dispositivo, renderiza el código de emparejamiento devuelto como un QR y sondea: el usuario aprueba en el teléfono en el que ya teclea con comodidad, la licencia firmada se entrega al escritorio por un canal de retorno que el teléfono nunca toca, y a partir de ahí la app valida sin conexión con Validate() como cualquier otra integración de Keyright. El QR solo contiene un código de emparejamiento de corta duración y un solo uso, así que una pantalla fotografiada no concede nada. El SDK .NET de Keyright te da BeginDeviceActivationAsync y PollDeviceActivationAsync para el apretón de manos y el mismo LicenseInfo en cerrado en todo lo que viene después, así que puedes licenciar la superficie más hostil a la entrada que distribuyas con un escaneo de treinta segundos.

Prueba Nebula.NET

Endurece tu código .NET en minutos — empieza con la edición gratuita.