En esta página

Visión general de OAuth 2.0

RFC 6749 - El marco de autorización OAuth 2.0 especifica un protocolo para la autorización de servicios mediante el uso de una aplicación de terceros. OAuth 2.0 utiliza tokens para abstraer la autorización del servicio y los usuarios. Este método es más seguro, ya que el propietario del servicio no necesita ocuparse de las credenciales de los usuarios. Sustituye al RFC 5849 OAuth 1.0.

El marco OAuth 2.0 define dos tipos de cliente, público o confidencial, y flujos como el flujo de código de autorización, la concesión implícita de código y varios otros para la autorización. Una aplicación Qt típica se considera una aplicación nativa pública. Una aplicación cliente pública es una aplicación en la que no se puede confiar que contenga información confidencial, como contraseñas, para incrustarla dentro del binario enviado.

RFC 8252 OAuth 2.0 for Native Apps define con más detalle las mejores prácticas para aplicaciones nativas. Específicamente, RFC 8252 recomienda el flujo de autorización con el navegador. Por lo tanto, las clases QtNetworkAuth proporcionan una implementación concreta de este flujo.

Como novedad en Qt 6.9, QtNetworkAuth proporciona soporte para RFC 8628 - OAuth 2.0 Device Authorization Grant. Este flujo de dispositivo está pensado para dispositivos que tienen capacidades de entrada limitadas o poco prácticas. Para este flujo, las concesiones de autorización utilizan un dispositivo secundario, como smartphones, en lugar del dispositivo. Ejemplos de estos dispositivos son televisores, consolas multimedia, HMI de máquinas y dispositivos IoT. El usuario puede entonces utilizar una aplicación en su smartphone para autorizar el dispositivo.

La siguiente tabla muestra los dos flujos OAuth 2.0 compatibles con Qt Network Authorization:

AspectoFlujo de código de autorizaciónFlujo de autorización del dispositivo
Conexión de red
Interacción con el usuarioNavegador / agente de usuario en el mismo dispositivoNavegador / agente de usuario en un dispositivo diferente
Redirección requeridaNo
Capacidad de entrada en el dispositivoGran capacidad de entradaCapacidad de entrada limitada o nula
ObjetivosAplicaciones móviles y de escritorioTelevisores, consolas, HMI, dispositivos IoT

OAuth 2.0 requiere el uso de un agente de usuario, que suele ser un navegador. Para más información, consulte Qt OAuth2 Browser Support.

Clases de OAuth 2.0

Qt Network Authorization proporciona clases OAuth 2.0 concretas y abstractas. Las clases abstractas están pensadas para implementar flujos personalizados, mientras que las clases concretas proporcionan una implementación concreta.

Para ver la lista de clases concretas, consulte la página QtNetworkAuth.

Qt Network Authorization tiene dos clases abstractas para implementar flujos OAuth 2.0:

Flujo del código de autorización

Esta sección es una visión general del flujo de código de autorización de RFC 6749 - Authorization Code y RFC 8252 - Authorization Request from a Native App para aplicaciones nativas.

Considere la siguiente configuración de ejemplo:

QOAuth2AuthorizationCodeFlow m_oauth;
QOAuthUriSchemeReplyHandler m_handler;

m_oauth.setAuthorizationUrl(QUrl(authorizationUrl));
m_oauth.setTokenUrl(QUrl(accessTokenUrl));
m_oauth.setClientIdentifier(clientIdentifier);
m_oauth.setRequestedScopeTokens({scope});

connect(&m_oauth, &QAbstractOAuth::authorizeWithBrowser, this, &QDesktopServices::openUrl);
connect(&m_oauth, &QAbstractOAuth::granted, this, [this]() {
    // Here we use QNetworkRequestFactory to store the access token
    m_api.setBearerToken(m_oauth.token().toLatin1());
    m_handler.close();
});

m_handler.setRedirectUrl(QUrl{"com.example.myqtapp://oauth2redirect"_L1});
m_oauth.setReplyHandler(&m_handler);

// Initiate the authorization
if (m_handler.listen()) {
    m_oauth.grant();
}

