From 50c5ae0f9c7aee1fb1e67ade68d357f9d7e49e3c Mon Sep 17 00:00:00 2001 From: Jesus Salgado Date: Fri, 13 Jun 2025 11:35:22 +0200 Subject: [PATCH 1/9] Add/update OAuth 2.0 flow documentation in IAP --- IAP.tex | 317 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 290 insertions(+), 27 deletions(-) diff --git a/IAP.tex b/IAP.tex index af87dd3..3b3d79e 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}, @@ -333,7 +333,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 @@ -485,6 +485,106 @@ \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 allows VO services to request authentication via Bearer tokens, +aligning with OAuth 2.0~\citep{rfc6749} and OpenID Connect standards. This scheme enables secure, +standards-compliant authentication for VO clients and applications, while preserving interoperability with +existing OAuth2 infrastructure and libraries. + +This approach is compatible with automated clients, desktop applications, and headless environments, and +supports token expiration, refresh workflows, and secure origin scoping via the \texttt{allowed\_origins} +field in token responses. + +\begin{description} + \item[Scheme name:] \verb|ivoa_bearer| + \item[Parameters:] \mbox{} + \begin{itemize} + \item \texttt{access\_url} (required): URL pointing to a discovery document that conforms to RFC 8414~\citep{rfc8414} . This document provides the metadata required for client configuration, including authorisation endpoints and other metadata. + \item \texttt{X-VO-Auth-Error} (optional): VO-specific error header. Examples include \texttt{expired\_token}, \texttt{invalid\_token}, or \texttt{invalid\_token\_type}. + \end{itemize} +\end{description} + +When a protected resource is accessed without valid authentication, the service MUST respond: + +\begin{verbatim} +HTTP/1.1 401 Unauthorized +WWW-Authenticate: ivoa_bearer + access_url="https://auth.example.org/.well-known/openid-configuration" +X-VO-Auth-Error: invalid_token_type +\end{verbatim} + +In order to continue the authorisation process, clients MUST fetch and parse the discovery document. +An example response is at follows: + +\begin{verbatim} +{ + "issuer": "https://auth.example.org", + "token_endpoint": "https://auth.example.org/oauth/token", + "authorization_endpoint": "https://auth.example.org/oauth/authorize", + "device_authorization_endpoint": "https://auth.example.org/oauth/device", + "registration_endpoint": "https://auth.example.org/oauth/register", + "grant_types_supported": [ + "authorization_code", + "refresh_token", + "urn:ietf:params:oauth:grant-type:device_code" + ], + "scopes_supported": ["openid", "email", "profile"], + "token_endpoint_auth_methods_supported": ["client_secret_post", "none"] +} +\end{verbatim} + +Once authorised, an authentication response should be provided as follows: + +\begin{verbatim} +{ + "access_token": "abc123", + "token_type": "Bearer", + "expires_in": 3600, + "refresh_token": "xyz789", + "allowed_origins": ["https://example.org", "https://data.example.org"] +} +\end{verbatim} + +\begin{itemize} + \item \texttt{allowed\_origins} (recommended): HTTP origins where the token may be used (legacy support). Clients MUST NOT use tokens elsewhere. +\end{itemize} + +If \texttt{allowed\_origins} is not present, service MUST support RFC 9728~\citep{rfc9728}. In this case, clients continue +discovery by requesting: + +\begin{verbatim} +GET /.well-known/oauth-authorization-server +\end{verbatim} + +This document, always present at a location relative to the issuer URL, produces a response like: + +\begin{verbatim} +{ + authorization_servers: ["https://auth.example.org"], + resource: "https://data.example.org" +} +\end{verbatim} + +where \texttt{authorization\_servers} is replacing the logic described by \texttt{allowed\_origins} for legacy services. + + +\textbf{Permit Usage} +Include the access token in the HTTP request header: +\begin{verbatim} +Authorization: Bearer abc123 +\end{verbatim} + +\textbf{Token Expiry and Refresh} +When expired: +\begin{verbatim} +HTTP/1.1 401 Unauthorized +WWW-Authenticate: ivoa_bearer + access_url="https://auth.example.org/.well-known/openid-configuration" +X-VO-Auth-Error: expired_token +\end{verbatim} + +Clients SHOULD use the \texttt{refresh\_token} if available. + \subsection{Common Challenge Parameters for VO Schemes} \label{sec:common-params} @@ -527,6 +627,19 @@ \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 exact login mechanism is not directly specified by + this identifier and MUST be discovered dynamically via a discovery URL provided at the + \texttt{service\_url} + parameter included in the \texttt{WWW-Authenticate} challenge. Clients are expected + to follow the metadata discovery process defined in RFC 8414~\citep{rfc8414} and, + if applicable, RFC 9728~\citep{rfc9728} to determine the supported grant types and endpoints. + The OAuth 2.0 flows most relevant to VO usage include the Device Authorization Grant (RFC 8628) + and the Authorization Code Grant for native apps (RFC 8252). Access and refresh tokens are acquired + through standard OAuth2 token endpoints and used in the Authorization header via the Bearer scheme. + \end{description} \todo{ @@ -538,29 +651,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} @@ -925,7 +1015,180 @@ \subsection{Mandatory authentication with certificates} \end{verbatim} } +\subsection{OAuth Device Code Flow (RFC 8628)} + +Headless clients such as command-line tools, HPC batch jobs, or remote scripts often lack access +to a local browser or a graphical user interface. Traditional OAuth flows like the Authorization +Code Grant (RFC 8252) assume user interaction through a browser, which may not be available or secure +in non-interactive environments. The Device Code Flow~\citep{rfc8628} is specifically designed for these cases: +it allows the client to initiate authentication by displaying a short code to the user, who completes the login +process in a browser on another device. This flow separates the user interaction from the headless environment, +enabling secure, standards-compliant authentication without requiring embedded browsers, cookies, or redirect handling. + +\subsubsection{Before execution} +\begin{enumerate} +\item{\textbf{Receive Unauthorised error:} The client attempts to access a protected resource and receives the following +HTTP response: +\begin{verbatim} +HTTP/1.1 401 Unauthorized +WWW-Authenticate: ivoa_bearer +service_url="https://auth.example.org/.well-known/openid-configuration" +X-VO-Auth-Error: missing_token +\end{verbatim} +This response tells the client that a Bearer token is required and provides a discovery URL for discovering the +authorisation server's metadata. The client MUST fetch and parse this discovery document (per [RFC 8414]) to continue. +From the discovery metadata, the client identifies among others: +\begin{itemize} +\item \verb|device_authorization_endpoint|: the URL to initiate device login +\item \verb|token_endpoint|: where the client polls and refreshes tokens +\item{\verb|grant_types_supported|: confirms support for device code flow (urn:ietf:params:oauth:grant-type:device\_code). +Also, \verb|refresh_token| for token refreshing.} +\item \verb|scopes_supported|: available scopes (e.g., openid for user authentication or offline\_access to request refresh tokens) +\end{itemize}} + +\item{\textbf{Request device code:} The client sends a request to the authorisation server \verb|device_authorization_endpoint| +to obtain a device code and user code for authentication. +\begin{verbatim} +curl -X POST https://auth.example.org/oauth/device + -d 'client_id=my-cli-client&scope=openid profile' +\end{verbatim}} +The response will contain, among others, metadata of a \verb|user_code|, a \verb|device_code| and a \verb|verification_uri| + +\item{\textbf{Prompt to user:} The application parses the response and displays a message instructing the user to visit a + \verb|verification_uri| and enter the \verb|user_code| on a device with browser access. +\begin{verbatim} +echo "Visit https://auth.example.org/activate and + enter code ABC-123" +\end{verbatim}} + +\item{\textbf{Poll for access token:} The client periodically polls the \verb|token_endpoint| +to check if the user completed the authorisation, sending the \verb|client_id| and \verb|device_code| +\begin{verbatim} +curl -X POST https://auth.example.org/oauth/token + -d 'grant_type=urn:ietf:params:oauth:grant-type:device_code& + device_code=abc123&client_id=my-cli-client' +\end{verbatim}} + +\item{\textbf{Authorisation success:} On authorisation success, a JSON document is received with an \verb|access_token| +and a \verb|refresh_token|} + +\item{\textbf{Use access token:} Operation execution can be invoked using the \verb|access_token| +\begin{verbatim} +GET /vo-resource +Authorization: Bearer SlAV32hkKG +\end{verbatim}} +\end{enumerate} + +\subsubsection{During execution} +\begin{enumerate} +\item{\textbf{Secure automation via refresh token} +For recurring or non-interactive workflows, the client may store the \verb|refresh_token| and later use it to obtain new access tokens: +\begin{verbatim} +curl -X POST https://auth.example.org/oauth/token + -d 'grant_type=refresh_token& + refresh_token=xyz456&client_id=my-cli-client' +\end{verbatim}} +\end{enumerate} + +This enables: +\begin{itemize} +\item Scheduled access (e.g., cron jobs) +\item HPC workflows +\item Re-authentication without re-prompting the user +\end{itemize} + +\textit{Warning! Refresh tokens must be stored securely. If compromised, they allow long-term access to the user’s identity.} + + +\subsection{OAuth Authorization Code Flow (RFC 8252)} +Desktop applications, such as rich GUI tools, typically have access to a local web browser and +can listen on loopback interfaces (e.g., \texttt{localhost}) for redirects. The Authorization Code Flow~\citep{rfc8252} +is the recommended method for these applications because it allows secure delegation of authentication to the user's +preferred identity provider. It involves redirecting the user to a browser for login, after which an authorisation code +is returned and exchanged for an access token. This flow ensures that sensitive credentials never pass through the client +application directly, improves compatibility with federated identity providers, and supports refresh tokens and fine-grained +scope control. +\begin{enumerate} + \item{}\textbf{Obtain the authorisation endpoint after error:} After authorisation error (see previous section), discover the \verb|authorization_endpoint| + using the \verb|openid-configuration| URI. For this flow, the grant type supported in the \verb|grant_types_supported| list MUST + be \verb|authorization_code| for first authorisation and \verb|refresh_token| for token refreshing. + \item{\textbf{Open browser:} The application launches the user’s browser using the \verb|authorization_endpoint| URL that begins the authorisation process + \begin{verbatim} + open https://auth.example.com/authorize? + response_type=code& + client_id=YOUR_CLIENT_ID& + redirect_uri=http://127.0.0.1:{port}/callback& + scope=openid ivoa:data.read& + code_challenge=& + code_challenge_method=S256& + state=" + \end{verbatim} + To generate the \verb|code_challenge|, it is required to implement PKCE (Proof Key for Code Exchange). That implies to + select a \verb|CODE_VERIFIER| and a \verb|STATE| (both, random strings generated by the client). The \verb|code_challenge| + will be generated by: + \begin{verbatim} + BASE64URL(SHA256()) + \end{verbatim} + The client MUST securely store both \verb|CODE_VERIFIER| and \verb|STATE| for future use.} +\item{\textbf{Handle redirect:} After the user logs in, the identity provider redirects to the app's registered redirect + URI with an authorisation code. This URL is provided by the client application and it will + receive the authentication code at the parameter \verb|code|. This code MUST be + securely saved at the client for future use +\begin{verbatim} +http://127.0.0.1:{port}/callback? + code=SplxlOBeZQQYbYS6WxSbIA&state=xyz +\end{verbatim} +Clients must validate the state and ensure the incoming request matches the originally stored values to prevent injection or hijacking.} + +\item{\textbf{Exchange code for token:} The application sends the authorisation code to the token endpoint to obtain an + access token (and optionally a refresh token). +\begin{verbatim} +curl -X POST https://auth.example.org/oauth/token + -d 'grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA& + redirect_uri=http://127.0.0.1:{port}/callback& + client_id=my-app& + code_verifier=' +\end{verbatim} +The client will receive in response a JSON document, including an \verb|access_token| and a \verb|refresh_token|. For example: +\begin{verbatim} +{ + "access_token": "SlAV32hkKG", + "token_type": "Bearer", + "expires_in": 3600, + "refresh_token": "8xLOxBtZp8", + "id_token": "eyJhbGciOiJSUzI1..." +} +\end{verbatim} +\textit{Note: Tokens will not be received at the redirect URI but an invocation like:} +\begin{verbatim} +http://127.0.0.1:{port}/callback? + code=SplxlOBeZQQYbYS6WxSbIA&state=xyz +\end{verbatim} +} +\item{\textbf{Token refresh:} Applications could refresh tokens as in previous section} +\end{enumerate} +\textbf{Note of the Redirect URI:} +The \verb|redirect_uri| tells the authorisation server where to send the user (along with the authorisation code) after +they’ve successfully logged in and granted consent. +This is how the client gets the authorisation code needed to obtain tokens in the next step. +\begin{enumerate} +\item{\textbf{Loopback Interface (Recommended):} +This opens a temporary HTTP server on localhost to receive the redirect. Format: +\begin{verbatim} +http://127.0.0.1:{port}/callback +\end{verbatim} +Pros: Doesn't require registering a custom URI scheme. Supported on: Desktop apps (Electron, Python, Java, etc.)} +\item{\textbf{Custom URI Scheme):} +The app registers a custom scheme (e.g., myapp://callback) with the OS. +Format: +\begin{verbatim} +com.example.app:/oauth2redirect +or +myapp://callback +\end{verbatim} +Pros: Works well on mobile (Android, iOS). Cons: Must register scheme in app manifest; some phishing risk if misconfigured.} +\end{enumerate} \appendix \section{Changes from Previous Versions} From f2d8d89bc4a8c070fdb8ecc60d9b6ba7fa9445b5 Mon Sep 17 00:00:00 2001 From: Jesus Salgado Date: Fri, 13 Jun 2025 17:45:38 +0200 Subject: [PATCH 2/9] Align parameters for oAuth --- IAP.tex | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/IAP.tex b/IAP.tex index 3b3d79e..5c7fb14 100644 --- a/IAP.tex +++ b/IAP.tex @@ -499,8 +499,13 @@ \subsubsection{\mbox{\tt ivoa\_bearer}}\label{sec:ivoa-bearer} \item[Scheme name:] \verb|ivoa_bearer| \item[Parameters:] \mbox{} \begin{itemize} - \item \texttt{access\_url} (required): URL pointing to a discovery document that conforms to RFC 8414~\citep{rfc8414} . This document provides the metadata required for client configuration, including authorisation endpoints and other metadata. - \item \texttt{X-VO-Auth-Error} (optional): VO-specific error header. Examples include \texttt{expired\_token}, \texttt{invalid\_token}, or \texttt{invalid\_token\_type}. + \item{\verb|access_url| (required): URL pointing to a discovery document that conforms to RFC 8414~\citep{rfc8414} . + This document provides the metadata required for client configuration, including authorisation endpoints and other metadata.} + \item{\verb|standard_id| (required) --- + indicates how to authenticate at \verb|access_url|, + see Section~\ref{sec:standard-id}} + \item{\verb|X-VO-Auth-Error| (optional): VO-specific error header. Examples include \verb|expired_token|, + \verb|invalid_token|, or \verb|invalid_token_type|.} \end{itemize} \end{description} @@ -508,7 +513,7 @@ \subsubsection{\mbox{\tt ivoa\_bearer}}\label{sec:ivoa-bearer} \begin{verbatim} HTTP/1.1 401 Unauthorized -WWW-Authenticate: ivoa_bearer +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: invalid_token_type \end{verbatim} @@ -578,7 +583,7 @@ \subsubsection{\mbox{\tt ivoa\_bearer}}\label{sec:ivoa-bearer} When expired: \begin{verbatim} HTTP/1.1 401 Unauthorized -WWW-Authenticate: ivoa_bearer +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} @@ -1031,8 +1036,8 @@ \subsubsection{Before execution} HTTP response: \begin{verbatim} HTTP/1.1 401 Unauthorized -WWW-Authenticate: ivoa_bearer -service_url="https://auth.example.org/.well-known/openid-configuration" +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: missing_token \end{verbatim} This response tells the client that a Bearer token is required and provides a discovery URL for discovering the From c785c70399d8e83f002e1bf4000e1883a8a8b92d Mon Sep 17 00:00:00 2001 From: Jesus Salgado Date: Mon, 16 Jun 2025 10:08:20 +0200 Subject: [PATCH 3/9] Reduce oAuth examples to RFC 7636 --- IAP.tex | 380 ++++++++++++++++++++++++++------------------------------ 1 file changed, 176 insertions(+), 204 deletions(-) diff --git a/IAP.tex b/IAP.tex index 5c7fb14..2a586a5 100644 --- a/IAP.tex +++ b/IAP.tex @@ -485,60 +485,82 @@ \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 allows VO services to request authentication via Bearer tokens, -aligning with OAuth 2.0~\citep{rfc6749} and OpenID Connect standards. This scheme enables secure, -standards-compliant authentication for VO clients and applications, while preserving interoperability with -existing OAuth2 infrastructure and libraries. -This approach is compatible with automated clients, desktop applications, and headless environments, and -supports token expiration, refresh workflows, and secure origin scoping via the \texttt{allowed\_origins} -field in token responses. +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}, OpenID Connect, and the latest +best practices such as the Authorization Code Flow with PKCE \citep{rfc7636}, offering a standards-compliant, +interoperable solution for clients and services. + +This scheme is compatible with: +\begin{itemize} + \item Interactive environments (e.g., notebooks, desktop apps) via the Authorization Code Flow with PKCE. + \item Headless clients (e.g., command-line tools or scripts) via the Device Authorization Flow \citep{rfc8628}. + \item Automated workflows using refresh tokens for reauthentication. +\end{itemize} + +It supports token expiry and renewal, secure scoping via the \texttt{allowed\_origins} field or the modern +\verb|oauth-authorization-server| discovery document \citep{rfc9728}, and integrates seamlessly with OAuth2-compatible +infrastructures like Keycloak-based IAM services. \begin{description} - \item[Scheme name:] \verb|ivoa_bearer| - \item[Parameters:] \mbox{} - \begin{itemize} - \item{\verb|access_url| (required): URL pointing to a discovery document that conforms to RFC 8414~\citep{rfc8414} . - This document provides the metadata required for client configuration, including authorisation endpoints and other metadata.} - \item{\verb|standard_id| (required) --- - indicates how to authenticate at \verb|access_url|, - see Section~\ref{sec:standard-id}} - \item{\verb|X-VO-Auth-Error| (optional): VO-specific error header. Examples include \verb|expired_token|, - \verb|invalid_token|, or \verb|invalid_token_type|.} - \end{itemize} +\item{{Scheme name:} \verb|ivoa_bearer|} +\item{{Parameters:} +\begin{itemize} + \item \verb|access_url| (required): URL to the RFC 8414 \citep{rfc8414} discovery document, providing essential metadata (e.g., endpoints, scopes). + \item \verb|standard_id| (required): Indicates how to authenticate using the specified \verb|access_url| + \item{\verb|X-VO-Auth-Error| (optional): A VO-specific error message. Typical values: \verb|missing_token|, + \verb|expired_token|, \verb|invalid_token|.} + \item \verb|X-VO-Auth-User-Action| (optional): A VO-specific info message. +\end{itemize}} \end{description} -When a protected resource is accessed without valid authentication, the service MUST respond: + +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" - access_url="https://auth.example.org/.well-known/openid-configuration" -X-VO-Auth-Error: invalid_token_type +access_url="https://auth.example.org/.well-known/openid-configuration" +X-VO-Auth-Error: missing_token \end{verbatim} -In order to continue the authorisation process, clients MUST fetch and parse the discovery document. -An example response is at follows: +Clients MUST retrieve the discovery document at the given \verb|access_url|. A typical response includes: \begin{verbatim} { "issuer": "https://auth.example.org", - "token_endpoint": "https://auth.example.org/oauth/token", "authorization_endpoint": "https://auth.example.org/oauth/authorize", + "token_endpoint": "https://auth.example.org/oauth/token", "device_authorization_endpoint": "https://auth.example.org/oauth/device", - "registration_endpoint": "https://auth.example.org/oauth/register", "grant_types_supported": [ "authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:device_code" ], + "code_challenge_methods_supported": ["S256"], "scopes_supported": ["openid", "email", "profile"], "token_endpoint_auth_methods_supported": ["client_secret_post", "none"] } \end{verbatim} -Once authorised, an authentication response should be provided as follows: +\begin{description} +\item{{Token acquisition:} + +Clients should initiate the Authorization Code Flow with PKCE by generating a \verb|code_verifier| and deriving a +\verb|code_challenge| from it using SHA-256. The user is then redirected to the \verb|authorization_endpoint| for login. + +In browserless environments, the server may return a \texttt{401 Unauthorized} with an \texttt{X-VO-Auth-Error} such as: + +\begin{verbatim} +X-VO-Auth-Error: login_required +X-VO-Auth-User-Action: visit https://auth.example.org/oauth/authorize +\end{verbatim} + +The user may then obtain the code via a browser and return it to the client, which exchanges it at the \texttt{token\_endpoint} using the original \texttt{code\_verifier}. + +Upon success, the client receives: \begin{verbatim} { @@ -546,50 +568,50 @@ \subsubsection{\mbox{\tt ivoa\_bearer}}\label{sec:ivoa-bearer} "token_type": "Bearer", "expires_in": 3600, "refresh_token": "xyz789", - "allowed_origins": ["https://example.org", "https://data.example.org"] + "allowed_origins": [ + "https://example.org", + "https://data.example.org" + ] } -\end{verbatim} +\end{verbatim}} +\item{{Token Scoping:} \begin{itemize} - \item \texttt{allowed\_origins} (recommended): HTTP origins where the token may be used (legacy support). Clients MUST NOT use tokens elsewhere. + \item \verb|allowed_origins| (recommended): An array of HTTP origins where the token is valid. Clients MUST NOT use the token outside these origins. + \item If \verb|allowed_origins| is missing, the client MUST check for support of RFC 9728 \citep{rfc9728} by querying: \end{itemize} -If \texttt{allowed\_origins} is not present, service MUST support RFC 9728~\citep{rfc9728}. In this case, clients continue -discovery by requesting: - \begin{verbatim} GET /.well-known/oauth-authorization-server \end{verbatim} -This document, always present at a location relative to the issuer URL, produces a response like: +This returns: \begin{verbatim} { - authorization_servers: ["https://auth.example.org"], - resource: "https://data.example.org" + "authorization_servers": ["https://auth.example.org"], + "resource": "https://data.example.org" } -\end{verbatim} - -where \texttt{authorization\_servers} is replacing the logic described by \texttt{allowed\_origins} for legacy services. +\end{verbatim}} +\item{{Using Tokens:} Tokens must be included in the HTTP \texttt{Authorization} header: -\textbf{Permit Usage} -Include the access token in the HTTP request header: \begin{verbatim} Authorization: Bearer abc123 -\end{verbatim} +\end{verbatim}} + +\item{{Token Expiry and Refresh:} On expiration: -\textbf{Token Expiry and Refresh} -When expired: \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" +access_url="https://auth.example.org/.well-known/openid-configuration" X-VO-Auth-Error: expired_token \end{verbatim} -Clients SHOULD use the \texttt{refresh\_token} if available. - +Clients SHOULD use the provided \verb|refresh_token| to obtain a new access token, avoiding repeated interactive logins. +There is an example in Section~\ref{sec:ivoa-bearer-example}.} +\end{description} \subsection{Common Challenge Parameters for VO Schemes} \label{sec:common-params} @@ -635,15 +657,21 @@ \subsubsection{\mbox{\tt standard\_id}} \item[{\tt ivo://ivoa.net/sso\#OAuth}] This value indicates that the service requires OAuth 2.0-based authentication - using a Bearer token. The exact login mechanism is not directly specified by - this identifier and MUST be discovered dynamically via a discovery URL provided at the - \texttt{service\_url} - parameter included in the \texttt{WWW-Authenticate} challenge. Clients are expected + 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|service_url| parameter of the \texttt{WWW-Authenticate} challenge. Clients are expected to follow the metadata discovery process defined in RFC 8414~\citep{rfc8414} and, - if applicable, RFC 9728~\citep{rfc9728} to determine the supported grant types and endpoints. - The OAuth 2.0 flows most relevant to VO usage include the Device Authorization Grant (RFC 8628) - and the Authorization Code Grant for native apps (RFC 8252). Access and refresh tokens are acquired - through standard OAuth2 token endpoints and used in the Authorization header via the Bearer scheme. + if applicable, RFC 9728~\citep{rfc9728}, to determine the supported endpoints and authorisation + server configuration. + + In this profile, only the Authorization Code Flow with Proof Key for Code Exchange (PKCE), + as defined in RFC 7636~\citep{rfc7636} and recommended for native apps in RFC 8252~\citep{rfc8252}, + is supported. This ensures secure and standards-compliant authentication in both interactive + and semi-interactive environments. + + Access and refresh tokens are acquired through the authorization 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} @@ -1019,181 +1047,125 @@ \subsection{Mandatory authentication with certificates} \end{verbatim} } +\subsection{OAuth Authorization Code Flow with PKCE (RFC 7636)} +\label{sec:ivoa-bearer-example} -\subsection{OAuth Device Code Flow (RFC 8628)} +In interactive environments such as desktop applications, Jupyter notebooks, or scripts requiring manual user +authentication, the OAuth 2.0 Authorization Code Flow with Proof Key for Code Exchange (PKCE) enables secure +token acquisition without requiring a client secret. It is especially suitable for public clients and supports +flexible workflows, including those where a browser is not directly accessible from the client environment. -Headless clients such as command-line tools, HPC batch jobs, or remote scripts often lack access -to a local browser or a graphical user interface. Traditional OAuth flows like the Authorization -Code Grant (RFC 8252) assume user interaction through a browser, which may not be available or secure -in non-interactive environments. The Device Code Flow~\citep{rfc8628} is specifically designed for these cases: -it allows the client to initiate authentication by displaying a short code to the user, who completes the login -process in a browser on another device. This flow separates the user interaction from the headless environment, -enabling secure, standards-compliant authentication without requiring embedded browsers, cookies, or redirect handling. +\subsubsection*{Before Execution} -\subsubsection{Before execution} \begin{enumerate} -\item{\textbf{Receive Unauthorised error:} The client attempts to access a protected resource and receives the following -HTTP response: -\begin{verbatim} + \item \textbf{Initial request and discovery:} The client attempts to access a protected resource and receives a + \texttt{401 Unauthorized} response: + + \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: missing_token -\end{verbatim} -This response tells the client that a Bearer token is required and provides a discovery URL for discovering the -authorisation server's metadata. The client MUST fetch and parse this discovery document (per [RFC 8414]) to continue. -From the discovery metadata, the client identifies among others: -\begin{itemize} -\item \verb|device_authorization_endpoint|: the URL to initiate device login -\item \verb|token_endpoint|: where the client polls and refreshes tokens -\item{\verb|grant_types_supported|: confirms support for device code flow (urn:ietf:params:oauth:grant-type:device\_code). -Also, \verb|refresh_token| for token refreshing.} -\item \verb|scopes_supported|: available scopes (e.g., openid for user authentication or offline\_access to request refresh tokens) -\end{itemize}} + \end{verbatim} -\item{\textbf{Request device code:} The client sends a request to the authorisation server \verb|device_authorization_endpoint| -to obtain a device code and user code for authentication. -\begin{verbatim} -curl -X POST https://auth.example.org/oauth/device - -d 'client_id=my-cli-client&scope=openid profile' -\end{verbatim}} -The response will contain, among others, metadata of a \verb|user_code|, a \verb|device_code| and a \verb|verification_uri| + This response includes a discovery URL. The client fetches the OpenID Connect discovery document, identifying: -\item{\textbf{Prompt to user:} The application parses the response and displays a message instructing the user to visit a - \verb|verification_uri| and enter the \verb|user_code| on a device with browser access. -\begin{verbatim} -echo "Visit https://auth.example.org/activate and - enter code ABC-123" -\end{verbatim}} + \begin{itemize} + \item \texttt{authorization\_endpoint} — the URL to initiate user login + \item \texttt{token\_endpoint} — used to exchange authorization codes for tokens + \item \texttt{code\_challenge\_methods\_supported} — must support \texttt{S256} + \item \texttt{grant\_types\_supported} — must include \texttt{authorization\_code} + \end{itemize} -\item{\textbf{Poll for access token:} The client periodically polls the \verb|token_endpoint| -to check if the user completed the authorisation, sending the \verb|client_id| and \verb|device_code| -\begin{verbatim} -curl -X POST https://auth.example.org/oauth/token - -d 'grant_type=urn:ietf:params:oauth:grant-type:device_code& - device_code=abc123&client_id=my-cli-client' -\end{verbatim}} + \item \textbf{Generate PKCE parameters:} The client creates a random \texttt{code\_verifier} and computes a SHA-256 + derived \verb|code_challenge|: -\item{\textbf{Authorisation success:} On authorisation success, a JSON document is received with an \verb|access_token| -and a \verb|refresh_token|} + \begin{verbatim} +code_verifier = "randomstring123" +code_challenge = BASE64URL-ENCODE(SHA256(code_verifier)) + \end{verbatim} -\item{\textbf{Use access token:} Operation execution can be invoked using the \verb|access_token| -\begin{verbatim} -GET /vo-resource -Authorization: Bearer SlAV32hkKG -\end{verbatim}} -\end{enumerate} + \item \textbf{Authorize via browser:} There are two possible paths: -\subsubsection{During execution} -\begin{enumerate} -\item{\textbf{Secure automation via refresh token} -For recurring or non-interactive workflows, the client may store the \verb|refresh_token| and later use it to obtain new access tokens: -\begin{verbatim} + \begin{itemize} + \item \textbf{(a) Automatic browser launch:} In environments with browser access (e.g., local desktops or notebooks), + the client constructs the URL and opens it: + + \begin{verbatim} +https://auth.example.org/oauth/authorize? + response_type=code& + client_id=my-client& + redirect_uri=http://localhost:8080/callback& + scope=openid profile offline_access& + code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM& + code_challenge_method=S256 + \end{verbatim} + + A temporary local HTTP server listens at \texttt{http://localhost:8080/callback} to receive the redirect containing + the authorisation code. + + \item \textbf{(b) Manual browser flow:} If the Python client is running in a non-interactive or headless environment + (e.g., remote shell or batch job), the server may respond with a \texttt{401 Unauthorized} and include an instruction + to use a browser for login: + + \begin{verbatim} +X-VO-Auth-Error: login_required +X-VO-Auth-User-Action: visit https://auth.example.org/oauth/authorize + \end{verbatim} + + The user authenticates manually in a separate browser, receives the authorisation code (e.g., from a redirect URI or + copy-paste page), and provides it to the client script via configuration or prompt. + \end{itemize} + + \item \textbf{Exchange code for tokens:} Once the client has obtained the code (via local redirect or user input), + it performs the token exchange: + + \begin{verbatim} curl -X POST https://auth.example.org/oauth/token - -d 'grant_type=refresh_token& - refresh_token=xyz456&client_id=my-cli-client' -\end{verbatim}} -\end{enumerate} + -d 'grant_type=authorization_code' + -d 'code=SplxlOBeZQQYbYS6WxSbIA' + -d 'redirect_uri=http://localhost:8080/callback' + -d 'client_id=my-client' + -d 'code_verifier=randomstring123' + \end{verbatim} -This enables: -\begin{itemize} -\item Scheduled access (e.g., cron jobs) -\item HPC workflows -\item Re-authentication without re-prompting the user -\end{itemize} + The server responds with an \verb|access_token| and optionally a \verb|refresh_token|. +\end{enumerate} -\textit{Warning! Refresh tokens must be stored securely. If compromised, they allow long-term access to the user’s identity.} +\subsubsection*{During Execution} +\begin{itemize} + \item \textbf{Access protected resources:} The client includes the \verb|access_token| in HTTP requests: -\subsection{OAuth Authorization Code Flow (RFC 8252)} -Desktop applications, such as rich GUI tools, typically have access to a local web browser and -can listen on loopback interfaces (e.g., \texttt{localhost}) for redirects. The Authorization Code Flow~\citep{rfc8252} -is the recommended method for these applications because it allows secure delegation of authentication to the user's -preferred identity provider. It involves redirecting the user to a browser for login, after which an authorisation code -is returned and exchanged for an access token. This flow ensures that sensitive credentials never pass through the client -application directly, improves compatibility with federated identity providers, and supports refresh tokens and fine-grained -scope control. -\begin{enumerate} - \item{}\textbf{Obtain the authorisation endpoint after error:} After authorisation error (see previous section), discover the \verb|authorization_endpoint| - using the \verb|openid-configuration| URI. For this flow, the grant type supported in the \verb|grant_types_supported| list MUST - be \verb|authorization_code| for first authorisation and \verb|refresh_token| for token refreshing. - \item{\textbf{Open browser:} The application launches the user’s browser using the \verb|authorization_endpoint| URL that begins the authorisation process \begin{verbatim} - open https://auth.example.com/authorize? - response_type=code& - client_id=YOUR_CLIENT_ID& - redirect_uri=http://127.0.0.1:{port}/callback& - scope=openid ivoa:data.read& - code_challenge=& - code_challenge_method=S256& - state=" +GET /vo-resource +Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR... \end{verbatim} - To generate the \verb|code_challenge|, it is required to implement PKCE (Proof Key for Code Exchange). That implies to - select a \verb|CODE_VERIFIER| and a \verb|STATE| (both, random strings generated by the client). The \verb|code_challenge| - will be generated by: + + \item \textbf{Refresh token support:} If the authorisation server issues a \verb|refresh_token|, the client may use + it to obtain new access tokens: + \begin{verbatim} - BASE64URL(SHA256()) +curl -X POST https://auth.example.org/oauth/token + -d 'grant_type=refresh_token' + -d 'refresh_token=xyz456' + -d 'client_id=my-client' \end{verbatim} - The client MUST securely store both \verb|CODE_VERIFIER| and \verb|STATE| for future use.} -\item{\textbf{Handle redirect:} After the user logs in, the identity provider redirects to the app's registered redirect - URI with an authorisation code. This URL is provided by the client application and it will - receive the authentication code at the parameter \verb|code|. This code MUST be - securely saved at the client for future use -\begin{verbatim} -http://127.0.0.1:{port}/callback? - code=SplxlOBeZQQYbYS6WxSbIA&state=xyz -\end{verbatim} -Clients must validate the state and ensure the incoming request matches the originally stored values to prevent injection or hijacking.} -\item{\textbf{Exchange code for token:} The application sends the authorisation code to the token endpoint to obtain an - access token (and optionally a refresh token). -\begin{verbatim} -curl -X POST https://auth.example.org/oauth/token - -d 'grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA& - redirect_uri=http://127.0.0.1:{port}/callback& - client_id=my-app& - code_verifier=' -\end{verbatim} -The client will receive in response a JSON document, including an \verb|access_token| and a \verb|refresh_token|. For example: -\begin{verbatim} -{ - "access_token": "SlAV32hkKG", - "token_type": "Bearer", - "expires_in": 3600, - "refresh_token": "8xLOxBtZp8", - "id_token": "eyJhbGciOiJSUzI1..." -} -\end{verbatim} -\textit{Note: Tokens will not be received at the redirect URI but an invocation like:} -\begin{verbatim} -http://127.0.0.1:{port}/callback? - code=SplxlOBeZQQYbYS6WxSbIA&state=xyz -\end{verbatim} -} -\item{\textbf{Token refresh:} Applications could refresh tokens as in previous section} -\end{enumerate} -\textbf{Note of the Redirect URI:} -The \verb|redirect_uri| tells the authorisation server where to send the user (along with the authorisation code) after -they’ve successfully logged in and granted consent. -This is how the client gets the authorisation code needed to obtain tokens in the next step. -\begin{enumerate} -\item{\textbf{Loopback Interface (Recommended):} -This opens a temporary HTTP server on localhost to receive the redirect. Format: -\begin{verbatim} -http://127.0.0.1:{port}/callback -\end{verbatim} + This enables: -Pros: Doesn't require registering a custom URI scheme. Supported on: Desktop apps (Electron, Python, Java, etc.)} -\item{\textbf{Custom URI Scheme):} -The app registers a custom scheme (e.g., myapp://callback) with the OS. -Format: -\begin{verbatim} -com.example.app:/oauth2redirect -or -myapp://callback -\end{verbatim} -Pros: Works well on mobile (Android, iOS). Cons: Must register scheme in app manifest; some phishing risk if misconfigured.} -\end{enumerate} + \begin{itemize} + \item Resuming sessions in notebooks + \item Long-term access from scripts without user interaction + \item Scheduled or re-authenticated use without password reuse + \end{itemize} + + \textbf{Security Note:} Refresh tokens must be stored securely. If leaked, they permit long-term access. +\end{itemize} + +This flow allows both seamless user-driven login during interactive use and pre-obtained authorisation for restricted +clients. It avoids passwords and client secrets, enforces token best practices, and enables full OAuth 2.0 compliance +even in lightweight clients. \appendix \section{Changes from Previous Versions} From 6b1b3ae05d8dec6458c0b7821c1c108599683aac Mon Sep 17 00:00:00 2001 From: Jesus Salgado Date: Mon, 16 Jun 2025 17:43:24 +0200 Subject: [PATCH 4/9] Align with SRCNet implementation --- IAP.tex | 302 ++++++++++++++++++++++++++------------------------------ 1 file changed, 140 insertions(+), 162 deletions(-) diff --git a/IAP.tex b/IAP.tex index 2a586a5..5c1a814 100644 --- a/IAP.tex +++ b/IAP.tex @@ -489,34 +489,26 @@ \subsubsection{\mbox{\tt ivoa\_x509}}\label{sec:ivoa-x509} \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}, OpenID Connect, and the latest -best practices such as the Authorization Code Flow with PKCE \citep{rfc7636}, offering a standards-compliant, -interoperable solution for clients and services. +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 is compatible with: -\begin{itemize} - \item Interactive environments (e.g., notebooks, desktop apps) via the Authorization Code Flow with PKCE. - \item Headless clients (e.g., command-line tools or scripts) via the Device Authorization Flow \citep{rfc8628}. - \item Automated workflows using refresh tokens for reauthentication. -\end{itemize} +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 supports token expiry and renewal, secure scoping via the \texttt{allowed\_origins} field or the modern -\verb|oauth-authorization-server| discovery document \citep{rfc9728}, and integrates seamlessly with OAuth2-compatible -infrastructures like Keycloak-based IAM services. +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{{Parameters:} \begin{itemize} - \item \verb|access_url| (required): URL to the RFC 8414 \citep{rfc8414} discovery document, providing essential metadata (e.g., endpoints, scopes). - \item \verb|standard_id| (required): Indicates how to authenticate using the specified \verb|access_url| - \item{\verb|X-VO-Auth-Error| (optional): A VO-specific error message. Typical values: \verb|missing_token|, - \verb|expired_token|, \verb|invalid_token|.} - \item \verb|X-VO-Auth-User-Action| (optional): A VO-specific info message. + \item \verb|access_url| (required): URL to the RFC 8414 \citep{rfc8414} discovery document, providing essential metadata such as endpoints and supported grant types. + \item \verb|standard_id| (required): Indicates the protocol and profile used for authentication (\texttt{ivo://ivoa.net/sso\#OAuth}). + \item \verb|X-VO-Auth-Error| (optional): A VO-specific error message. Typical values include \verb|missing_token|, \verb|expired_token|, or \verb|invalid_token|. + \item \verb|X-VO-Auth-User-Action| (optional): A VO-specific informational message for user guidance. \end{itemize}} \end{description} - When a protected resource is accessed without valid authentication, the service responds with: \begin{verbatim} @@ -531,34 +523,22 @@ \subsubsection{\mbox{\tt ivoa\_bearer}}\label{sec:ivoa-bearer} \begin{verbatim} { "issuer": "https://auth.example.org", - "authorization_endpoint": "https://auth.example.org/oauth/authorize", "token_endpoint": "https://auth.example.org/oauth/token", "device_authorization_endpoint": "https://auth.example.org/oauth/device", "grant_types_supported": [ - "authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:device_code" ], - "code_challenge_methods_supported": ["S256"], - "scopes_supported": ["openid", "email", "profile"], - "token_endpoint_auth_methods_supported": ["client_secret_post", "none"] + "scopes_supported": ["openid", "email", "profile"] } \end{verbatim} \begin{description} \item{{Token acquisition:} -Clients should initiate the Authorization Code Flow with PKCE by generating a \verb|code_verifier| and deriving a -\verb|code_challenge| from it using SHA-256. The user is then redirected to the \verb|authorization_endpoint| for login. - -In browserless environments, the server may return a \texttt{401 Unauthorized} with an \texttt{X-VO-Auth-Error} such as: - -\begin{verbatim} -X-VO-Auth-Error: login_required -X-VO-Auth-User-Action: visit https://auth.example.org/oauth/authorize -\end{verbatim} - -The user may then obtain the code via a browser and return it to the client, which exchanges it at the \texttt{token\_endpoint} using the original \texttt{code\_verifier}. +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: @@ -567,40 +547,19 @@ \subsubsection{\mbox{\tt ivoa\_bearer}}\label{sec:ivoa-bearer} "access_token": "abc123", "token_type": "Bearer", "expires_in": 3600, - "refresh_token": "xyz789", - "allowed_origins": [ - "https://example.org", - "https://data.example.org" - ] -} -\end{verbatim}} - -\item{{Token Scoping:} -\begin{itemize} - \item \verb|allowed_origins| (recommended): An array of HTTP origins where the token is valid. Clients MUST NOT use the token outside these origins. - \item If \verb|allowed_origins| is missing, the client MUST check for support of RFC 9728 \citep{rfc9728} by querying: -\end{itemize} - -\begin{verbatim} -GET /.well-known/oauth-authorization-server -\end{verbatim} - -This returns: - -\begin{verbatim} -{ - "authorization_servers": ["https://auth.example.org"], - "resource": "https://data.example.org" + "refresh_token": "xyz789" } \end{verbatim}} -\item{{Using Tokens:} Tokens must be included in the HTTP \texttt{Authorization} header: +\item{{Using Tokens:} Access tokens must be included in the HTTP \texttt{Authorization} header: \begin{verbatim} Authorization: Bearer abc123 \end{verbatim}} -\item{{Token Expiry and Refresh:} On expiration: +\item{{Token Expiry and Refresh:} + +When an access token expires, the server may respond: \begin{verbatim} HTTP/1.1 401 Unauthorized @@ -609,10 +568,11 @@ \subsubsection{\mbox{\tt ivoa\_bearer}}\label{sec:ivoa-bearer} X-VO-Auth-Error: expired_token \end{verbatim} -Clients SHOULD use the provided \verb|refresh_token| to obtain a new access token, avoiding repeated interactive logins. -There is an example in Section~\ref{sec:ivoa-bearer-example}.} +Clients SHOULD use the provided \verb|refresh_token| to obtain a new access token without prompting the user. +} \end{description} + \subsection{Common Challenge Parameters for VO Schemes} \label{sec:common-params} @@ -659,17 +619,15 @@ \subsubsection{\mbox{\tt standard\_id}} 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|service_url| parameter of the \texttt{WWW-Authenticate} challenge. Clients are expected - to follow the metadata discovery process defined in RFC 8414~\citep{rfc8414} and, - if applicable, RFC 9728~\citep{rfc9728}, to determine the supported endpoints and authorisation - server configuration. + \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 Authorization Code Flow with Proof Key for Code Exchange (PKCE), - as defined in RFC 7636~\citep{rfc7636} and recommended for native apps in RFC 8252~\citep{rfc8252}, - is supported. This ensures secure and standards-compliant authentication in both interactive - and semi-interactive environments. + 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 authorization and token endpoints + 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. @@ -1047,125 +1005,145 @@ \subsection{Mandatory authentication with certificates} \end{verbatim} } -\subsection{OAuth Authorization Code Flow with PKCE (RFC 7636)} +\subsection{5.X OAuth Device Code Flow (RFC 8628)} \label{sec:ivoa-bearer-example} -In interactive environments such as desktop applications, Jupyter notebooks, or scripts requiring manual user -authentication, the OAuth 2.0 Authorization Code Flow with Proof Key for Code Exchange (PKCE) enables secure -token acquisition without requiring a client secret. It is especially suitable for public clients and supports -flexible workflows, including those where a browser is not directly accessible from the client environment. +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. -\subsubsection*{Before Execution} +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). -\begin{enumerate} - \item \textbf{Initial request and discovery:} The client attempts to access a protected resource and receives a - \texttt{401 Unauthorized} response: +\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} +\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: missing_token - \end{verbatim} +\end{verbatim} - This response includes a discovery URL. The client fetches the OpenID Connect discovery document, identifying: +This indicates that a Bearer token is required. The client \textbf{MUST} fetch and parse the OpenID Connect Discovery +document located at the \verb|access_url|.} - \begin{itemize} - \item \texttt{authorization\_endpoint} — the URL to initiate user login - \item \texttt{token\_endpoint} — used to exchange authorization codes for tokens - \item \texttt{code\_challenge\_methods\_supported} — must support \texttt{S256} - \item \texttt{grant\_types\_supported} — must include \texttt{authorization\_code} - \end{itemize} +\item{\textbf{Extract Relevant Metadata} - \item \textbf{Generate PKCE parameters:} The client creates a random \texttt{code\_verifier} and computes a SHA-256 - derived \verb|code_challenge|: +From the discovery document \\ +(\texttt{https://auth.example.org/.well-known/openid-configuration}), \\ +the client retrieves the following fields: - \begin{verbatim} -code_verifier = "randomstring123" -code_challenge = BASE64URL-ENCODE(SHA256(code_verifier)) - \end{verbatim} +\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} - \item \textbf{Authorize via browser:} There are two possible paths: +Example metadata: - \begin{itemize} - \item \textbf{(a) Automatic browser launch:} In environments with browser access (e.g., local desktops or notebooks), - the client constructs the URL and opens it: - - \begin{verbatim} -https://auth.example.org/oauth/authorize? - response_type=code& - client_id=my-client& - redirect_uri=http://localhost:8080/callback& - scope=openid profile offline_access& - code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM& - code_challenge_method=S256 - \end{verbatim} - - A temporary local HTTP server listens at \texttt{http://localhost:8080/callback} to receive the redirect containing - the authorisation code. - - \item \textbf{(b) Manual browser flow:} If the Python client is running in a non-interactive or headless environment - (e.g., remote shell or batch job), the server may respond with a \texttt{401 Unauthorized} and include an instruction - to use a browser for login: - - \begin{verbatim} -X-VO-Auth-Error: login_required -X-VO-Auth-User-Action: visit https://auth.example.org/oauth/authorize - \end{verbatim} - - The user authenticates manually in a separate browser, receives the authorisation code (e.g., from a redirect URI or - copy-paste page), and provides it to the client script via configuration or prompt. - \end{itemize} +\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{Exchange code for tokens:} Once the client has obtained the code (via local redirect or user input), - it performs the token exchange: +\item{\textbf{Request Device Code} - \begin{verbatim} -curl -X POST https://auth.example.org/oauth/token - -d 'grant_type=authorization_code' - -d 'code=SplxlOBeZQQYbYS6WxSbIA' - -d 'redirect_uri=http://localhost:8080/callback' - -d 'client_id=my-client' - -d 'code_verifier=randomstring123' - \end{verbatim} +The client initiates the device flow: - The server responds with an \verb|access_token| and optionally a \verb|refresh_token|. -\end{enumerate} +\begin{verbatim} +curl -X POST https://auth.example.org/oauth/device \ + -d 'client_id=my-client-id&scope=openid profile' +\end{verbatim} -\subsubsection*{During Execution} +This URL is obtained from \verb|device_authorization_endpoint|.} -\begin{itemize} - \item \textbf{Access protected resources:} The client includes the \verb|access_token| in HTTP requests: +\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} - \begin{verbatim} +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 eyJhbGciOiJSUzI1NiIsInR... - \end{verbatim} +Authorization: Bearer SlAV32hkKG +\end{verbatim}} +\end{description} - \item \textbf{Refresh token support:} If the authorisation server issues a \verb|refresh_token|, the client may use - it to obtain new access tokens: +\subsubsection{During Execution} - \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' - \end{verbatim} +\begin{description} +\item{\textbf{Token Refresh} - This enables: +To maintain access for long-running jobs or recurring tasks, the client refreshes the token using: - \begin{itemize} - \item Resuming sessions in notebooks - \item Long-term access from scripts without user interaction - \item Scheduled or re-authenticated use without password reuse - \end{itemize} +\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} - \textbf{Security Note:} Refresh tokens must be stored securely. If leaked, they permit long-term access. -\end{itemize} +This request is also sent to the \verb|token_endpoint|. -This flow allows both seamless user-driven login during interactive use and pre-obtained authorisation for restricted -clients. It avoids passwords and client secrets, enforces token best practices, and enables full OAuth 2.0 compliance -even in lightweight clients. +\textbf{Note:} Refresh tokens must be stored securely. If compromised, they provide persistent access to user resources.} +\end{description} \appendix \section{Changes from Previous Versions} From 9c3b9ea784da654f2d820efcaab4d5877c04793f Mon Sep 17 00:00:00 2001 From: Jesus Salgado Date: Mon, 16 Jun 2025 17:43:47 +0200 Subject: [PATCH 5/9] Add RFCs references --- localrefs.bib | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) 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}} +} + From 2fd2109b5303e8b5d3312d8f89dc6a4457699a49 Mon Sep 17 00:00:00 2001 From: Jesus Salgado Date: Mon, 16 Jun 2025 18:12:16 +0200 Subject: [PATCH 6/9] correct typos --- IAP.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IAP.tex b/IAP.tex index 5c1a814..01541e1 100644 --- a/IAP.tex +++ b/IAP.tex @@ -1005,7 +1005,7 @@ \subsection{Mandatory authentication with certificates} \end{verbatim} } -\subsection{5.X OAuth Device Code Flow (RFC 8628)} +\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 From 3a1872e58a4cc3314d41b1d876e33eb5948b1051 Mon Sep 17 00:00:00 2001 From: Jesus Salgado Date: Fri, 20 Jun 2025 16:32:29 +0100 Subject: [PATCH 7/9] correct typos --- IAP.tex | 107 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 74 insertions(+), 33 deletions(-) diff --git a/IAP.tex b/IAP.tex index 01541e1..a5026f4 100644 --- a/IAP.tex +++ b/IAP.tex @@ -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, @@ -500,12 +503,12 @@ \subsubsection{\mbox{\tt ivoa\_bearer}}\label{sec:ivoa-bearer} \begin{description} \item{{Scheme name:} \verb|ivoa_bearer|} -\item{{Parameters:} +\item{\textbf{Parameters:} \begin{itemize} - \item \verb|access_url| (required): URL to the RFC 8414 \citep{rfc8414} discovery document, providing essential metadata such as endpoints and supported grant types. - \item \verb|standard_id| (required): Indicates the protocol and profile used for authentication (\texttt{ivo://ivoa.net/sso\#OAuth}). - \item \verb|X-VO-Auth-Error| (optional): A VO-specific error message. Typical values include \verb|missing_token|, \verb|expired_token|, or \verb|invalid_token|. - \item \verb|X-VO-Auth-User-Action| (optional): A VO-specific informational message for user guidance. + \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} @@ -513,13 +516,15 @@ \subsubsection{\mbox{\tt ivoa\_bearer}}\label{sec:ivoa-bearer} \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: missing_token +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} -Clients MUST retrieve the discovery document at the given \verb|access_url|. A typical response includes: - +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", @@ -533,8 +538,27 @@ \subsubsection{\mbox{\tt ivoa\_bearer}}\label{sec:ivoa-bearer} } \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{{Token acquisition:} +\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 @@ -557,7 +581,7 @@ \subsubsection{\mbox{\tt ivoa\_bearer}}\label{sec:ivoa-bearer} Authorization: Bearer abc123 \end{verbatim}} -\item{{Token Expiry and Refresh:} +\item{\textbf{Token Expiry and Refresh:} When an access token expires, the server may respond: @@ -568,7 +592,20 @@ \subsubsection{\mbox{\tt ivoa\_bearer}}\label{sec:ivoa-bearer} X-VO-Auth-Error: expired_token \end{verbatim} -Clients SHOULD use the provided \verb|refresh_token| to obtain a new access token without prompting the user. +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} @@ -1019,21 +1056,23 @@ \subsection{OAuth Device Code Flow (RFC 8628)} \subsubsection{Before Execution} \begin{description} -\item{\textbf{Receive Unauthorized Error} +\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 standard_id="ivo://ivoa.net/sso#OAuth" -access_url="https://auth.example.org/.well-known/openid-configuration" -X-VO-Auth-Error: missing_token +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|access_url|.} +document located at the \verb|discovery_url|. -\item{\textbf{Extract Relevant Metadata} +\item{\textbf{Extract Relevant Metadata}} From the discovery document \\ (\texttt{https://auth.example.org/.well-known/openid-configuration}), \\ @@ -1065,9 +1104,9 @@ \subsubsection{Before Execution} ], "scopes_supported": ["openid", "profile", "offline_access"] } -\end{verbatim}} +\end{verbatim} -\item{\textbf{Request Device Code} +\item{\textbf{Request Device Code}} The client initiates the device flow: @@ -1076,9 +1115,9 @@ \subsubsection{Before Execution} -d 'client_id=my-client-id&scope=openid profile' \end{verbatim} -This URL is obtained from \verb|device_authorization_endpoint|.} +This URL is obtained from \verb|device_authorization_endpoint|. -\item{\textbf{Prompt User to Authenticate} +\item{\textbf{Prompt User to Authenticate}} The client displays a message like: @@ -1088,9 +1127,9 @@ \subsubsection{Before Execution} in a browser and enter the code: ABC-123 \end{verbatim} -Alternatively, the complete link from \verb|verification_uri_complete| can be shown directly.} +Alternatively, the complete link from \verb|verification_uri_complete| can be shown directly. -\item{\textbf{Poll for Token} +\item{\textbf{Poll for Token}} The client polls the token endpoint (from \verb|token_endpoint|): @@ -1099,9 +1138,9 @@ \subsubsection{Before Execution} -d 'grant_type=urn:ietf:params:oauth:grant-type:device_code' \ -d 'device_code=abc123' \ -d 'client_id=my-client-id' -\end{verbatim}} +\end{verbatim} -\item{\textbf{Receive Tokens} +\item{\textbf{Receive Tokens}} After successful user authentication, the client receives: @@ -1114,22 +1153,23 @@ \subsubsection{Before Execution} } \end{verbatim} -The tokens should be stored securely for future requests.} +The tokens should be stored securely for future requests. -\item{\textbf{Use the Token} +\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{verbatim} + \end{description} \subsubsection{During Execution} \begin{description} -\item{\textbf{Token Refresh} +\item{\textbf{Token Refresh}} To maintain access for long-running jobs or recurring tasks, the client refreshes the token using: @@ -1142,9 +1182,10 @@ \subsubsection{During Execution} 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.} +\textbf{Note:} Refresh tokens must be stored securely. If compromised, they provide persistent access to user resources. \end{description} + \appendix \section{Changes from Previous Versions} From f26c16e766726eeacaac3a90dee1348361359ae8 Mon Sep 17 00:00:00 2001 From: Jesus Salgado Date: Thu, 2 Oct 2025 14:22:33 +0100 Subject: [PATCH 8/9] declaring dynamic registration for bearer token --- IAP.tex => AuthVO.tex | 36 ++++++++++++++++++++++++++++++++++-- localrefs.bib | 8 ++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) rename IAP.tex => AuthVO.tex (96%) diff --git a/IAP.tex b/AuthVO.tex similarity index 96% rename from IAP.tex rename to AuthVO.tex index a5026f4..487fb76 100644 --- a/IAP.tex +++ b/AuthVO.tex @@ -3,7 +3,7 @@ \input gitmeta \usepackage[textsize=small,textwidth=3.8cm,backgroundcolor=yellow]{todonotes} -\title{IVOA Interoperable Authentication Protocol} +\title{Authentication in the Virtual Observatory} % see ivoatexDoc for what group names to use here; use \ivoagroup[IG] for % interest groups. @@ -13,6 +13,7 @@ \author{Sara Bertocco} \author{Patrick Dowler} \author{Brian Major} +\author{Jesus Salgado} \editor{Mark Taylor} @@ -25,7 +26,7 @@ \begin{document} \begin{abstract} -IVOA's Interoperable Authentication Protocol explains how +This document explains how VO services can manage the authentication process for interoperability with clients, especially non-browser clients. Particularly, this document @@ -1106,6 +1107,37 @@ \subsubsection{Before Execution} } \end{verbatim} +\item{\textbf{Dynamic Client Registration}} +Before initiating the Device Authorization Flow, clients may need to dynamically register with the Authorization Server in order to obtain a \texttt{client\_id}. + +This process is described in the OAuth 2.0 Dynamic Client Registration Protocol (RFC~\cite{rfc591}). + +\begin{verbatim} +curl -X POST https://auth.example.org/oauth/register \ + -H "Content-Type: application/json" \ + -d '{ + "client_name": "My VO Tool", + "grant_types": ["urn:ietf:params:oauth:grant-type:device_code"], + "token_endpoint_auth_method": "none" + }' +\end{verbatim} + +The server responds with a JSON object containing the newly assigned \texttt{client\_id} (and optionally a \texttt{client\_secret} if required): + +\begin{verbatim} +{ + "client_id": "my-client-id-123", + "client_secret": "optional-secret" +} +\end{verbatim} + +The client MUST store the \texttt{client\_id} securely and reuse it for future sessions. +This ensures that scripts, tools, or applications can obtain valid credentials even if they are not pre-registered with the VO infrastructure. + +In practice, some infrastructures may pre-register a small set of trusted clients (e.g., TOPCAT or Astroquery), but in general the mechanism of RFC~\cite{rfc7591} ensures that any compliant client can obtain credentials. +This registration step precedes the Device Authorization Grant (RFC~\cite{rfc8628}) and provides the necessary \texttt{client\_id} for the subsequent requests. + + \item{\textbf{Request Device Code}} The client initiates the device flow: diff --git a/localrefs.bib b/localrefs.bib index 70dfed0..e96a247 100644 --- a/localrefs.bib +++ b/localrefs.bib @@ -104,4 +104,12 @@ @misc{rfc8252 note = {\url{https://www.rfc-editor.org/rfc/rfc8252}} } +@misc{rfc7591, + author = {Justin Richer and John Bradley and Michael B. Jones and Nat Sakimura}, + title = {OAuth 2.0 Dynamic Client Registration Protocol}, + howpublished = {RFC 7591}, + year = {2015}, + url = {https://www.rfc-editor.org/info/rfc7591}, + note = {\url{https://www.rfc-editor.org/rfc/rfc7591}} +} From 7aac276887e501f1b371855fcebec7b5f32e98d7 Mon Sep 17 00:00:00 2001 From: jesusjuansalgado Date: Thu, 2 Oct 2025 17:15:31 +0100 Subject: [PATCH 9/9] Remove standard_id from bearer tokens and correcting note 2 about client_ids --- AuthVO.tex | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/AuthVO.tex b/AuthVO.tex index 8c9d596..394c52c 100644 --- a/AuthVO.tex +++ b/AuthVO.tex @@ -526,7 +526,6 @@ \subsubsection{\mbox{\tt ivoa\_bearer}}\label{sec: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}} @@ -537,7 +536,6 @@ \subsubsection{\mbox{\tt ivoa\_bearer}}\label{sec:ivoa-bearer} \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" @@ -607,7 +605,7 @@ \subsubsection{\mbox{\tt ivoa\_bearer}}\label{sec:ivoa-bearer} \begin{verbatim} HTTP/1.1 401 Unauthorized -WWW-Authenticate: ivoa_bearer standard_id="ivo://ivoa.net/sso#OAuth" +WWW-Authenticate: ivoa_bearer access_url="https://auth.example.org/.well-known/openid-configuration" X-VO-Auth-Error: expired_token \end{verbatim} @@ -622,9 +620,7 @@ \subsubsection{\mbox{\tt ivoa\_bearer}}\label{sec:ivoa-bearer} \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. +\textbf{Note 2:} These \verb|client_id|s are registered using dynamic client registration (see \ref{sec:ivoa-bearer-example}). } \end{description} @@ -1086,7 +1082,6 @@ \subsubsection{Before Execution} 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}