class Gems::TrustedPublisherAuthenticator
Authenticator for trusted publishing
Exchanges an OIDC ID token for a RubyGems API key on first use and then authenticates requests with that key.
The ID token and the host it is exchanged with are what an authenticator is built with, beside the connection, request builder, redirect handler, and retry handler it sends the exchange with, which default to ones built for the authenticator: a client hands it the ones it makes its own requests with, so that the exchange is sent with the timeouts, proxy, and retries the client was configured with. The ID token is given to a client with the ‘id_token` option, and exchanged by hand with {API::APIKeyEndpoints#exchange_trusted_publisher_token}.
@api private
Attributes
The connection used for the token exchange @api private @return [Connection] the connection @example Get the connection
authenticator.connection
The path of the endpoint the ID token is exchanged with @api private @return [String] the path, which is the trusted publishing exchange unless an API key role is assumed @example Get the path of the exchange
authenticator.exchange_path
The host to exchange the token with @api private @return [String] the host, including scheme @example Get the host
authenticator.host
The OIDC ID token @api private @return [String] the OIDC ID token @example Get the ID token
authenticator.id_token
The redirect handler the token exchange is followed with @api private @return [RedirectHandler] the redirect handler @example Get the redirect handler
authenticator.redirect_handler
The request builder used for the token exchange @api private @return [RequestBuilder] the request builder @example Get the request builder
authenticator.request_builder
The retry handler the token exchange is sent again with @api private @return [RetryHandler] the retry handler @example Get the retry handler
authenticator.retry_handler
Public Class Methods
Initialize a new TrustedPublisherAuthenticator
The four that follow the ID token and the host are what the exchange is sent with, and default to ones built for the authenticator (see the class).
@api private @param id_token [String] the OIDC ID token @param host [String] the host to exchange the token with, including scheme @param connection [Connection] the connection used for the token exchange @param request_builder [RequestBuilder] the request builder used for the token exchange @param redirect_handler [RedirectHandler] the redirect handler the exchange is followed with @param retry_handler [RetryHandler] the retry handler the exchange is sent again with @param exchange_path [String] the path of the endpoint the token is exchanged with, which is the trusted
publishing exchange unless the token assumes an API key role
@return [TrustedPublisherAuthenticator] a new instance @example Create a trusted publisher authenticator
authenticator = Gems::TrustedPublisherAuthenticator.new(id_token: ENV.fetch("ID_TOKEN"))
# File lib/gems/trusted_publisher_authenticator.rb, line 101 def initialize(id_token:, host: Gems.default_host, connection: Connection.new, request_builder: RequestBuilder.new, redirect_handler: RedirectHandler.new(connection:, request_builder:), retry_handler: RetryHandler.new, exchange_path: EXCHANGE_TOKEN_PATH) @id_token = id_token @exchange_path = exchange_path @host = host @connection = connection @request_builder = request_builder @redirect_handler = redirect_handler @retry_handler = retry_handler @mutex = Mutex.new end
Public Instance Methods
Exchange the OIDC ID token for a RubyGems API key
The redirects of the exchange are followed as those of every other request are, so that a host that answers the endpoint with one is exchanged with rather than raising the {HTTPError} of the redirect. A redirect to another scheme, host, or port is followed without the ‘Accept` header of the exchange, as it is for a request of the client, and with the body only when the redirect preserves the method.
The token is exchanged once, under a lock, and the key it was exchanged for is returned from then on, since RubyGems.org refuses to exchange a token again: a caller exchanging the token itself while a request is being authenticated is given the key the request was authenticated with, whichever of them took the lock first.
@api private @return [APIKey] the exchanged API key, including its name, scopes, and expiry @raise [HTTPError] if the token exchange fails @example Exchange the ID token
authenticator.exchange_token!.expires_at
# File lib/gems/trusted_publisher_authenticator.rb, line 167 def exchange_token! @mutex.synchronize { @exchanged_key ||= exchange! } end
Generate the authentication headers for a request
Exchanges the ID token for an API key on first use, once even when requests are made concurrently, since RubyGems.org issues the key only once per token.
@api private @param _request [Net::HTTPRequest] the HTTP request @return [Hash{String => String}] the authentication headers with the exchanged API key @raise [HTTPError] if the token exchange fails @raise [InvalidResponse] if the response is not JSON @example Generate an authentication header
authenticator.header(request)
# File lib/gems/trusted_publisher_authenticator.rb, line 127 def header(_request) {AUTHENTICATION_HEADER => exchange_token!.key} end
Summarize the authenticator for the console
@api private @return [String] the summary, which includes the host but not the ID token or API key @example Inspect a trusted publisher authenticator
authenticator.inspect # => #<Gems::TrustedPublisherAuthenticator host="https://rubygems.org">
# File lib/gems/trusted_publisher_authenticator.rb, line 147 def inspect "#<#{self.class} host=#{host.inspect}>" end