// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only

/*!
\page qt-oauth2-overview.html

\title Qt OAuth2 Overview
\ingroup explanations-networkauth
\brief An overview of QtNetworkAuth OAuth2

\section1 OAuth2

\l {https://datatracker.ietf.org/doc/html/rfc6749}{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
\e {public native} application. The \e {public} implies that the
application isn't trusted to hold secrets, such as passwords, embedded
within the shipped binary.

\l {https://datatracker.ietf.org/doc/html/rfc8252}{RFC 8252 OAuth 2.0 for Native Apps}
further defines the best practices for such applications. Among other things,
it defines the
\l {https://datatracker.ietf.org/doc/html/rfc8252#section-6}{Authorization Code Flow}
as the recommended flow, and therefore QtNetworkAuth provides a concrete
implementation of this flow.

Since Qt 6.9, QtNetworkAuth also provides support for
\l {https://datatracker.ietf.org/doc/html/rfc8628}{RFC 8628 OAuth 2.0 Device Authorization Grant}.
This \e {device flow} is intended for connected devices that have limited input
capabilities, or where using a user-agent or browser is not practical.
Examples of such devices include televisions, media consoles, machine HMIs,
and IoT devices.

The following table highlights the main aspects of the two concrete OAuth2
flows supported by QtNetworkAuth module:

\table
\header
    \li Aspect
    \li Authorization Code Flow
    \li Device Authorization Flow
\row
    \li Network Connection
    \li Yes
    \li Yes
\row
    \li User Interaction
    \li Browser / user-agent on the same device
    \li Browser / user-agent on a different device
\row
    \li Redirect Handling Required
    \li Yes
    \li No
\row
    \li Input Capability On Device
    \li Rich input capabilities
    \li Limited or no input capability
\row
    \li Targets
    \li Desktop and Mobile Apps
    \li TVs, Consoles, HMIs, IoT Devices
\endtable

OAuth2 requires using a user-agent which is typically a browser.
For further information, see \l {Qt OAuth2 Browser Support}.

\section1 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.

QtNetworkAuth has two abstract classes for implementing OAuth2 flows:
\list
    \li An \e {OAuth2 flow implementation} class provides the main API,
        and is the orchestrator of the flow. The abstract class is
        QAbstractOAuth2, and the concrete implementations are
        QOAuth2AuthorizationCodeFlow and QOAuth2DeviceAuthorizationFlow.
    \li A \e {Reply handler} class which handles redirects and replies
        from an authorization server. The reply handler abstract class
        is QAbstractOAuthReplyHandler, and the concrete classes are
        QOAuthHttpServerReplyHandler and QOAuthUriSchemeReplyHandler.
        The main difference between reply handlers is what kind
        of redirects they are designed to handle. QOAuth2AuthorizationCodeFlow
        relies on having a reply handler to handle the redirects,
        whereas QOAuth2DeviceAuthorizationFlow, which is not based on
        redirects, does not use reply handlers.
\endlist

\section1 Authorization Code Flow

The \l {https://datatracker.ietf.org/doc/html/rfc6749#section-1.3.1}{authorization code flow}
is the
\l {https://datatracker.ietf.org/doc/html/rfc8252#section-6}{recommended OAuth2 flow}
for native applications like Qt applications.

The following code snippet provides an example setup:

\snippet src_oauth_replyhandlers_p.h uri-variables
\codeline
\snippet src_oauth_replyhandlers.cpp uri-service-configuration
\codeline
\snippet src_oauth_replyhandlers.cpp uri-oauth-setup
\codeline
\snippet src_oauth_replyhandlers.cpp uri-handler-setup

\section2 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:

\image oauth2-codeflow-stages.webp

\list
    \li In authorization stage, the user is authenticated, and
        the user authorizes the access to resources. This requires browser
        interaction by the user.
    \li After the authorization the received authorization
        code is used to request an access token, and optionally a refresh
        token.
    \li 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.
        \l {https://datatracker.ietf.org/doc/html/rfc6750}{There are several
        ways to include the token as part of the requests}, but
        including it in the \l {https://datatracker.ietf.org/doc/html/rfc6750#section-2.1}
        {HTTP \c Authorization header} is arguably the most common.
    \li 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).
\endlist

\section2 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.

\image oauth2-codeflow-details.webp

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 callbacks which are settable with
\l QAbstractOAuth::setModifyParametersFunction() and
\l QAbstractOAuth2::setNetworkRequestModifier().

\section2 Choosing A Reply Handler

The decision on which reply hander to use, or to implement,
is dependent on the
\l {https://datatracker.ietf.org/doc/html/rfc6749#appendix-A.6}{redirect_uri}
used. The \c redirect_uri is where the browser is redirected upon concluding
the authorization stage.

In the context of native applications,
\l {https://datatracker.ietf.org/doc/html/rfc8252#section-7}
{RFC 8252 outlines three main types of URI schemes}:
\c loopback, \c https, and private-use.

\list
    \li \l{https://datatracker.ietf.org/doc/html/rfc8252#section-7.1}{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 \l QOAuthUriSchemeReplyHandler.
    \li \l{https://datatracker.ietf.org/doc/html/rfc8252#section-7.2}{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 \l QOAuthUriSchemeReplyHandler.
    \li \l{https://datatracker.ietf.org/doc/html/rfc8252#section-7.3}{Loopback Interfaces}:
        These are commonly used for desktop applications, and applications
        during development. The \l QOAuthHttpServerReplyHandler is designed to
        handle these URIs by setting up a local server to handle the
        redirection.
\endlist

The choice depends on several factors such as:
\list
    \li 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.
    \li Redirect URI schemes supported by the target platform(s).
    \li Application-specific usability, security, and other requirements.
\endlist

\quotation \l {https://datatracker.ietf.org/doc/html/rfc8252#section-7.2}
 {RFC 8252 recommends using the \c https scheme} for
security and usability advantages over the other methods.
\endquotation

\section1 Device Authorization Flow

The Device Authorization Flow is intended for connected devices that are
limited in terms of input capabilities, or where user-agent/browser usage
is not practical.

The following code snippet provides an example setup:
\snippet src_oauth_replyhandlers.cpp deviceflow-setup
\codeline
\snippet src_oauth_replyhandlers.cpp deviceflow-handle-authorizewithusercode
\codeline
\snippet src_oauth_replyhandlers.cpp deviceflow-handle-grant

\section2 Stages

Device Authorization Flow has three main stages: initializing
the authorization, polling for tokens, and completing the authorization.
These are optionally followed by token usage and token refreshing.
The following figure illustrates these stages:

\image oauth2-deviceflow-stages.webp

\list
    \li Authorization is initialized by sending a HTTP request to
        the authorization server. The authorization server provides a user
        code, verification URL(s), and a device code in response.
    \li After authorization is initialized, user is provided with
        a user code and verification URL(s) for completing the authorization.
        Providing the information for the user is
        \l {https://datatracker.ietf.org/doc/html/rfc8628#section-3.3}{use-case specific}:
        it can be a visible URL on the screen, QR code, an email, and so on.
    \li While waiting for a user to complete the authorization, the device flow
        polls the authorization server for tokens. The device code
        received in the previous step is used to match the authorization
        session. The poll interval is decided by the authorization server,
        and is typically 5 seconds.
    \li Once user has accepted (or denied) the authorization, the
        authorization server responds to a poll request with the requested
        tokens or an error code (in case of denial), and the authorization is
        complete.
\endlist

\section2 Details and Customization

The following figure illustrates the flow in more detail. The figure
illustrates also the main customization points, which may sometimes
be needed (for instance proprietary parameters, or additional
authentication credentials).

\image oauth2-deviceflow-details.webp

\section1 Refreshing Tokens

A full OAuth2 flow requires user interaction, which can be intrusive
to the user experience. To minimize these interactions,
tokens can be
\l {https://datatracker.ietf.org/doc/html/rfc6749#section-6}
{silently refreshed} from user's perspective.

Refreshing tokens requires that the authorization server provide a
\l {https://datatracker.ietf.org/doc/html/rfc6749#section-1.5}{refresh token}
during authorization. Providing a refresh token is up to the
authorization server: some servers provide it always, some never,
and some provide it if a specific
\l {QAbstractOAuth2::requestedScopeTokens}{scope} was present in the
authorization request.

The following figure illustrates the token refresh in more detail:

\image oauth2-refresh-details.webp

As shown in the figure above, the usual customization points
are also available when refreshing tokens.

To refresh the tokens after an application startup,
the application needs to persist the refresh token securely,
and set it with \l {QAbstractOAuth2::setRefreshToken()}.
\l QAbstractOAuth2::refreshTokens() can
then be called to request new tokens.

Since Qt 6.9, applications can also use refresh convenience
functionality to automatically refresh the tokens - see
\l {QAbstractOAuth2::accessTokenAboutToExpire()},
\l {QAbstractOAuth2::autoRefresh}, and
\l {QAbstractOAuth2::refreshLeadTime}.

The expiration time for a refresh token is generally not indicated by
the authorization server (apart from the server's documentation).
Their validity can range from days to months, or longer. Furthermore,
as with other tokens, they can be revoked by the user and thus
invalidated at any time. Therefore, it is important to properly detect a failed
refresh attempt with
\l {QAbstractOAuth::requestFailed()} or
\l {QAbstractOAuth2::serverReportedErrorOccurred()}.

\section1 Qt OpenID Connect Support

OpenID Connect (OIDC) is a
\l {https://openid.net/specs/openid-connect-core-1_0-final.html}
{simple identity layer on top of OAuth2 protocol}. Where authorization
provides means to authorize users to perform actions, OIDC enables
establishing a trusted identity of a user.

Qt's support for OIDC is at the moment limited to getting
\l {https://openid.net/specs/openid-connect-core-1_0-final.html#Terminology}{ID tokens}.
An \c {ID token} is a
\l {https://datatracker.ietf.org/doc/html/rfc7519}{JSON Web Token (JWT)}
that contains claims about the authentication event.

Notably the support for \c {ID token} validation or \c {ID token}
decryption is currently not implemented.

Assuming the application is able to \l {Validating an ID Token}{validate}
the received tokens, the token can be used to establish
the identity of the user reliably (to the degree the OIDC provider itself
is trusted).

ID tokens are sensitive information and should be kept as a secret.
ID tokens are not intended for sending out in API
calls - the access token is intended for that purpose. Note that some
vendors may use the same
\l {https://datatracker.ietf.org/doc/html/rfc7519}{JWT format} for access
tokens, but that is not to be confused with actual ID tokens which
incidentally use the same format. With ID tokens the client receiving
the token is responsible for verifying the token, whereas with access
tokens it's the resource server accepting the token that is responsible
for verification.

\section2 Getting an ID Token

Getting an ID token is very similar to getting an access token.
First we need to set the appropriate scope. Authorization Server
vendor may support additional scope specifiers such as \c {profile}
and \c {email}, but all OIDC requests must include \c {openid} scope:

\snippet src_oauth_replyhandlers.cpp oidc-setting-scope

For OIDC it is strongly recommended to use
\l {QAbstractOAuth2::nonce}{nonce} parameter.
This is done by ensuring that appropriate
\l {QAbstractOAuth2::NonceMode}{NonceMode} is set.

\snippet src_oauth_replyhandlers.cpp oidc-setting-nonce-mode

As last step we can listen for either \l {QAbstractOAuth2::granted}
signal or the \l {QAbstractOAuth2::idTokenChanged} directly:

\snippet src_oauth_replyhandlers.cpp oidc-listen-idtoken-change

\section2 Validating an ID Token

Validating the received ID Token is a crucial part of the
flow, and,
\l {https://openid.net/specs/openid-connect-core-1_0-final.html#IDTokenValidation}
{when fully implemented, a somewhat complicated task}.

At its outline validation consists of these steps
\list
    \li Decrypting the token if needed
        (\l {https://datatracker.ietf.org/doc/html/rfc7516}{see JWE})
    \li Extracting the token header, payload, and signature
    \li Validating the signature
    \li Validating the fields of the payload
        (such as \c {aud, iss, exp, nonce, iat})
\endlist

Qt currently doesn't provide support for ID token validation,
but there are
\l {https://jwt.io/libraries}{several C++ library options available},
such as
\l {https://github.com/Thalhammer/jwt-cpp}{jwt-cpp}.

\section3 ID Token Verification Example

This section illustrates a simple verification with the help of
\l {https://github.com/Thalhammer/jwt-cpp}{jwt-cpp} library.
As prerequisites, the development environment needs to have
\l {https://www.openssl.org/}{OpenSSL} libraries, and
\l {https://github.com/Thalhammer/jwt-cpp}{jwt-cpp} include
folder under the application project's source directory.

In application project's \c {CMakeLists.txt} we first check
that the prerequisities are met:
\snippet ../snippets/CMakeLists.txt oidc-jwt-cpp-available-cmake

Then we add necessary includes and libraries:
\snippet ../snippets/CMakeLists.txt oidc-jwt-cpp-link-and-include-cmake

In the application source files, include the verification library:
\snippet src_oauth_replyhandlers.cpp oidc-jwt-cpp-include

Once the application receives an \c {ID token}, it's time to verify it.
First we find a matching key from JSON Web Key Sets (JWKS, see
\l {OpenID Connect Discovery}).
\snippet src_oauth_replyhandlers.cpp oidc-jwt-cpp-key-matching

And then we do the actual verification:
\snippet src_oauth_replyhandlers.cpp oidc-jwt-cpp-verification

It is recommended to check up-to-date documentation and examples
of the library being used, and also to familiarize with
\l {https://openid.net/specs/openid-connect-core-1_0-final.html#IDTokenValidation}
{ID token verification}.

\section2 Reading ID Token Values

The ID token is in
\l {https://datatracker.ietf.org/doc/html/rfc7519}{JSON Web Token (JWT)}
format and consists of a header, payload, and signature parts,
separated by dots \{'.'}.

Reading the values of the ID token is straightforward. As an example,
assuming a struct:

\snippet src_oauth_replyhandlers_p.h oidc-id-token-struct

And a function:
\snippet src_oauth_replyhandlers_p.h oidc-id-token-parser-declaration

The token can be extracted:
\snippet src_oauth_replyhandlers.cpp oidc-id-token-parsing

In more rare cases the token may be encrypted with
\l {https://datatracker.ietf.org/doc/html/rfc7516}{JSON Web Encryption (JWE)},
which internally contains a JWT token. In this case the token must
be decrypted first.

\section2 OpenID Connect Discovery

\l {https://openid.net/specs/openid-connect-discovery-1_0.html}
{OpenID Connect Discovery} defines means to discover needed
OpenID provider details, in order to interact with it. This includes
things such as \c {authorization_endpoint} and \c {token_endpoint} URLs.

While these provider details can be statically configured in the application,
discovering the details at runtime may provide more flexibility and
robustness in interacting with various providers.

Getting the discovery document is a simple \c {HTTP GET} request.
The document is typically located in
\c {https://<the-domain eg. example.com>/.well-known/openid_configuration}

\snippet src_oauth_replyhandlers.cpp oidc-get-openid-configuration

Notably, for token validation, the
\l {https://datatracker.ietf.org/doc/html/rfc7517#section-5}{jwks_uri} field
provides a link for accessing the current (public) security credentials.
Using that removes the need to hardcode such credentials in the application
directly. This also helps with key rotation; the vendors may change the used
keys from time to time, and therefore ensuring an up-to-date key is important.

Getting the keys is similarly a simple \c {HTTP GET} request:

\snippet src_oauth_replyhandlers.cpp oidc-get-jwks-keys

The key set typically contains several keys. The correct key is indicated
in the JWT header (care must be taken to match the keys properly, just
checking the key id (\c {kid}) field is not adequate).

\section2 OpenID UserInfo Endpoint

An alternative way to access user information is to use
\l {https://openid.net/specs/openid-connect-core-1_0-final.html#UserInfo}
{OpenID UserInfo Endpoint}, if the OIDC provider supports it. The URL
for the userinfo is in \c {userinfo_endpoint} field of the
\l {OpenID Connect Discovery} document.

The userinfo endpoint does not use the ID token, but is
accessed with the access token. Accessing the userinfo is similar
to accessing any other resource with an access token.

Assuming the access token is received and set for example by:

\snippet src_oauth_replyhandlers.cpp oidc-set-bearertoken

Then accessing the userinfo is a \c {HTTP GET} request:

\snippet src_oauth_replyhandlers.cpp oidc-read-userinfo

*/
