| rev | line source | 
| jbe/bsw@20 | 1 --[[-- | 
| jbe/bsw@20 | 2 claimed_identifier,                        -- identifier owned by the user | 
| jbe/bsw@20 | 3 errmsg,                                    -- error message in case of failure | 
| jbe/bsw@20 | 4 errcode =                                  -- error code in case of failure (TODO: not implemented yet) | 
| jbe/bsw@20 | 5 auth.openid.verify( | 
| jbe/bsw@20 | 6   force_https              = force_https,  -- only allow https | 
| jbe/bsw@20 | 7   curl_options             = curl_options  -- options passed to "curl" binary, when performing discovery | 
| jbe/bsw@20 | 8 ) | 
| jbe/bsw@20 | 9 | 
| jbe/bsw@20 | 10 --]]-- | 
| jbe/bsw@20 | 11 | 
| jbe/bsw@20 | 12 function auth.openid.verify(args) | 
| jbe/bsw@20 | 13   local args = args or {} | 
| jbe/bsw@20 | 14   if cgi.params["openid.ns"] ~= "http://specs.openid.net/auth/2.0" then | 
| jbe/bsw@20 | 15     return nil, "No indirect OpenID 2.0 message received." | 
| jbe/bsw@20 | 16   end | 
| jbe/bsw@20 | 17   local mode = cgi.params["openid.mode"] | 
| jbe/bsw@20 | 18   if mode == "id_res" then | 
| jbe/bsw@20 | 19     local return_to_url = cgi.params["openid.return_to"] | 
| jbe/bsw@20 | 20     if not return_to_url then | 
| jbe/bsw@20 | 21       return nil, "No return_to URL received in answer." | 
| jbe/bsw@20 | 22     end | 
| jbe/bsw@20 | 23     if return_to_url ~= encode.url{ | 
| jbe/bsw@20 | 24       base   = request.get_absolute_baseurl(), | 
| jbe/bsw@20 | 25       module = request.get_module(), | 
| jbe/bsw@20 | 26       view   = request.get_view() | 
| jbe/bsw@20 | 27     } then | 
| jbe/bsw@20 | 28       return nil, "return_to URL not matching." | 
| jbe/bsw@20 | 29     end | 
| jbe/bsw@20 | 30     local discovery_args = table.new(args) | 
| jbe/bsw@20 | 31     local claimed_identifier = cgi.params["openid.claimed_id"] | 
| jbe/bsw@20 | 32     if not claimed_identifier then | 
| jbe/bsw@20 | 33       return nil, "No claimed identifier received." | 
| jbe/bsw@20 | 34     end | 
| jbe/bsw@20 | 35     local cropped_identifier = string.match(claimed_identifier, "[^#]*") | 
| jbe/bsw@20 | 36     local normalized_identifier = auth.openid._normalize_url( | 
| jbe/bsw@20 | 37       cropped_identifier | 
| jbe/bsw@20 | 38     ) | 
| jbe/bsw@20 | 39     if not normalized_identifier then | 
| jbe/bsw@20 | 40       return nil, "Claimed identifier could not be normalized." | 
| jbe/bsw@20 | 41     end | 
| jbe/bsw@20 | 42     if normalized_identifier ~= cropped_identifier then | 
| jbe/bsw@20 | 43       return nil, "Claimed identifier was not normalized." | 
| jbe/bsw@20 | 44     end | 
| jbe/bsw@20 | 45     discovery_args.user_supplied_identifier = cropped_identifier | 
| jbe/bsw@20 | 46     local dd, errmsg, errcode = auth.openid.discover(discovery_args) | 
| jbe/bsw@20 | 47     if not dd then | 
| jbe/bsw@20 | 48       return nil, errmsg, errcode | 
| jbe/bsw@20 | 49     end | 
| jbe/bsw@20 | 50     if not dd.claimed_identifier then | 
| jbe/bsw@20 | 51       return nil, "Identifier is an OpenID Provider." | 
| jbe/bsw@20 | 52     end | 
| jbe/bsw@20 | 53     if dd.claimed_identifier ~= cropped_identifier then | 
| jbe/bsw@20 | 54       return nil, "Claimed identifier does not match." | 
| jbe/bsw@20 | 55     end | 
| jbe/bsw@20 | 56     local nonce = cgi.params["openid.response_nonce"] | 
| jbe/bsw@20 | 57     if not nonce then | 
| jbe/bsw@20 | 58       return nil, "Did not receive a response nonce." | 
| jbe/bsw@20 | 59     end | 
| jbe/bsw@20 | 60     local year, month, day, hour, minute, second = string.match( | 
| jbe/bsw@20 | 61       nonce, | 
| jbe/bsw@20 | 62       "^([0-9][0-9][0-9][0-9])%-([0-9][0-9])%-([0-9][0-9])T([0-9][0-9]):([0-9][0-9]):([0-9][0-9])Z" | 
| jbe/bsw@20 | 63     ) | 
| jbe/bsw@20 | 64     if not year then | 
| jbe/bsw@20 | 65       return nil, "Response nonce did not contain a parsable date/time." | 
| jbe/bsw@20 | 66     end | 
| jbe/bsw@20 | 67     local ts = atom.timestamp{ | 
| jbe/bsw@20 | 68       year   = tonumber(year), | 
| jbe/bsw@20 | 69       month  = tonumber(month), | 
| jbe/bsw@20 | 70       day    = tonumber(day), | 
| jbe/bsw@20 | 71       hour   = tonumber(hour), | 
| jbe/bsw@20 | 72       minute = tonumber(minute), | 
| jbe/bsw@20 | 73       second = tonumber(second) | 
| jbe/bsw@20 | 74     } | 
| jbe/bsw@20 | 75     -- NOTE: 50 hours margin allows us to ignore time zone issues here: | 
| jbe/bsw@20 | 76     if math.abs(ts - atom.timestamp:get_current()) > 3600 * 50 then | 
| jbe/bsw@20 | 77       return nil, "Response nonce contains wrong time or local time is wrong." | 
| jbe/bsw@20 | 78     end | 
| jbe/bsw@20 | 79     local params = {} | 
| jbe/bsw@20 | 80     for key, value in pairs(cgi.params) do | 
| jbe/bsw@20 | 81       local trimmed_key = string.match(key, "^openid%.(.+)") | 
| jbe/bsw@20 | 82       if trimmed_key then | 
| jbe/bsw@20 | 83         params[key] = value | 
| jbe/bsw@20 | 84       end | 
| jbe/bsw@20 | 85     end | 
| jbe/bsw@20 | 86     params["openid.mode"] = "check_authentication" | 
| jbe/bsw@20 | 87     local options = table.new(args.curl_options) | 
| jbe/bsw@20 | 88     for key, value in pairs(params) do | 
| jbe/bsw@20 | 89       options[#options+1] = "--data-urlencode" | 
| jbe/bsw@20 | 90       options[#options+1] = key .. "=" .. value | 
| jbe/bsw@20 | 91     end | 
| jbe/bsw@20 | 92     local status, headers, body = auth.openid._curl(dd.op_endpoint, options) | 
| jbe/bsw@20 | 93     if status ~= 200 then | 
| jbe/bsw@20 | 94       return nil, "Authorization could not be verified." | 
| jbe/bsw@20 | 95     end | 
| jbe/bsw@20 | 96     local result = {} | 
| jbe/bsw@20 | 97     for key, value in string.gmatch(body, "([^\n:]+):([^\n]*)") do | 
| jbe/bsw@20 | 98       result[key] = value | 
| jbe/bsw@20 | 99     end | 
| jbe/bsw@20 | 100     if result.ns ~= "http://specs.openid.net/auth/2.0" then | 
| jbe/bsw@20 | 101       return nil, "No OpenID 2.0 message replied." | 
| jbe/bsw@20 | 102     end | 
| jbe/bsw@20 | 103     if result.is_valid == "true" then | 
| jbe/bsw@20 | 104       return claimed_identifier | 
| jbe/bsw@20 | 105     else | 
| jbe/bsw@20 | 106       return nil, "Signature invalid." | 
| jbe/bsw@20 | 107     end | 
| jbe/bsw@20 | 108   elseif mode == "cancel" then | 
| jbe/bsw@20 | 109     return nil, "Authorization failed according to OpenID provider." | 
| jbe/bsw@20 | 110   else | 
| jbe/bsw@20 | 111     return nil, "Unexpected OpenID mode." | 
| jbe/bsw@20 | 112   end | 
| jbe/bsw@20 | 113 end |