Skip to content

Add/update OAuth 2.0 flow documentation in IAP #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
315 changes: 287 additions & 28 deletions IAP.tex
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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}
Expand All @@ -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},
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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}

Expand Down Expand Up @@ -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{
Expand All @@ -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}
Expand Down Expand Up @@ -924,7 +1042,148 @@ \subsection{Mandatory authentication with certificates}
</uws:jobs>
\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
Expand Down
Loading