#!/usr/bin/env ruby require "cgi" require "uri" require "pathname" require "webrick" include WEBrick # load the openid library, first trying rubygems begin require "openid" rescue LoadError require "rubygems" gem "ruby-openid" end ################ start config ########################## # use your desired store implementation here. store_dir = Pathname.new(Dir.tmpdir).join("openid-store") store = OpenID::FilesystemStore.new(store_dir) $host = "localhost" $port = 2000 ################ end config ############################ if $port.nil? $base_url = "http://#{$host}/" else $base_url = "http://#{$host}:#{$port}/" end # NOTE: Please note that a Hash is not a valid session storage type, it is just # used here to get something that works. In a production environment this # should be an object representing the CURRENT USER's session, NOT a global # hash. Every user visiting this running consumer.rb will write into this # same hash. $session = {} $trust_root = $base_url $consumer = OpenID::Consumer.new($session, store) server = HTTPServer.new(:Port=>$port) class SimpleServlet < HTTPServlet::AbstractServlet def do_GET(req, res) @req = req @res = res begin case req.path when "", "/", "/start" self.render when "/begin" self.do_begin when "/complete" self.do_complete when '/policy' self.do_policy else self.redirect(self.build_url("/")) end ensure @req = nil @res = nil end end def do_begin # First make sure the user entered something openid_url = @req.query.fetch("openid_url", "") if openid_url.empty? self.render("Enter an identity URL to verify", css_class="error", form_contents=openid_url) return HTTPStatus::Success end # Then ask the openid library to begin the authorization request = $consumer.begin(openid_url) # If the URL was unusable (either because of network conditions, # a server error, or that the response returned was not an OpenID # identity page), the library will return HTTP_FAILURE or PARSE_ERROR. # Let the user know that the URL is unusable. case request.status when OpenID::FAILURE self.render("Unable to find openid server for #{openid_url}", css_class="error", form_contents=openid_url) return HTTPStatus::Success when OpenID::SUCCESS # The URL was a valid identity URL. Now we just need to send a redirect # to the server using the redirect_url the library created for us. # check to see if we want to make an SREG request. Generally this will # not take the form of a checkbox, but will be part of your site policy. # For example, you may perform an sreg request if the user appears # to be new to the site. The checkbox is here for convenience of # testing. do_sreg = @req.query.fetch('sreg', nil) if do_sreg policy_url = self.build_url('/policy') request.add_extension_arg('sreg','policy_url', policy_url) request.add_extension_arg('sreg','required','email,nickname') request.add_extension_arg('sreg','optional','fullname,dob,gender,postcode,country') end if do_sreg extra = {'did_sreg' => 'true'} else extra = {} end return_to = self.build_url("/complete", extra) # build the redirect redirect_url = request.redirect_url($trust_root, return_to) # send redirect to the server self.redirect(redirect_url) else # Should never get here raise "Not Reached" end end # handle the redirect from the OpenID server def do_complete # Ask the library to check the response that the server sent # us. Status is a code indicating the response type. info is # either nil or a string containing more information about # the return type. response = $consumer.complete(@req.query) css_class = "error" did_sreg = @req.query.fetch('did_sreg', nil) sreg_checked = did_sreg ? 'checked="checked"' : '' if response.status == OpenID::FAILURE # In the case of failure, if info is non-nil, it is the # URL that we were verifying. We include it in the error # message to help the user figure out what happened. if response.identity_url message = "Verification of #{response.identity_url} failed" else message = 'Verification failed.' end # add on the failure message for a little debug info message += ' '+response.msg.to_s elsif response.status == OpenID::SUCCESS # Success means that the transaction completed without # error. If info is nil, it means that the user cancelled # the verification. css_class = "alert" message = "You have successfully verified #{response.identity_url} as your identity." # get the signed extension sreg arguments sreg = response.extension_response('sreg') if sreg.length > 0 message += "
With simple registration fields:
" sreg.keys.sort.each {|k| message += "
#{k}: #{sreg[k]}"} elsif did_sreg message += "
But the server does not support simple registration." end elsif response.status == OpenID::CANCEL message = "Verification cancelled." else message = "Unknown response status: #{response.status}" end self.render(message, css_class, response.identity_url, sreg_checked) end def do_policy @res.body = <

Ruby Consumer Simple Registration Policy

This consumer makes a simple registration request for the following fields:

Required: email, nickname
Optional: fullname, dob, gender, postcode, country

Nothing is actually done with the data provided, it simply exists to illustrate the simple registration protocol.

END end # build a URL relative to the server base URL, with the given query # parameters added. def build_url(action, query=nil) url = URI.parse($base_url).merge(action).to_s url = OpenID::Util.append_args(url, query) unless query.nil? return url end def redirect(url) @res.set_redirect(HTTPStatus::TemporaryRedirect, url) end def render(message=nil, css_class="alert", form_contents="", checked="") @res.body = self.page_header unless message.nil? @res.body << "
#{message}
" end @res.body << self.page_footer(form_contents, checked) end def page_header(title="Ruby OpenID WEBrick example") header = < #{title}

#{title}

This example consumer uses the Ruby OpenID library on a WEBrick platform. The example just verifies that the URL that you enter is your identity URL.

END_OF_STRING end def page_footer(form_contents="", checked="") form_contents = "" if form_contents == "/" footer = <
Identity URL: ?
END_OF_STRING end end # Bootstrap the example server.mount("/", SimpleServlet) trap("INT") {server.shutdown} print "\nVisit http://#{$host}:#{$port}/ in your browser.\n\n" server.start