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@223
|
14 if request.get_param{name="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@223
|
17 local mode = request.get_param{name="openid.mode"}
|
jbe/bsw@20
|
18 if mode == "id_res" then
|
jbe@223
|
19 local return_to_url = request.get_param{name="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@223
|
31 local claimed_identifier = request.get_param{name="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@223
|
56 local nonce = request.get_param{name="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
|