class Gem::WebauthnListener

The WebauthnListener class retrieves an OTP after a user successfully WebAuthns with the Gem host. An instance opens a socket using the TCPServer instance given and listens for a request from the Gem host. The request should be a GET request to the root path and contains the OTP code in the form of a query parameter ‘code`. The listener will return the code which will be used as the OTP for API requests.

Types of responses sent by the listener after receiving a request:

- 200 OK: OTP code was successfully retrieved
- 204 No Content: If the request was an OPTIONS request
- 400 Bad Request: If the request did not contain a query parameter `code`
- 404 Not Found: The request was not to the root path
- 405 Method Not Allowed: OTP code was not retrieved because the request was not a GET/OPTIONS request

Example usage:

server = TCPServer.new(0)
otp = Gem::WebauthnListener.wait_for_otp_code("https://rubygems.example", server)

The WebauthnListener Response class is used by the WebauthnListener to create responses to be sent to the Gem host. It creates a Net::HTTPResponse instance when initialized and can be converted to the appropriate format to be sent by a socket using ‘to_s`. Net::HTTPResponse instances cannot be directly sent over a socket.

Types of response classes:

- OkResponse
- NoContentResponse
- BadRequestResponse
- NotFoundResponse
- MethodNotAllowedResponse

Example usage:

server = TCPServer.new(0)
socket = server.accept

response = OkResponse.for("https://rubygems.example")
socket.print response.to_s
socket.close

Attributes

host[R]

Public Class Methods

new(host) click to toggle source
# File rubygems/webauthn_listener.rb, line 28
def initialize(host)
  @host = host
end
wait_for_otp_code(host, server) click to toggle source
# File rubygems/webauthn_listener.rb, line 32
def self.wait_for_otp_code(host, server)
  new(host).fetch_otp_from_connection(server)
end

Public Instance Methods

fetch_otp_from_connection(server) click to toggle source
# File rubygems/webauthn_listener.rb, line 36
def fetch_otp_from_connection(server)
  loop do
    socket = server.accept
    request_line = socket.gets

    method, req_uri, _protocol = request_line.split(" ")
    req_uri = URI.parse(req_uri)

    responder = SocketResponder.new(socket)

    unless root_path?(req_uri)
      responder.send(NotFoundResponse.for(host))
      raise Gem::WebauthnVerificationError, "Page at #{req_uri.path} not found."
    end

    case method.upcase
    when "OPTIONS"
      responder.send(NoContentResponse.for(host))
      next # will be GET
    when "GET"
      if otp = parse_otp_from_uri(req_uri)
        responder.send(OkResponse.for(host))
        return otp
      end
      responder.send(BadRequestResponse.for(host))
      raise Gem::WebauthnVerificationError, "Did not receive OTP from #{host}."
    else
      responder.send(MethodNotAllowedResponse.for(host))
      raise Gem::WebauthnVerificationError, "Invalid HTTP method #{method.upcase} received."
    end
  end
end