Comparison

mod_auth_oauth_external/mod_auth_oauth_external.lua @ 5673:0eb2d5ea2428

merge
author Stephen Paul Weber <singpolyma@singpolyma.net>
date Sat, 06 May 2023 19:40:23 -0500
parent 5346:d9bc8712a745
child 5433:b40299bbdf14
comparison
equal deleted inserted replaced
5672:2c69577b28c2 5673:0eb2d5ea2428
1 local http = require "net.http";
2 local async = require "util.async";
3 local json = require "util.json";
4 local sasl = require "util.sasl";
5
6 local issuer_identity = module:get_option_string("oauth_external_issuer");
7 local oidc_discovery_url = module:get_option_string("oauth_external_discovery_url",
8 issuer_identity and issuer_identity .. "/.well-known/oauth-authorization-server" or nil);
9 local validation_endpoint = module:get_option_string("oauth_external_validation_endpoint");
10 local token_endpoint = module:get_option_string("oauth_external_token_endpoint");
11
12 local username_field = module:get_option_string("oauth_external_username_field", "preferred_username");
13 local allow_plain = module:get_option_boolean("oauth_external_resource_owner_password", true);
14
15 -- XXX Hold up, does whatever done here even need any of these things? Are we
16 -- the OAuth client? Is the XMPP client the OAuth client? What are we???
17 local client_id = module:get_option_string("oauth_external_client_id");
18 -- TODO -- local client_secret = module:get_option_string("oauth_external_client_secret");
19
20 --[[ More or less required endpoints
21 digraph "oauth endpoints" {
22 issuer -> discovery -> { registration validation }
23 registration -> { client_id client_secret }
24 { client_id client_secret validation } -> required
25 }
26 --]]
27
28 local host = module.host;
29 local provider = {};
30
31 function provider.get_sasl_handler()
32 local profile = {};
33 profile.http_client = http.default; -- TODO configurable
34 local extra = { oidc_discovery_url = oidc_discovery_url };
35 if token_endpoint and allow_plain then
36 local map_username = function (username, _realm) return username; end; --jid.join; -- TODO configurable
37 function profile:plain_test(username, password, realm)
38 local tok, err = async.wait_for(self.profile.http_client:request(token_endpoint, {
39 headers = { ["Content-Type"] = "application/x-www-form-urlencoded; charset=utf-8"; ["Accept"] = "application/json" };
40 body = http.formencode({
41 grant_type = "password";
42 client_id = client_id;
43 username = map_username(username, realm);
44 password = password;
45 scope = "openid";
46 });
47 }))
48 if err or not (tok.code >= 200 and tok.code < 300) then
49 return false, nil;
50 end
51 local token_resp = json.decode(tok.body);
52 if not token_resp or string.lower(token_resp.token_type or "") ~= "bearer" then
53 return false, nil;
54 end
55 local ret, err = async.wait_for(self.profile.http_client:request(validation_endpoint,
56 { headers = { ["Authorization"] = "Bearer " .. token_resp.access_token; ["Accept"] = "application/json" } }));
57 if err then
58 return false, nil;
59 end
60 if not (ret.code >= 200 and ret.code < 300) then
61 return false, nil;
62 end
63 local response = json.decode(ret.body);
64 if type(response) ~= "table" or (response[username_field]) ~= username then
65 return false, nil, nil;
66 end
67 if response.jid then
68 self.username, self.realm, self.resource = jid.prepped_split(response.jid, true);
69 end
70 self.role = response.role;
71 self.token_info = response;
72 return true, true;
73 end
74 end
75 function profile:oauthbearer(token)
76 if token == "" then
77 return false, nil, extra;
78 end
79
80 local ret, err = async.wait_for(self.profile.http_client:request(validation_endpoint,
81 { headers = { ["Authorization"] = "Bearer " .. token; ["Accept"] = "application/json" } }));
82 if err then
83 return false, nil, extra;
84 end
85 local response = ret and json.decode(ret.body);
86 if not (ret.code >= 200 and ret.code < 300) then
87 return false, nil, response or extra;
88 end
89 if type(response) ~= "table" or type(response[username_field]) ~= "string" then
90 return false, nil, nil;
91 end
92
93 return response[username_field], true, response;
94 end
95 return sasl.new(host, profile);
96 end
97
98 module:provides("auth", provider);