Qt OAuth2 Overview
OAuth2
RFC 6749 OAuth 2.0 defines an authorization framework which enables resource authorization without exposing sensitive user credentials such as passwords.
The OAuth2 framework defines several client types (public and confidential) as well as flows (implicit, authorization code, and several others). For typical Qt applications the client type should be considered as public native application. The public implies that the application isn't trusted to hold secrets, such as passwords, embedded within the shipped binary.
RFC 8252 OAuth 2.0 for Native Apps further defines the best practices for such applications. Among other things, it defines the Authorization Code Flow as the recommended flow. QtNetworkAuth provides a concrete implementation for this flow, and it is also the focus of this documentation.
Qt OAuth2 Classes
QtNetworkAuth provides both concrete and abstract OAuth2 classes. The abstract classes are intended for implementing custom flows, while the concrete classes provide a concrete implementation.
To implement an OAuth2 flow with QtNetworkAuth, two classes are needed:
- A OAuth2 flow implementation class provides the main API, and is the orchestrator of the flow. It usually owns one reply handler. The abstract class is QAbstractOAuth2, and the concrete implementation is QOAuth2AuthorizationCodeFlow.
- A Reply handler class which handles replies from an authorization server. With authorization code flow, these include authorization, access token request, and access token refresh. The results of processing a reply are further handled by the flow class. The reply handler abstract class is QAbstractOAuthReplyHandler, and the concrete classes are QOAuthHttpServerReplyHandler and QOAuthUriSchemeReplyHandler.
Authorization Code Flow
The authorization code flow is the recommended OAuth2 flow for native applications like Qt applications.
The following code snippet provides an example setup:
QOAuth2AuthorizationCodeFlow m_oauth; QOAuthUriSchemeReplyHandler m_handler; m_oauth.setAuthorizationUrl(QUrl("https://some.authorization.service/v3/authorize"_L1)); m_oauth.setAccessTokenUrl(QUrl("https://some.authorization.service/v3/access_token"_L1)); m_oauth.setClientIdentifier("a_client_id"_L1); m_oauth.setScope("read"_L1); 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.my.app:/oauth2redirect"_L1}); m_oauth.setReplyHandler(&m_handler); // Initiate the authorization if (m_handler.listen()) { m_oauth.grant(); }
Stages
The Authorization Code Flow has two main stages: resource authorization (including any necessary user authentication) followed up by an access token request. These are optionally followed by access token usage and access token refreshing. The following figure illustrates these stages:
- In authorization stage, the user is authenticated, and the user authorizes the access to resources. This requires browser interaction by the user.
- After the authorization the received authorization code is used to request an access token, and optionally a refresh token.
- Once the access token is acquired, the application uses it to access the resources of interest. The access token is included in the resource requests, and it is up to the resource server to verify the token's validity. There are several ways to include the token as part of the requests, but including it in the HTTP
Authorization
header is arguably the most common. - Access token refreshing. Access tokens typically expire relatively quickly, say in one hour. If the application received a refresh token in addition to the access token, the refresh token can be used to request a new access token. Refresh tokens are long-lived and applications can persist them to avoid the need for a new authorization stage (and thus another browser interaction).
Details and Customization
OAuth2 flows are dynamic and following the details can be tricky at first. The figure below illustrates the main details of a successful authorization code flow.
For clarity the figure omits some less used signals, but altogether illustrates the details and main customization points. The customization points are the various signals/slots the application can catch (and call), as well as the callback which is settable with QAbstractOAuth::setModifyParametersFunction().
Choosing A Reply Handler
The decision on which reply hander to use, or to implement, is dependent on the redirect_uri used. The redirect_uri
is where the browser is redirected upon concluding the authorization stage.
In the context of native applications, RFC 8252 outlines three main types of URI schemes: loopback
, https
, and private-use.
- Private-use URIs: Can be used if the OS allows an application to register a custom URI scheme. An attempt to open an URL with such custom scheme will open the related native application. See QOAuthUriSchemeReplyHandler.
- HTTPS URIs: Can be used if the OS allows the application to register a custom HTTPS URL. An attempt to open this URL will open the related native application. This scheme is recommended if the OS supports it. See QOAuthUriSchemeReplyHandler.
- Loopback Interfaces: These are commonly used for desktop applications, and applications during development. The QOAuthHttpServerReplyHandler is designed to handle these URIs by setting up a local server to handle the redirection.
The choice depends on several factors such as:
- Redirect URIs supported by the authorization server vendor. The support varies from vendor to vendor, and is often specific to a particular client type and operating system. Also, the support may vary depending on whether the application is published or not.
- Redirect URI schemes supported by the target platform(s).
- Application-specific usability, security, and other requirements.
RFC 8252 recommends using the
https
scheme for security and usability advantages over the other methods.