annotate mod_auth_oauth_external/mod_auth_oauth_external.lua @ 5434:92ad8f03f225

mod_auth_oauth_external: Work without token validation endpoint In this mode, only PLAIN is possible and the provided username is assumed to be the XMPP localpart.
author Kim Alvefur <zash@zash.se>
date Mon, 08 May 2023 20:01:34 +0200
parents b40299bbdf14
children b3e7886fea6a
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
5344
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
1 local http = require "net.http";
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
2 local async = require "util.async";
5433
b40299bbdf14 mod_auth_oauth_external: Fix missing import of util.jid
Kim Alvefur <zash@zash.se>
parents: 5346
diff changeset
3 local jid = require "util.jid";
5344
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
4 local json = require "util.json";
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
5 local sasl = require "util.sasl";
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
6
5346
d9bc8712a745 mod_auth_oauth_external: Allow setting identity instead of discovery URL
Kim Alvefur <zash@zash.se>
parents: 5345
diff changeset
7 local issuer_identity = module:get_option_string("oauth_external_issuer");
d9bc8712a745 mod_auth_oauth_external: Allow setting identity instead of discovery URL
Kim Alvefur <zash@zash.se>
parents: 5345
diff changeset
8 local oidc_discovery_url = module:get_option_string("oauth_external_discovery_url",
d9bc8712a745 mod_auth_oauth_external: Allow setting identity instead of discovery URL
Kim Alvefur <zash@zash.se>
parents: 5345
diff changeset
9 issuer_identity and issuer_identity .. "/.well-known/oauth-authorization-server" or nil);
5344
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
10 local validation_endpoint = module:get_option_string("oauth_external_validation_endpoint");
5345
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
11 local token_endpoint = module:get_option_string("oauth_external_token_endpoint");
5344
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
12
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
13 local username_field = module:get_option_string("oauth_external_username_field", "preferred_username");
5345
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
14 local allow_plain = module:get_option_boolean("oauth_external_resource_owner_password", true);
5344
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
15
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
16 -- XXX Hold up, does whatever done here even need any of these things? Are we
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
17 -- the OAuth client? Is the XMPP client the OAuth client? What are we???
5345
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
18 local client_id = module:get_option_string("oauth_external_client_id");
5344
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
19 -- TODO -- local client_secret = module:get_option_string("oauth_external_client_secret");
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
20
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
21 --[[ More or less required endpoints
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
22 digraph "oauth endpoints" {
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
23 issuer -> discovery -> { registration validation }
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
24 registration -> { client_id client_secret }
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
25 { client_id client_secret validation } -> required
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
26 }
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
27 --]]
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
28
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
29 local host = module.host;
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
30 local provider = {};
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
31
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
32 function provider.get_sasl_handler()
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
33 local profile = {};
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
34 profile.http_client = http.default; -- TODO configurable
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
35 local extra = { oidc_discovery_url = oidc_discovery_url };
5345
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
36 if token_endpoint and allow_plain then
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
37 local map_username = function (username, _realm) return username; end; --jid.join; -- TODO configurable
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
38 function profile:plain_test(username, password, realm)
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
39 local tok, err = async.wait_for(self.profile.http_client:request(token_endpoint, {
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
40 headers = { ["Content-Type"] = "application/x-www-form-urlencoded; charset=utf-8"; ["Accept"] = "application/json" };
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
41 body = http.formencode({
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
42 grant_type = "password";
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
43 client_id = client_id;
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
44 username = map_username(username, realm);
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
45 password = password;
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
46 scope = "openid";
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
47 });
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
48 }))
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
49 if err or not (tok.code >= 200 and tok.code < 300) then
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
50 return false, nil;
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
51 end
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
52 local token_resp = json.decode(tok.body);
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
53 if not token_resp or string.lower(token_resp.token_type or "") ~= "bearer" then
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
54 return false, nil;
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
55 end
5434
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
56 if not validation_endpoint then
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
57 -- We're not going to get more info, only the username
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
58 self.username = jid.escape(username);
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
59 self.token_info = token_resp;
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
60 return true, true;
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
61 end
5345
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
62 local ret, err = async.wait_for(self.profile.http_client:request(validation_endpoint,
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
63 { headers = { ["Authorization"] = "Bearer " .. token_resp.access_token; ["Accept"] = "application/json" } }));
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
64 if err then
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
65 return false, nil;
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
66 end
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
67 if not (ret.code >= 200 and ret.code < 300) then
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
68 return false, nil;
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
69 end
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
70 local response = json.decode(ret.body);
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
71 if type(response) ~= "table" or (response[username_field]) ~= username then
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
72 return false, nil, nil;
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
73 end
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
74 if response.jid then
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
75 self.username, self.realm, self.resource = jid.prepped_split(response.jid, true);
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
76 end
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
77 self.role = response.role;
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
78 self.token_info = response;
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
79 return true, true;
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
80 end
3390bb2f9f6c mod_auth_oauth_external: Support PLAIN via resource owner password grant
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
81 end
5434
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
82 if validation_endpoint then
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
83 function profile:oauthbearer(token)
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
84 if token == "" then
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
85 return false, nil, extra;
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
86 end
5344
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
87
5434
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
88 local ret, err = async.wait_for(self.profile.http_client:request(validation_endpoint, {
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
89 headers = { ["Authorization"] = "Bearer " .. token; ["Accept"] = "application/json" };
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
90 }));
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
91 if err then
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
92 return false, nil, extra;
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
93 end
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
94 local response = ret and json.decode(ret.body);
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
95 if not (ret.code >= 200 and ret.code < 300) then
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
96 return false, nil, response or extra;
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
97 end
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
98 if type(response) ~= "table" or type(response[username_field]) ~= "string" then
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
99 return false, nil, nil;
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
100 end
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
101
92ad8f03f225 mod_auth_oauth_external: Work without token validation endpoint
Kim Alvefur <zash@zash.se>
parents: 5433
diff changeset
102 return response[username_field], true, response;
5344
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
103 end
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
104 end
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
105 return sasl.new(host, profile);
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
106 end
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
107
0a6d2b79a8bf mod_auth_oauth_external: Authenticate against an OAuth 2 provider
Kim Alvefur <zash@zash.se>
parents:
diff changeset
108 module:provides("auth", provider);