annotate mod_http_oauth2/mod_http_oauth2.lua @ 5119:048e339706ba

mod_rest: Remove manual reference expansion in schema This hack was originally added to reduce the number of definitions of common attributes (type, to, from etc) and payloads (e.g. delay). This predated pointers and references, and until now was needed because parsing picked out the correct stanza kind from the schema, which broke internal references. Removing this hack paves the way for allowing the schema to be configured or customized more easily.
author Kim Alvefur <zash@zash.se>
date Tue, 20 Dec 2022 21:48:28 +0100
parents 5dadbe0718f1
children 2c6acf2d6fd4
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
4263
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4260
diff changeset
1 local hashes = require "util.hashes";
4271
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4270
diff changeset
2 local cache = require "util.cache";
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
3 local http = require "util.http";
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
4 local jid = require "util.jid";
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
5 local json = require "util.json";
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
6 local usermanager = require "core.usermanager";
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
7 local errors = require "util.error";
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
8 local url = require "socket.url";
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
9 local uuid = require "util.uuid";
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
10 local encodings = require "util.encodings";
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
11 local base64 = encodings.base64;
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
12
3915
80dffbbd056b mod_rest, mod_http_oauth2: Switch from mod_authtokens to mod_tokenauth per Prosody bf81523e2ff4
Matthew Wild <mwild1@gmail.com>
parents: 3908
diff changeset
13 local tokens = module:depends("tokenauth");
3908
8ac5d9933106 mod_http_oauth2: Implement real tokens using mod_authtokens
Matthew Wild <mwild1@gmail.com>
parents: 3903
diff changeset
14
4260
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4259
diff changeset
15 local clients = module:open_store("oauth2_clients", "map");
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
16
4998
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4670
diff changeset
17 local function filter_scopes(username, host, requested_scope_string)
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4670
diff changeset
18 if host ~= module.host then
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4670
diff changeset
19 return usermanager.get_jid_role(username.."@"..host, module.host).name;
4340
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
20 end
4998
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4670
diff changeset
21
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4670
diff changeset
22 if requested_scope_string then -- Specific role requested
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4670
diff changeset
23 -- TODO: The requested scope string is technically a space-delimited list
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4670
diff changeset
24 -- of scopes, but for simplicity we're mapping this slot to role names.
5006
5dadbe0718f1 mod_http_oauth2: Update for new new role API
Matthew Wild <mwild1@gmail.com>
parents: 4998
diff changeset
25 if usermanager.user_can_assume_role(username, module.host, requested_scope_string) then
4998
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4670
diff changeset
26 return requested_scope_string;
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4670
diff changeset
27 end
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4670
diff changeset
28 end
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4670
diff changeset
29
5006
5dadbe0718f1 mod_http_oauth2: Update for new new role API
Matthew Wild <mwild1@gmail.com>
parents: 4998
diff changeset
30 return usermanager.get_user_role(username, module.host).name;
4340
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
31 end
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
32
4669
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4370
diff changeset
33 local function code_expires_in(code)
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4370
diff changeset
34 return os.difftime(os.time(), code.issued);
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4370
diff changeset
35 end
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4370
diff changeset
36
4269
143515d0b212 mod_http_oauth2: Factor out authorization code validity decision
Kim Alvefur <zash@zash.se>
parents: 4265
diff changeset
37 local function code_expired(code)
4669
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4370
diff changeset
38 return code_expires_in(code) > 120;
4269
143515d0b212 mod_http_oauth2: Factor out authorization code validity decision
Kim Alvefur <zash@zash.se>
parents: 4265
diff changeset
39 end
143515d0b212 mod_http_oauth2: Factor out authorization code validity decision
Kim Alvefur <zash@zash.se>
parents: 4265
diff changeset
40
4271
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4270
diff changeset
41 local codes = cache.new(10000, function (_, code)
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4270
diff changeset
42 return code_expired(code)
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4270
diff changeset
43 end);
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4270
diff changeset
44
4272
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4271
diff changeset
45 module:add_timer(900, function()
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4271
diff changeset
46 local k, code = codes:tail();
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4271
diff changeset
47 while code and code_expired(code) do
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4271
diff changeset
48 codes:set(k, nil);
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4271
diff changeset
49 k, code = codes:tail();
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4271
diff changeset
50 end
4669
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4370
diff changeset
51 return code and code_expires_in(code) + 1 or 900;
4272
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4271
diff changeset
52 end)
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4271
diff changeset
53
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
54 local function oauth_error(err_name, err_desc)
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
55 return errors.new({
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
56 type = "modify";
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
57 condition = "bad-request";
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
58 code = err_name == "invalid_client" and 401 or 400;
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
59 text = err_desc and (err_name..": "..err_desc) or err_name;
4276
ec33b3b1136c mod_http_oauth2: Fix passing OAuth-specific error details
Kim Alvefur <zash@zash.se>
parents: 4272
diff changeset
60 extra = { oauth2_response = { error = err_name, error_description = err_desc } };
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
61 });
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
62 end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
63
3918
dea6bea2ddd3 mod_http_oauth2: Refactor re-joining of JID out of token constructor
Kim Alvefur <zash@zash.se>
parents: 3915
diff changeset
64 local function new_access_token(token_jid, scope, ttl)
3908
8ac5d9933106 mod_http_oauth2: Implement real tokens using mod_authtokens
Matthew Wild <mwild1@gmail.com>
parents: 3903
diff changeset
65 local token = tokens.create_jid_token(token_jid, token_jid, scope, ttl);
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
66 return {
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
67 token_type = "bearer";
3908
8ac5d9933106 mod_http_oauth2: Implement real tokens using mod_authtokens
Matthew Wild <mwild1@gmail.com>
parents: 3903
diff changeset
68 access_token = token;
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
69 expires_in = ttl;
4340
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
70 scope = scope;
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
71 -- TODO: include refresh_token when implemented
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
72 };
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
73 end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
74
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
75 local grant_type_handlers = {};
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
76 local response_type_handlers = {};
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
77
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
78 function grant_type_handlers.password(params)
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
79 local request_jid = assert(params.username, oauth_error("invalid_request", "missing 'username' (JID)"));
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
80 local request_password = assert(params.password, oauth_error("invalid_request", "missing 'password'"));
3919
8ed261a08a9c mod_http_oauth2: Allow creation of full JID tokens
Kim Alvefur <zash@zash.se>
parents: 3918
diff changeset
81 local request_username, request_host, request_resource = jid.prepped_split(request_jid);
4340
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
82
3908
8ac5d9933106 mod_http_oauth2: Implement real tokens using mod_authtokens
Matthew Wild <mwild1@gmail.com>
parents: 3903
diff changeset
83 if not (request_username and request_host) or request_host ~= module.host then
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
84 return oauth_error("invalid_request", "invalid JID");
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
85 end
4340
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
86 if not usermanager.test_password(request_username, request_host, request_password) then
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
87 return oauth_error("invalid_grant", "incorrect credentials");
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
88 end
4340
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
89
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
90 local granted_jid = jid.join(request_username, request_host, request_resource);
4998
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4670
diff changeset
91 local granted_scopes = filter_scopes(request_username, request_host, params.scope);
4340
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
92 return json.encode(new_access_token(granted_jid, granted_scopes, nil));
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
93 end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
94
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
95 function response_type_handlers.code(params, granted_jid)
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
96 if not params.client_id then return oauth_error("invalid_request", "missing 'client_id'"); end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
97 if not params.redirect_uri then return oauth_error("invalid_request", "missing 'redirect_uri'"); end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
98
4260
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4259
diff changeset
99 local client_owner, client_host, client_id = jid.prepped_split(params.client_id);
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4259
diff changeset
100 if client_host ~= module.host then
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4259
diff changeset
101 return oauth_error("invalid_client", "incorrect credentials");
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4259
diff changeset
102 end
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4259
diff changeset
103 local client, err = clients:get(client_owner, client_id);
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
104 if err then error(err); end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
105 if not client then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
106 return oauth_error("invalid_client", "incorrect credentials");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
107 end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
108
4998
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4670
diff changeset
109 local granted_scopes = filter_scopes(client_owner, client_host, params.scope);
4340
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
110
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
111 local code = uuid.generate();
4670
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4669
diff changeset
112 local ok = codes:set(params.client_id .. "#" .. code, {
4340
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
113 issued = os.time();
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
114 granted_jid = granted_jid;
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
115 granted_scopes = granted_scopes;
4670
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4669
diff changeset
116 });
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4669
diff changeset
117 if not ok then
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4669
diff changeset
118 return {status_code = 429};
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4669
diff changeset
119 end
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
120
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
121 local redirect = url.parse(params.redirect_uri);
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
122 local query = http.formdecode(redirect.query or "");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
123 if type(query) ~= "table" then query = {}; end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
124 table.insert(query, { name = "code", value = code })
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
125 if params.state then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
126 table.insert(query, { name = "state", value = params.state });
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
127 end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
128 redirect.query = http.formencode(query);
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
129
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
130 return {
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
131 status_code = 302;
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
132 headers = {
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
133 location = url.build(redirect);
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
134 };
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
135 }
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
136 end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
137
4263
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4260
diff changeset
138 local pepper = module:get_option_string("oauth2_client_pepper", "");
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4260
diff changeset
139
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4260
diff changeset
140 local function verify_secret(stored, salt, i, secret)
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4260
diff changeset
141 return base64.decode(stored) == hashes.pbkdf2_hmac_sha256(secret, salt .. pepper, i);
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4260
diff changeset
142 end
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4260
diff changeset
143
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
144 function grant_type_handlers.authorization_code(params)
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
145 if not params.client_id then return oauth_error("invalid_request", "missing 'client_id'"); end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
146 if not params.client_secret then return oauth_error("invalid_request", "missing 'client_secret'"); end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
147 if not params.code then return oauth_error("invalid_request", "missing 'code'"); end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
148 if params.scope and params.scope ~= "" then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
149 return oauth_error("invalid_scope", "unknown scope requested");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
150 end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
151
4260
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4259
diff changeset
152 local client_owner, client_host, client_id = jid.prepped_split(params.client_id);
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4259
diff changeset
153 if client_host ~= module.host then
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4259
diff changeset
154 module:log("debug", "%q ~= %q", client_host, module.host);
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
155 return oauth_error("invalid_client", "incorrect credentials");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
156 end
4260
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4259
diff changeset
157 local client, err = clients:get(client_owner, client_id);
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4259
diff changeset
158 if err then error(err); end
4263
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4260
diff changeset
159 if not client or not verify_secret(client.secret_hash, client.salt, client.iteration_count, params.client_secret) then
4260
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4259
diff changeset
160 module:log("debug", "client_secret mismatch");
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4259
diff changeset
161 return oauth_error("invalid_client", "incorrect credentials");
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4259
diff changeset
162 end
4271
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4270
diff changeset
163 local code, err = codes:get(params.client_id .. "#" .. params.code);
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
164 if err then error(err); end
4269
143515d0b212 mod_http_oauth2: Factor out authorization code validity decision
Kim Alvefur <zash@zash.se>
parents: 4265
diff changeset
165 if not code or type(code) ~= "table" or code_expired(code) then
4260
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4259
diff changeset
166 module:log("debug", "authorization_code invalid or expired: %q", code);
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
167 return oauth_error("invalid_client", "incorrect credentials");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
168 end
4260
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4259
diff changeset
169 assert(codes:set(client_owner, client_id .. "#" .. params.code, nil));
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
170
4340
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
171 return json.encode(new_access_token(code.granted_jid, code.granted_scopes, nil));
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
172 end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
173
4370
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
174 local function check_credentials(request, allow_token)
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
175 local auth_type, auth_data = string.match(request.headers.authorization, "^(%S+)%s(.+)$");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
176
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
177 if auth_type == "Basic" then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
178 local creds = base64.decode(auth_data);
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
179 if not creds then return false; end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
180 local username, password = string.match(creds, "^([^:]+):(.*)$");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
181 if not username then return false; end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
182 username, password = encodings.stringprep.nodeprep(username), encodings.stringprep.saslprep(password);
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
183 if not username then return false; end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
184 if not usermanager.test_password(username, module.host, password) then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
185 return false;
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
186 end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
187 return username;
4370
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
188 elseif auth_type == "Bearer" and allow_token then
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
189 local token_info = tokens.get_token_info(auth_data);
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
190 if not token_info or not token_info.session or token_info.session.host ~= module.host then
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
191 return false;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
192 end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
193 return token_info.session.username;
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
194 end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
195 return nil;
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
196 end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
197
3920
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
198 if module:get_host_type() == "component" then
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
199 local component_secret = assert(module:get_option_string("component_secret"), "'component_secret' is a required setting when loaded on a Component");
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
200
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
201 function grant_type_handlers.password(params)
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
202 local request_jid = assert(params.username, oauth_error("invalid_request", "missing 'username' (JID)"));
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
203 local request_password = assert(params.password, oauth_error("invalid_request", "missing 'password'"));
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
204 local request_username, request_host, request_resource = jid.prepped_split(request_jid);
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
205 if params.scope then
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
206 return oauth_error("invalid_scope", "unknown scope requested");
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
207 end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
208 if not request_host or request_host ~= module.host then
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
209 return oauth_error("invalid_request", "invalid JID");
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
210 end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
211 if request_password == component_secret then
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
212 local granted_jid = jid.join(request_username, request_host, request_resource);
4257
145e8e8a247a mod_http_oauth2: Fix incomplete function arity change in dea6bea2ddd3
Kim Alvefur <zash@zash.se>
parents: 4256
diff changeset
213 return json.encode(new_access_token(granted_jid, nil, nil));
3920
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
214 end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
215 return oauth_error("invalid_grant", "incorrect credentials");
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
216 end
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
217
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
218 -- TODO How would this make sense with components?
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
219 -- Have an admin authenticate maybe?
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
220 response_type_handlers.code = nil;
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
221 grant_type_handlers.authorization_code = nil;
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
222 check_credentials = function () return false end
3920
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
223 end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
224
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
225 function handle_token_grant(event)
3934
469408682152 mod_http_oauth2: Set content type on successful repsponses (fixes #1501)
Kim Alvefur <zash@zash.se>
parents: 3920
diff changeset
226 event.response.headers.content_type = "application/json";
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
227 local params = http.formdecode(event.request.body);
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
228 if not params then
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
229 return oauth_error("invalid_request");
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
230 end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
231 local grant_type = params.grant_type
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
232 local grant_handler = grant_type_handlers[grant_type];
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
233 if not grant_handler then
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
234 return oauth_error("unsupported_grant_type");
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
235 end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
236 return grant_handler(params);
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
237 end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
238
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
239 local function handle_authorization_request(event)
4258
cc712899becd mod_http_oauth2: Unpack event object to improve readability
Kim Alvefur <zash@zash.se>
parents: 4257
diff changeset
240 local request, response = event.request, event.response;
cc712899becd mod_http_oauth2: Unpack event object to improve readability
Kim Alvefur <zash@zash.se>
parents: 4257
diff changeset
241 if not request.headers.authorization then
cc712899becd mod_http_oauth2: Unpack event object to improve readability
Kim Alvefur <zash@zash.se>
parents: 4257
diff changeset
242 response.headers.www_authenticate = string.format("Basic realm=%q", module.host.."/"..module.name);
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
243 return 401;
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
244 end
4258
cc712899becd mod_http_oauth2: Unpack event object to improve readability
Kim Alvefur <zash@zash.se>
parents: 4257
diff changeset
245 local user = check_credentials(request);
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
246 if not user then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
247 return 401;
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
248 end
4265
7b4a73364363 mod_http_oauth2: Add TODO
Kim Alvefur <zash@zash.se>
parents: 4263
diff changeset
249 -- TODO ask user for consent here
4258
cc712899becd mod_http_oauth2: Unpack event object to improve readability
Kim Alvefur <zash@zash.se>
parents: 4257
diff changeset
250 if not request.url.query then
cc712899becd mod_http_oauth2: Unpack event object to improve readability
Kim Alvefur <zash@zash.se>
parents: 4257
diff changeset
251 response.headers.content_type = "application/json";
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
252 return oauth_error("invalid_request");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
253 end
4258
cc712899becd mod_http_oauth2: Unpack event object to improve readability
Kim Alvefur <zash@zash.se>
parents: 4257
diff changeset
254 local params = http.formdecode(request.url.query);
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
255 if not params then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
256 return oauth_error("invalid_request");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
257 end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
258 local response_type = params.response_type;
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
259 local response_handler = response_type_handlers[response_type];
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
260 if not response_handler then
4258
cc712899becd mod_http_oauth2: Unpack event object to improve readability
Kim Alvefur <zash@zash.se>
parents: 4257
diff changeset
261 response.headers.content_type = "application/json";
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
262 return oauth_error("unsupported_response_type");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
263 end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
264 return response_handler(params, jid.join(user, module.host));
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
265 end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
266
4370
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
267 local function handle_revocation_request(event)
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
268 local request, response = event.request, event.response;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
269 if not request.headers.authorization then
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
270 response.headers.www_authenticate = string.format("Basic realm=%q", module.host.."/"..module.name);
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
271 return 401;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
272 elseif request.headers.content_type ~= "application/x-www-form-urlencoded"
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
273 or not request.body or request.body == "" then
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
274 return 400;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
275 end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
276 local user = check_credentials(request, true);
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
277 if not user then
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
278 return 401;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
279 end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
280
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
281 local form_data = http.formdecode(event.request.body);
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
282 if not form_data or not form_data.token then
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
283 return 400;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
284 end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
285 local ok, err = tokens.revoke_token(form_data.token);
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
286 if not ok then
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
287 module:log("warn", "Unable to revoke token: %s", tostring(err));
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
288 return 500;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
289 end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
290 return 200;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
291 end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
292
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
293 module:depends("http");
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
294 module:provides("http", {
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
295 route = {
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
296 ["POST /token"] = handle_token_grant;
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
297 ["GET /authorize"] = handle_authorization_request;
4370
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
298 ["POST /revoke"] = handle_revocation_request;
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
299 };
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
300 });
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
301
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
302 local http_server = require "net.http.server";
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
303
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
304 module:hook_object_event(http_server, "http-error", function (event)
4276
ec33b3b1136c mod_http_oauth2: Fix passing OAuth-specific error details
Kim Alvefur <zash@zash.se>
parents: 4272
diff changeset
305 local oauth2_response = event.error and event.error.extra and event.error.extra.oauth2_response;
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
306 if not oauth2_response then
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
307 return;
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
308 end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
309 event.response.headers.content_type = "application/json";
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
310 event.response.status_code = event.error.code or 400;
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
311 return json.encode(oauth2_response);
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
312 end, 5);