diff options
Diffstat (limited to 'lib/rubygems/webauthn_listener.rb')
-rw-r--r-- | lib/rubygems/webauthn_listener.rb | 92 |
1 files changed, 0 insertions, 92 deletions
diff --git a/lib/rubygems/webauthn_listener.rb b/lib/rubygems/webauthn_listener.rb deleted file mode 100644 index 22f7ea2011..0000000000 --- a/lib/rubygems/webauthn_listener.rb +++ /dev/null @@ -1,92 +0,0 @@ -# frozen_string_literal: true - -require_relative "webauthn_listener/response" - -## -# 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) -# - -class Gem::WebauthnListener - attr_reader :host - - def initialize(host) - @host = host - end - - def self.wait_for_otp_code(host, server) - new(host).fetch_otp_from_connection(server) - end - - 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 - - private - - def root_path?(uri) - uri.path == "/" - end - - def parse_otp_from_uri(uri) - require "cgi" - - return if uri.query.nil? - CGI.parse(uri.query).dig("code", 0) - end - - class SocketResponder - def initialize(socket) - @socket = socket - end - - def send(response) - @socket.print response.to_s - @socket.close - end - end -end |