webmcp

diff framework/env/auth/openid/verify.lua @ 20:47ddf0f86009

OpenID 2.0 Relying Party support
author jbe/bsw
date Fri Apr 02 02:11:32 2010 +0200 (2010-04-02)
parents
children 32ec28229bb5
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/framework/env/auth/openid/verify.lua	Fri Apr 02 02:11:32 2010 +0200
     1.3 @@ -0,0 +1,113 @@
     1.4 +--[[--
     1.5 +claimed_identifier,                        -- identifier owned by the user
     1.6 +errmsg,                                    -- error message in case of failure
     1.7 +errcode =                                  -- error code in case of failure (TODO: not implemented yet)
     1.8 +auth.openid.verify(
     1.9 +  force_https              = force_https,  -- only allow https
    1.10 +  curl_options             = curl_options  -- options passed to "curl" binary, when performing discovery
    1.11 +)
    1.12 +
    1.13 +--]]--
    1.14 +
    1.15 +function auth.openid.verify(args)
    1.16 +  local args = args or {}
    1.17 +  if cgi.params["openid.ns"] ~= "http://specs.openid.net/auth/2.0" then
    1.18 +    return nil, "No indirect OpenID 2.0 message received."
    1.19 +  end
    1.20 +  local mode = cgi.params["openid.mode"]
    1.21 +  if mode == "id_res" then
    1.22 +    local return_to_url = cgi.params["openid.return_to"]
    1.23 +    if not return_to_url then
    1.24 +      return nil, "No return_to URL received in answer."
    1.25 +    end
    1.26 +    if return_to_url ~= encode.url{
    1.27 +      base   = request.get_absolute_baseurl(),
    1.28 +      module = request.get_module(),
    1.29 +      view   = request.get_view()
    1.30 +    } then
    1.31 +      return nil, "return_to URL not matching."
    1.32 +    end
    1.33 +    local discovery_args = table.new(args)
    1.34 +    local claimed_identifier = cgi.params["openid.claimed_id"]
    1.35 +    if not claimed_identifier then
    1.36 +      return nil, "No claimed identifier received."
    1.37 +    end
    1.38 +    local cropped_identifier = string.match(claimed_identifier, "[^#]*")
    1.39 +    local normalized_identifier = auth.openid._normalize_url(
    1.40 +      cropped_identifier
    1.41 +    )
    1.42 +    if not normalized_identifier then
    1.43 +      return nil, "Claimed identifier could not be normalized."
    1.44 +    end
    1.45 +    if normalized_identifier ~= cropped_identifier then
    1.46 +      return nil, "Claimed identifier was not normalized."
    1.47 +    end
    1.48 +    discovery_args.user_supplied_identifier = cropped_identifier
    1.49 +    local dd, errmsg, errcode = auth.openid.discover(discovery_args)
    1.50 +    if not dd then
    1.51 +      return nil, errmsg, errcode
    1.52 +    end
    1.53 +    if not dd.claimed_identifier then
    1.54 +      return nil, "Identifier is an OpenID Provider."
    1.55 +    end
    1.56 +    if dd.claimed_identifier ~= cropped_identifier then
    1.57 +      return nil, "Claimed identifier does not match."
    1.58 +    end
    1.59 +    local nonce = cgi.params["openid.response_nonce"]
    1.60 +    if not nonce then
    1.61 +      return nil, "Did not receive a response nonce."
    1.62 +    end 
    1.63 +    local year, month, day, hour, minute, second = string.match(
    1.64 +      nonce,
    1.65 +      "^([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"
    1.66 +    )
    1.67 +    if not year then
    1.68 +      return nil, "Response nonce did not contain a parsable date/time."
    1.69 +    end
    1.70 +    local ts = atom.timestamp{
    1.71 +      year   = tonumber(year),
    1.72 +      month  = tonumber(month),
    1.73 +      day    = tonumber(day),
    1.74 +      hour   = tonumber(hour),
    1.75 +      minute = tonumber(minute),
    1.76 +      second = tonumber(second)
    1.77 +    }
    1.78 +    -- NOTE: 50 hours margin allows us to ignore time zone issues here:
    1.79 +    if math.abs(ts - atom.timestamp:get_current()) > 3600 * 50 then
    1.80 +      return nil, "Response nonce contains wrong time or local time is wrong."
    1.81 +    end
    1.82 +    local params = {}
    1.83 +    for key, value in pairs(cgi.params) do
    1.84 +      local trimmed_key = string.match(key, "^openid%.(.+)")
    1.85 +      if trimmed_key then
    1.86 +        params[key] = value
    1.87 +      end
    1.88 +    end
    1.89 +    params["openid.mode"] = "check_authentication"
    1.90 +    local options = table.new(args.curl_options)
    1.91 +    for key, value in pairs(params) do
    1.92 +      options[#options+1] = "--data-urlencode"
    1.93 +      options[#options+1] = key .. "=" .. value
    1.94 +    end
    1.95 +    local status, headers, body = auth.openid._curl(dd.op_endpoint, options)
    1.96 +    if status ~= 200 then
    1.97 +      return nil, "Authorization could not be verified."
    1.98 +    end
    1.99 +    local result = {}
   1.100 +    for key, value in string.gmatch(body, "([^\n:]+):([^\n]*)") do
   1.101 +      result[key] = value
   1.102 +    end
   1.103 +    if result.ns ~= "http://specs.openid.net/auth/2.0" then
   1.104 +      return nil, "No OpenID 2.0 message replied."
   1.105 +    end
   1.106 +    if result.is_valid == "true" then
   1.107 +      return claimed_identifier
   1.108 +    else
   1.109 +      return nil, "Signature invalid."
   1.110 +    end
   1.111 +  elseif mode == "cancel" then
   1.112 +    return nil, "Authorization failed according to OpenID provider."
   1.113 +  else
   1.114 +    return nil, "Unexpected OpenID mode."
   1.115 +  end
   1.116 +end

Impressum / About Us