diff --git a/IAP.tex b/IAP.tex index af87dd3..a5026f4 100644 --- a/IAP.tex +++ b/IAP.tex @@ -149,7 +149,7 @@ \section{Introduction}\label{sec:intro} authentication frameworks to any ``VO-sanctioned'' technology, but by implementing the proposals here they can become usable in a broader range of scenarios. -VO services however are not required to use any of the +VO services however are not required to use any of the mechanisms proposed in this document; if they can establish the client interoperability they require using other mechanisms, or a combination of VO and other mechansisms, they are free to do so. @@ -232,7 +232,7 @@ \subsection{Terminology} \section{Challenge and Response} -The standard way to negotiate authentication over HTTP is using +The standard way to negotiate authentication over HTTP is using authentication challenges supplied in HTTP responses. \subsection{Challenge/Response Framework} @@ -246,7 +246,7 @@ \subsection{Challenge/Response Framework} the basics are outlined here: % 401, 403, WWW-Authenticate, maybe Authorization. \begin{itemize} - \item An HTTP response may include one or more + \item An HTTP response may include one or more \header{WWW-Authenticate} headers to indicate that authentication is possible. The content of these headers is one or more {\em challenges}, @@ -307,7 +307,10 @@ \subsection{Authentication Schemes in the VO} but the challenge provides no information about how to acquire such a token which means it is not suitable for clients lacking prior knowledge about the target service, as described in -Section~\ref{sec:intro}. +Section~\ref{sec:intro}. In order to allow those connections, the +{\em bearer token} response should provide the service configuration +metadata, usually contained in the \verb|discovery_url|. +See \ref{sec:ivoa-bearer}. Other methods of authentication over HTTP also exist and are used by VO services, @@ -333,7 +336,7 @@ \section{Authentication Schemes}\label{sec:authschemes} this is communicated by the scheme-specific parameters \end{enumerate} Acquiring a permit (such as a cookie, certificate or token) -typically requires supplying credentials known to the user +typically requires supplying credentials known to the user (such as a username and password) in a particular way to a particular endpoint. Common parameters describing this activity are given in @@ -486,6 +489,127 @@ \subsubsection{\mbox{\tt ivoa\_x509}}\label{sec:ivoa-x509} There is an example in Section~\ref{sec:x509-example}. +\subsubsection{\mbox{\tt ivoa\_bearer}}\label{sec:ivoa-bearer} + +The \verb|ivoa_bearer| authentication scheme enables Virtual Observatory (VO) services to request Bearer tokens +for secure access control. This approach is aligned with OAuth 2.0 \citep{rfc6749} and OpenID Connect \citep{openid}, +offering a standards-compliant and interoperable solution for clients and services. + +This scheme supports authentication for headless clients (e.g., command-line tools or scripts) via the +Device Authorization Flow \citep{rfc8628}, enabling login through a secondary browser-enabled device. + +It also supports token expiry and renewal via refresh tokens, and integrates with OAuth2-compatible infrastructures +such as Keycloak-based IAM services. + +\begin{description} +\item{{Scheme name:} \verb|ivoa_bearer|} +\item{\textbf{Parameters:} +\begin{itemize} + \item \verb|discovery_url| (required): URL to a discovery document following RFC 8414, providing metadata such as supported grant types and endpoints. + \item \verb|standard_id| (required): Must be \verb|ivo://ivoa.net/sso#OAuth|, indicating use of the IVOA OAuth2 profile. + \item \verb|error| (optional): OAuth 2.0-compatible error code, e.g., \verb|invalid_request|, \verb|invalid_token|, or \verb|expired_token|. + \item \verb|error_description| (optional): Human-readable explanation of the error condition. +\end{itemize}} +\end{description} + +When a protected resource is accessed without valid authentication, the service responds with: + +\begin{verbatim} +HTTP/1.1 401 Unauthorized +WWW-Authenticate: ivoa_bearer \ + standard_id="ivo://ivoa.net/sso#OAuth", \ + discovery_url="https://auth.example.org/.well-known/openid-configuration", \ + error="missing_token", \ + error_description="Authentication required" +\end{verbatim} + +The client MUST retrieve and parse the discovery document. +Here there is an example of a \verb|discovery_url|: +\begin{verbatim} +{ + "issuer": "https://auth.example.org", + "token_endpoint": "https://auth.example.org/oauth/token", + "device_authorization_endpoint": "https://auth.example.org/oauth/device", + "grant_types_supported": [ + "refresh_token", + "urn:ietf:params:oauth:grant-type:device_code" + ], + "scopes_supported": ["openid", "email", "profile"] +} +\end{verbatim} + +Possible error codes are standardised on oAuth. A typical list of possible codes can +be found in Table \ref{table:oAuthCodes}. +\begin{table}[] +\centering +\begin{tabular}{ll} +\textbf{Error Code} & \textbf{Description} \\ +\verb|invalid_request| & {Request is missing required parameters or malformed.} \\ +\verb|unauthorized_client| & {The client is not allowed to use this grant type.} \\ +\verb|access_denied| & {The resource owner denied the request.} \\ +\verb|unsupported_response_type| & {Response type is not supported by the server.} \\ +\verb|invalid_scope| & {The requested scope is invalid or unknown.} \\ +\verb|invalid_token| & {The token is expired, malformed, or invalid.} \\ +\verb|expired_token| & {The token has expired and must be refreshed.} \\ +\end{tabular} +\caption{Standard OAuth 2.0 Error Codes (RFC 6749)} +\label{table:oAuthCodes} +\end{table} + + +\begin{description} +\item{\textbf{Token acquisition:} + +Clients MUST use the Device Authorization Flow. This involves requesting a device code from the +\verb|device_authorization_endpoint|, prompting the user to authenticate via a browser, and polling the +\verb|token_endpoint| for completion. + +Upon success, the client receives: + +\begin{verbatim} +{ + "access_token": "abc123", + "token_type": "Bearer", + "expires_in": 3600, + "refresh_token": "xyz789" +} +\end{verbatim}} + +\item{{Using Tokens:} Access tokens must be included in the HTTP \texttt{Authorization} header: + +\begin{verbatim} +Authorization: Bearer abc123 +\end{verbatim}} + +\item{\textbf{Token Expiry and Refresh:} + +When an access token expires, the server may respond: + +\begin{verbatim} +HTTP/1.1 401 Unauthorized +WWW-Authenticate: ivoa_bearer standard_id="ivo://ivoa.net/sso#OAuth" +access_url="https://auth.example.org/.well-known/openid-configuration" +X-VO-Auth-Error: expired_token +\end{verbatim} + +The client SHOULD use the refresh token to obtain a new access token: + +\begin{verbatim} +curl -X POST https://auth.example.org/oauth/token \ + -d 'grant_type=refresh_token' \ + -d 'refresh_token=xyz789' \ + -d 'client_id=my-client-id' +\end{verbatim} + +\textbf{Note 1:} Refresh tokens must be stored securely and must not be exposed.\\ +\textbf{Note 2:} The \verb|client_id| must correspond to a pre-registered application, +approved and controlled by IVOA. These \verb|client_id|s are pre-registered into the +IAM services by administrators. + +} +\end{description} + + \subsection{Common Challenge Parameters for VO Schemes} \label{sec:common-params} @@ -527,6 +651,23 @@ \subsubsection{\mbox{\tt standard\_id}} These items are passed as the values of parameters ``{\tt username}'' and ``{\tt password}'' respectively, transmitted in {\tt application/x-www-form-urlencoded} format. + + \item[{\tt ivo://ivoa.net/sso\#OAuth}] + This value indicates that the service requires OAuth 2.0-based authentication + using a Bearer token. The specific login mechanism is not directly specified by + this identifier and MUST be dynamically discovered via the discovery URL provided in the + \verb|access_url| parameter of the \texttt{WWW-Authenticate} challenge. Clients are expected + to follow the metadata discovery process defined in RFC 8414~\citep{rfc8414} to determine the supported + endpoints and authorisation server configuration. + + In this profile, only the Device Authorization Flow, as defined in RFC 8628~\citep{rfc8628}, + is supported. This enables headless or browserless clients—such as command-line tools or scripts— + to prompt the user to authenticate on a separate device with a browser. + + Access and refresh tokens are acquired through the device and token endpoints + defined in the discovery document and must be included in subsequent requests + using the HTTP \texttt{Authorization} header with the Bearer scheme. + \end{description} \todo{ @@ -538,29 +679,6 @@ \subsubsection{\mbox{\tt standard\_id}} } -\subsection{Bearer Tokens} - -Bearer Tokens form the permit for -the OAuth2 authorization framework, -% Terminology: it's called an "Authorization Framework" -% in the title of \rfc{6749} and \rfc{6750}. -which is used by a number of VO data providers. -A bearer token is an opaque string; -in order to use one, the client simply presents the token -following the keyword ``{\tt Bearer}'' -in an \header{Authorization} HTTP request header, -as described by \rfc{6750}. - -Various methods of token acquisition are defined by OAuth2 and -associated standards, but at time of writing it's not clear which if -any of these are suitable for use by clients lacking prior knowledge -of the services for which they are intended, -and no standard scoping mechanisms seem to be defined. - -This document does not therefore currently recommend any way in -which non-browser VO clients can use Bearer Tokens, -but it is hoped that progress will be made on this in future. - \section{Challenge/Response Use in the VO} @@ -924,7 +1042,148 @@ \subsection{Mandatory authentication with certificates} \end{verbatim} } +\subsection{OAuth Device Code Flow (RFC 8628)} +\label{sec:ivoa-bearer-example} + +Command-line tools, batch scripts, or other headless clients often lack access to a browser, making typical +OAuth flows like the Authorization Code Grant (which requires user interaction) unsuitable. The Device +Authorization Grant (Denniss and Bradley, 2019) enables these clients to initiate the authentication process, +prompting the user to complete login in a browser on a separate device. + +This flow is particularly useful for environments where the client does not have graphical capabilities or where +authentication must occur externally (e.g., via a mobile device or desktop browser). + +\subsubsection{Before Execution} + +\begin{description} +\item{\textbf{Receive Unauthorized Error}} + +When an unauthenticated client attempts to access a protected resource, the server responds with: + +\begin{verbatim} +HTTP/1.1 401 Unauthorized +WWW-Authenticate: ivoa_bearer \ + error="invalid_request", \ + error_description="Missing access token", \ + standard_id="ivo://ivoa.net/sso#OAuth", \ + discovery_url="https://auth.example.org/.well-known/openid-configuration" +\end{verbatim} + +This indicates that a Bearer token is required. The client \textbf{MUST} fetch and parse the OpenID Connect Discovery +document located at the \verb|discovery_url|. + +\item{\textbf{Extract Relevant Metadata}} + +From the discovery document \\ +(\texttt{https://auth.example.org/.well-known/openid-configuration}), \\ +the client retrieves the following fields: + +\begin{itemize} + \item{\verb|device_authorization_endpoint| — used to initiate the device authorisation flow.} + \item{\verb|token_endpoint| — used to poll for access tokens.} + \item{\verb|verification_uri| — the base URL shown to the user.} + \item{\verb|verification_uri_complete| — a direct link including the user code.} + \item{\verb|grant_types_supported| — must include \\ + \verb|urn:ietf:params:oauth:grant-type:device_code|.} + \item{\verb|scopes_supported| — typically includes \verb|openid|, \verb|profile|, \verb|offline_access|.} +\end{itemize} + +Example metadata: + +\begin{verbatim} +{ + "device_authorization_endpoint": + "https://auth.example.org/oauth/device", + "token_endpoint": "https://auth.example.org/oauth/token", + "verification_uri": "https://auth.example.org/activate", + "verification_uri_complete": + "https://auth.example.org/activate?user_code=ABC-123", + "grant_types_supported": [ + "urn:ietf:params:oauth:grant-type:device_code", + "refresh_token" + ], + "scopes_supported": ["openid", "profile", "offline_access"] +} +\end{verbatim} + +\item{\textbf{Request Device Code}} + +The client initiates the device flow: + +\begin{verbatim} +curl -X POST https://auth.example.org/oauth/device \ + -d 'client_id=my-client-id&scope=openid profile' +\end{verbatim} + +This URL is obtained from \verb|device_authorization_endpoint|. + +\item{\textbf{Prompt User to Authenticate}} + +The client displays a message like: + +\begin{verbatim} +Please open +https://auth.example.org/activate +in a browser and enter the code: ABC-123 +\end{verbatim} + +Alternatively, the complete link from \verb|verification_uri_complete| can be shown directly. +\item{\textbf{Poll for Token}} + +The client polls the token endpoint (from \verb|token_endpoint|): + +\begin{verbatim} +curl -X POST https://auth.example.org/oauth/token \ + -d 'grant_type=urn:ietf:params:oauth:grant-type:device_code' \ + -d 'device_code=abc123' \ + -d 'client_id=my-client-id' +\end{verbatim} + +\item{\textbf{Receive Tokens}} + +After successful user authentication, the client receives: + +\begin{verbatim} +{ + "access_token": "SlAV32hkKG", + "token_type": "Bearer", + "expires_in": 3600, + "refresh_token": "xyz456" +} +\end{verbatim} + +The tokens should be stored securely for future requests. + +\item{\textbf{Use the Token}} + +The client accesses protected resources by including the access token in the \verb|Authorization| header: + +\begin{verbatim} +GET /vo-resource +Authorization: Bearer SlAV32hkKG +\end{verbatim} + +\end{description} + +\subsubsection{During Execution} + +\begin{description} +\item{\textbf{Token Refresh}} + +To maintain access for long-running jobs or recurring tasks, the client refreshes the token using: + +\begin{verbatim} +curl -X POST https://auth.example.org/oauth/token \ + -d 'grant_type=refresh_token' \ + -d 'refresh_token=xyz456' \ + -d 'client_id=my-client-id' +\end{verbatim} + +This request is also sent to the \verb|token_endpoint|. + +\textbf{Note:} Refresh tokens must be stored securely. If compromised, they provide persistent access to user resources. +\end{description} \appendix diff --git a/localrefs.bib b/localrefs.bib index bab1f85..70dfed0 100644 --- a/localrefs.bib +++ b/localrefs.bib @@ -59,4 +59,49 @@ @Misc{std:RFC9110 year = 2022 } +@misc{rfc6749, + author = {D. Hardt}, + title = {{The OAuth 2.0 Authorization Framework}}, + howpublished = {RFC 6749}, + year = {2012}, + url = {https://www.rfc-editor.org/rfc/rfc6749}, + note = {\url{https://www.rfc-editor.org/rfc/rfc6749}} +} + +@misc{rfc8414, + author = {J. Jones and N. Sakimura}, + title = {{OAuth 2.0 Authorization Server Metadata}}, + howpublished = {RFC 8414}, + year = {2018}, + url = {https://www.rfc-editor.org/rfc/rfc8414}, + note = {\url{https://www.rfc-editor.org/rfc/rfc8414}} +} + +@misc{rfc9728, + author = {Aaron Parecki and Dick Hardt}, + title = {{OAuth 2.0 Protected Resource Metadata}}, + howpublished = {RFC 9728}, + year = {2024}, + url = {https://www.rfc-editor.org/rfc/rfc9728}, + note = {\url{https://www.rfc-editor.org/rfc/rfc9728}} +} + +@misc{rfc8628, + author = {W. Denniss and J. Bradley}, + title = {{OAuth 2.0 Device Authorization Grant}}, + howpublished = {RFC 8628}, + year = {2019}, + url = {https://www.rfc-editor.org/rfc/rfc8628}, + note = {\url{https://www.rfc-editor.org/rfc/rfc8628}} +} + +@misc{rfc8252, + author = {W. Denniss and B. Campbell}, + title = {{OAuth 2.0 for Native Apps}}, + howpublished = {RFC 8252}, + year = {2017}, + url = {https://www.rfc-editor.org/rfc/rfc8252}, + note = {\url{https://www.rfc-editor.org/rfc/rfc8252}} +} +