Etapas del flujo de autorización

El flujo de código de autorización RFC 6749 tiene dos etapas principales: autorización de recursos (incluida cualquier autenticación de usuario necesaria) seguida de una solicitud de token de acceso. A éstas les siguen, opcionalmente, el uso del token de acceso y la renovación del token de acceso. La siguiente figura ilustra estas etapas:

Proceso de autorización simplificado en una aplicación Qt mediante un servidor de autorización y un navegador

  • En la etapa de autorización, el usuario se autentica y autoriza el acceso a los recursos. Esto requiere la interacción del usuario con el navegador.
  • Tras la autorización, el código de autorización recibido se utiliza para solicitar un token de acceso y, opcionalmente, un token de actualización.
  • Una vez obtenido el token de acceso, la aplicación lo utiliza para acceder a los recursos de interés. El token de acceso se incluye en las solicitudes de recursos, y corresponde al servidor de recursos verificar la validez del token. Hay varias formas de incluir el token como parte de las peticiones con tokens portadores. Incluir el token en la cabecera HTTP Authorization es posiblemente la más común.
  • Actualización del token de acceso. Los tokens de acceso suelen caducar relativamente rápido, por ejemplo, al cabo de una hora. Si la aplicación recibió un token de actualización además del token de acceso, el token de actualización puede utilizarse para solicitar un nuevo token de acceso. Las aplicaciones pueden mantener los tokens de actualización durante más tiempo para evitar la necesidad de una nueva etapa de autorización (y, por tanto, otra interacción con el navegador).

Detalles y personalización

Los flujos de OAuth 2.0 son dinámicos y la aplicación de las especificaciones puede resultar complicada al principio. La siguiente figura ilustra los principales detalles de un flujo de código de autorización satisfactorio.

Detalles del flujo de OAuth 2.0 mostrando las llamadas a eventos específicos

Para mayor claridad, la figura omite algunas señales, pero en conjunto ilustra los detalles y los principales puntos de personalización. Los puntos de personalización son las diversas señales y ranuras que la aplicación puede utilizar, así como las devoluciones de llamada que se pueden configurar con QAbstractOAuth::setModifyParametersFunction() y QAbstractOAuth2::setNetworkRequestModifier().

Elegir un gestor de respuesta

Decidir qué manejador utilizar depende del elemento redirect_uri. El redirect_uri se establece a donde el navegador es redirigido al concluir la etapa de autorización.

Para recibir la respuesta de autorización en aplicaciones nativas, el RFC 8252 especifica tres tipos principales de esquemas URI de respuesta: private-use, loopback y https.

  • URI de uso privado: Pueden utilizarse si el sistema operativo permite a una aplicación registrar un esquema URI personalizado. Un intento de abrir una URL con dicho esquema personalizado abrirá la aplicación nativa relacionada. Véase QOAuthUriSchemeReplyHandler.
  • URIs HTTPS: Puede utilizarse si el SO permite a la aplicación registrar una URL HTTPS personalizada. Un intento de abrir esta URL abrirá la aplicación nativa relacionada. Este esquema se recomienda si el sistema operativo lo admite. Véase QOAuthUriSchemeReplyHandler.
  • Interfaces Loopback: Estas son comúnmente usadas para aplicaciones de escritorio, y aplicaciones durante el desarrollo. QOAuthHttpServerReplyHandler está diseñado para gestionar estas URI mediante la configuración de un servidor local que se encargue de la redirección.

La elección depende de varios factores como:

  • URIs de redirección soportados por el proveedor del servidor de autorización. El soporte varía de un proveedor a otro, y a menudo es específico para un tipo de cliente y sistema operativo en particular. Además, el soporte puede variar dependiendo de si la aplicación está publicada o no.
  • Esquemas URI de redirección compatibles con la plataforma o plataformas de destino.
  • Requisitos de usabilidad, seguridad y otros específicos de la aplicación.

El RFC 8252 recomienda utilizar el esquema https por las ventajas de seguridad y usabilidad sobre los otros métodos.

Concesión de autorización de dispositivos OAuth 2.0

ElRFC 8628 OAuth 2.0 Device Authorization Grant está pensado para dispositivos conectados que están limitados en términos de capacidades de entrada, o donde el uso de user-agent-browser no es práctico. Ejemplos de dispositivos que utilizan este flujo son los electrodomésticos inteligentes que necesitan dispositivos externos para la autorización.

Considere la siguiente configuración de ejemplo:

m_deviceFlow.setAuthorizationUrl(QUrl(authorizationUrl)); m_deviceFlow.setTokenUrl(QUrl(accessTokenUrl)); m_deviceFlow.setRequestedScopeTokens({scope}); m_deviceFlow.setClientIdentifier(clientIdentifier);// La necesidad de un secreto de cliente depende del servidor de autorizaciónm_deviceFlow.setClientIdentifierSharedKey(clientSecret); connect(&m_deviceFlow, &QOAuth2DeviceAuthorizationFlow::authorizeWithUserCode, this,[](const QUrl &verificationUrl, const QString &userCode, const QUrl &completeVerificationUrl) { if (completeVerificationUrl.isValid()) { // Si el servidor de autorización ha proporcionado una URL completa // que ya contiene los datos necesarios como parte de los parámetros de la URL, // puede optar por utilizarla.            qDebug() << "Complete verification uri:" << completeVerificationUrl;
        } else { // El servidor de autorización sólo ha proporcionado la URL de verificación; utilícela            qDebug() << "Verification uri and usercode:" << verificationUrl << userCode;
        } } ); connect(&m_deviceFlow, &QAbstractOAuth::granted, this, [this](){ // Aquí usamos QNetworkRequestFactory para almacenar el token de accesom_api.setBearerToken(m_deviceFlow.token().toLatin1()); }); m_deviceFlow.grant();

Etapas de concesión de autorización de dispositivos

El flujo de concesión de autorización de dispositivos consta de tres etapas principales: inicialización de la autorización, búsqueda de tokens y finalización de la autorización. A éstas les siguen, opcionalmente, el uso y la actualización de los tokens. La siguiente figura ilustra estas etapas:

Flujo de concesión de autorización simplificado en una aplicación Qt que utiliza un navegador en un dispositivo independiente.

  • La autorización se inicializa enviando una petición HTTP al servidor de autorización. El servidor de autorización proporciona como respuesta un código de usuario, URL de verificación y un código de dispositivo.
  • Una vez inicializada la autorización, se proporciona al usuario un código de usuario y una o varias URL de verificación para completar la autorización. El mecanismo para el usuario final varía: puede ser una URL visible en la pantalla, un código QR, un correo electrónico, etc. Para más información, consulte RFC 8628 - User Interaction.
  • Mientras se espera a que el usuario final complete la autorización, el flujo del dispositivo sondea el servidor de autorización en busca de tokens. El código de dispositivo recibido en el paso anterior se utiliza para emparejar la sesión de autorización. El intervalo de sondeo lo decide el servidor de autorización y suele ser de 5 segundos.
  • Una vez que el usuario final ha aceptado o denegado la autorización, el servidor de autorización responde a una solicitud de sondeo con los tokens solicitados o un código de error en caso de denegación, y la autorización queda completada.

Detalles y personalización

La siguiente figura ilustra con más detalle el flujo de concesión de autorizaciones de dispositivos. La figura muestra los principales puntos de personalización, que a veces son necesarios. Por ejemplo, parámetros propietarios o credenciales de autenticación adicionales.

Detalles del flujo de concesión de dispositivos OAuth 2.0 mostrando las llamadas a eventos específicos

Actualizar tokens

Los tokens de actualización requieren que el servidor de autorización proporcione un token de actualización durante la autorización. Proporcionar un token de actualización depende del servidor de autorización: algunos servidores pueden elegir proporcionarlo siempre, otros no proporcionarlo nunca y otros lo proporcionan si un scope específico estaba presente en la solicitud de autorización.

La siguiente figura ilustra la actualización del testigo con más detalle:

Detalles de las fichas de actualización que muestran las llamadas a eventos específicos

Como se muestra en la figura anterior, los puntos de personalización habituales también están disponibles cuando se refrescan los tokens.

Para refrescar los tokens después del inicio de una aplicación, la aplicación necesita persistir el token de refresco de forma segura, y establecerlo con QAbstractOAuth2::setRefreshToken. A continuación, se puede llamar a QAbstractOAuth2::refreshTokens para solicitar nuevos tokens.

Como novedad en Qt 6.9, las aplicaciones pueden refrescar automáticamente los tokens - ver QAbstractOAuth2::accessTokenAboutToExpire, QAbstractOAuth2::autoRefresh, y QAbstractOAuth2::refreshLeadTime.

El tiempo de expiración de un token de refresco no suele ser indicado por el servidor de autorización (aparte de la documentación del servidor). Su validez puede oscilar entre días, meses o más. Además, como ocurre con otros tokens, los refresh tokens pueden ser revocados por el usuario y, por tanto, invalidados en cualquier momento. Por lo tanto, es importante detectar correctamente un intento de actualización fallido con QAbstractOAuth::requestFailed o QAbstractOAuth2::serverReportedErrorOccurred.

Los flujos de OAuth 2.0 requieren muchas interacciones por parte del usuario, lo que puede resultar intrusivo para su experiencia. Para minimizar estas interacciones, los tokens pueden ser refrescados silenciosamente para el usuario. Consulte el RFC 6749 - Refreshing Access Tokens para obtener más información.

Compatibilidad de Qt con OpenID Connect

OpenID Connect (OIDC) es una capa de identidad simple sobre OAuth 2.0. OIDC puede utilizar un servidor de autorización para autenticar la identidad de un usuario. OIDC también permite acceder a la información del perfil de un usuario.

El soporte de Qt para OIDC se limita por el momento a obtener tokens de identificación. Un token de identificación es un JSON Web Token (JWT) que contiene afirmaciones sobre el evento de autenticación.

Nota: La validación de tokens de identificación o el descifrado de tokens de identificación no están implementados actualmente. Debe utilizar una biblioteca JWT de terceros para la firma o verificación de tokens JWT.

Asumiendo que la aplicación es capaz de validar los tokens recibidos, el token puede ser utilizado para establecer la identidad del usuario de forma fiable (siempre y cuando el propio proveedor de OIDC sea de confianza).

Los tokens de identificación son información sensible que debe mantenerse en secreto y no son lo mismo que los tokens de acceso. Los tokens de identificación no están pensados para ser enviados en llamadas API - el token de acceso está pensado para ese propósito. Tenga en cuenta que algunos proveedores pueden utilizar el mismo formato JWT para los tokens de acceso, pero no debe confundirse con los tokens de identificación reales, que utilizan el mismo formato. Con los tokens de identificación, el cliente que recibe el token es el responsable de verificarlo, mientras que con los tokens de acceso es el servidor de recursos que acepta el token el responsable de la verificación.

Obtener un token de identificación

La obtención de un token de identificación es similar a la de un token de acceso. En primer lugar, hay que establecer el ámbito adecuado. El proveedor del servidor de autorización puede admitir especificadores de ámbito adicionales como profile y email, pero todas las solicitudes de OIDC deben incluir el ámbito openid:

m_oauth.setRequestedScopeTokens({"openid"});

Para OIDC, se recomienda encarecidamente utilizar el parámetro nonce. Esto se hace asegurándose de que se establece el NonceMode apropiado.

// This is for illustrative purposes, 'Automatic' is the default mode
m_oauth.setNonceMode(QAbstractOAuth2::NonceMode::Automatic);

Como último paso, podemos escuchar la señal QAbstractOAuth2::granted o la QAbstractOAuth2::idTokenChanged directamente:

connect(&m_oauth, &QAbstractOAuth2::idTokenChanged, this, [this](const QString &token) {
    Q_UNUSED(token); // Handle token
});

Validación de un token de identificación

Validar el token de ID recibido es una parte crítica del flujo de autenticación, y cuando se implementa completamente, una tarea algo complicada. Consulte la información completa en Validación de ID de OpenID Connect.

Como pequeño resumen, la validación consiste en estos pasos:

  • Descifrar el token si es necesario(ver JWE)
  • Extracción del encabezado, la carga útil y la firma del token
  • Validación de la firma
  • Validación de los campos de la carga útil (como aud, iss, exp, nonce, iat)

Actualmente Qt no proporciona soporte para la validación de tokens ID, pero existen librerías JWT de terceros, como jwt-cpp.

Ejemplo de verificación de token de identificación

Esta sección ilustra un ejemplo sencillo de verificación. Como prerrequisitos, el entorno de desarrollo necesita tener las librerías OpenSSL, y jwt-cpp en la carpeta include bajo el directorio fuente del proyecto de aplicación.

En el archivo CMakeLists.txt del proyecto de aplicación, comprobamos en primer lugar que se cumplen los requisitos previos:

find_package(OpenSSL 1.0.0 QUIET)
set(JWT_CPP_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/include")
if(OPENSSL_FOUND AND EXISTS "${JWT_CPP_INCLUDE_DIR}/jwt-cpp/jwt.h")

Después añadimos los includes y librerías necesarios:

    target_include_directories(networkauth_oauth_snippets PRIVATE "${JWT_CPP_INCLUDE_DIR}")
    target_link_libraries(networkauth_oauth_snippets PRIVATE OpenSSL::SSL OpenSSL::Crypto)
    target_compile_definitions(networkauth_oauth_snippets PRIVATE JWT_CPP_AVAILABLE)

En los archivos fuente de la aplicación, incluya la biblioteca de verificación:

#ifdef JWT_CPP_AVAILABLE
#include "jwt-cpp/jwt.h"
#endif

Una vez que la aplicación recibe un token de ID, es hora de verificarlo. Primero buscamos una clave que coincida en JSON Web Key Sets (JWKS), véase OpenID Connect Discovery).

try {
    const auto jwt = jwt::decode(m_oauth.idToken().toStdString());
    const auto jwks = jwt::parse_jwks(m_jwks->toJson(QJsonDocument::Compact).toStdString());
    const auto jwk = jwks.get_jwk(jwt.get_key_id());

Y luego hacemos la verificación propiamente dicha:

   // Aquí usamos el módulo y el  exponente  para derivar la clave const auto n = jwk.get_jwk_claim("n").as_string(); // módulo const auto e = jwk.get_jwk_claim("e").as_string(); // exponente if (n.empty() || e.empty()) {        qWarning() << "Modulus or exponent empty";
       return false; } if (jwt.get_algorithm() != "RS256") { // Este ejemplo sólo admite RS256        qWarning() << "Unsupported algorithm:" << jwt.get_algorithm();
       return false; } if (jwk.get_jwk_claim("kty").as_string() != "RSA") {        qWarning() << "Unsupported key type:" << jwk.get_jwk_claim("kty").as_string();
       return false; } if (jwk.has_jwk_claim("use") && jwk.get_jwk_claim("use").as_string() != "sig") {        qWarning() << "Key not for signature" << jwk.get_jwk_claim("use").as_string();
       return false; } // Verificación mínima simple (omite casos especiales y, por ejemplo, la verificación 'sub'). // jwt-cpp comprueba también 'exp', 'iat', y 'nbf' si están presentes. const auto keyPEM = jwt::helper::create_public_key_from_rsa_components(n, e); auto verifier = jwt::verify() .allow_algorithm(jwt::algorithm::rs256(keyPEM)) . with_claim("nonce", jwt::claim(m_oauth.nonce().toStdString())) . with_issuer(m_oidcConfig->value("issuer"_L1).toString().toStdString()) . with_audience(std::string(clientIdentifier.data())) . leeway(60UL); verifier.verify(jwt);    qDebug() << "ID Token verified successfully";
   return true; } catch(const std::exception &e) { // Manejar el error. Alternativamente pasar el parámetro error a las llamadas jwt-cpp    qWarning() << "ID Token verification failed" << e.what();
   return false; }

Lectura de los valores de los tokens ID

El token ID está en formato JSON Web Token (JWT) y consta de una cabecera, una carga útil y partes de firma, separadas por puntos ..

La lectura de los valores del token ID es sencilla. A modo de ejemplo, supongamos que existe un struct:

struct IDToken {
    QJsonObject header;
    QJsonObject payload;
    QByteArray signature;
};

Y una función:

std::optional<IDToken> parseIDToken(const QString &token) const;

El token se puede extraer con:

if (token.isEmpty())
    return std::nullopt;

QList<QByteArray> parts = token.toLatin1().split('.');
if (parts.size() != 3)
    return std::nullopt;

QJsonParseError parsing;

QJsonDocument header = QJsonDocument::fromJson(
    QByteArray::fromBase64(parts.at(0), QByteArray::Base64UrlEncoding), &parsing);
if (parsing.error != QJsonParseError::NoError || !header.isObject())
    return std::nullopt;

QJsonDocument payload = QJsonDocument::fromJson(
    QByteArray::fromBase64(parts.at(1), QByteArray::Base64UrlEncoding), &parsing);
if (parsing.error != QJsonParseError::NoError || !payload.isObject())
    return std::nullopt;

QByteArray signature = QByteArray::fromBase64(parts.at(2), QByteArray::Base64UrlEncoding);

return IDToken{header.object(), payload.object(), signature};

En algunos casos el token puede estar encriptado como JSON Web Encryption (JWE) que internamente contiene un token JWT. En este caso, el token debe ser descifrado primero.

Descubrimiento de OpenID Connect

OpenIDConnect Discovery define los medios para descubrir los detalles necesarios del proveedor OpenID, con el fin de interactuar con él. Esto incluye información como authorization_endpoint y token_endpoint URLs.

Aunque estos detalles del proveedor pueden ser configurados estáticamente en la aplicación, descubrir los detalles en tiempo de ejecución puede proporcionar más flexibilidad y robustez en la interacción con varios proveedores.

Obtener el documento de descubrimiento es una simple petición HTTP GET. El documento se encuentra normalmente en https://<domain name>/.well-known/openid_configuration.

m_network->get(request, this, [this](QRestReply &reply) {
    if (reply.isSuccess()) {
        if (auto doc = reply.readJson(); doc && doc->isObject())
            m_oidcConfig = doc->object(); // Store the configuration
    }
});

En particular, para la validación de tokens, el campo jwks_uri proporciona un enlace para acceder a las credenciales de seguridad actuales (públicas). Su uso elimina la necesidad de codificar directamente dichas credenciales en la aplicación. Esto también ayuda con la rotación de claves; los proveedores pueden cambiar las claves utilizadas de vez en cuando y, por lo tanto, garantizar una clave actualizada es importante.

Obtener las claves es similarmente una simple petición HTTP GET:

m_network->get(request, this, [this](QRestReply &reply) {
    if (reply.isSuccess()) {
        if (auto doc = reply.readJson(); doc && doc->isObject())
            m_jwks = doc; // Use the keys later to verify tokens
    }
});

El conjunto de claves suele contener varias claves. La clave correcta se indica en la cabecera JWT (hay que tener cuidado para que las claves coincidan correctamente, no basta con comprobar el campo de id de clave, kid,).

Punto final OpenID UserInfo

Una forma alternativa de acceder a la información del usuario es utilizar OpenID Connect UserInfo Endpoint, si el proveedor de OIDC lo soporta. La URL para userinfo se encuentra en el campo userinfo_endpoint del documento OpenID Connect Discovery.

El punto final UserInfo no utiliza el token de ID, sino que se accede a él con el token de acceso. El acceso a UserInfo es similar al acceso a cualquier otro recurso con un token de acceso.

Asumiendo que el token de acceso es recibido y establecido por ejemplo por:

QNetworkRequestFactory userInfoApi(url);
userInfoApi.setBearerToken(m_oauth.token().toLatin1());

Entonces acceder a la UserInfo es una petición HTTP GET:

m_network->get(userInfoApi.createRequest(), this, [this](QRestReply &respuesta) { if (reply.isSuccess()) { if(auto doc = reply.readJson(); doc &&  doc->isObject())            qDebug() << doc->object(); // Use the userinfo
    } });

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.