mod_http_oauth2/mod_http_oauth2.lua
author Matthew Wild <mwild1@gmail.com>
Sat, 24 Sep 2022 09:26:26 +0100
changeset 5063 5f1120c284c5
parent 5010 5dadbe0718f1
child 5185 2c6acf2d6fd4
permissions -rw-r--r--
mod_cloud_notify_extensions: Add note about dependency Noting here because people might not click through to see it on the mod_cloud_notify_encrypted page.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
4267
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4264
diff changeset
     1
local hashes = require "util.hashes";
4275
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4274
diff changeset
     2
local cache = require "util.cache";
3907
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";
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
     8
local url = require "socket.url";
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
     9
local uuid = require "util.uuid";
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
    10
local encodings = require "util.encodings";
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
    11
local base64 = encodings.base64;
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    12
3919
80dffbbd056b mod_rest, mod_http_oauth2: Switch from mod_authtokens to mod_tokenauth per Prosody bf81523e2ff4
Matthew Wild <mwild1@gmail.com>
parents: 3912
diff changeset
    13
local tokens = module:depends("tokenauth");
3912
8ac5d9933106 mod_http_oauth2: Implement real tokens using mod_authtokens
Matthew Wild <mwild1@gmail.com>
parents: 3907
diff changeset
    14
4264
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4263
diff changeset
    15
local clients = module:open_store("oauth2_clients", "map");
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
    16
5002
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4674
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: 4674
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: 4674
diff changeset
    19
		return usermanager.get_jid_role(username.."@"..host, module.host).name;
4344
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
    20
	end
5002
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4674
diff changeset
    21
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4674
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: 4674
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: 4674
diff changeset
    24
		-- of scopes, but for simplicity we're mapping this slot to role names.
5010
5dadbe0718f1 mod_http_oauth2: Update for new new role API
Matthew Wild <mwild1@gmail.com>
parents: 5002
diff changeset
    25
		if usermanager.user_can_assume_role(username, module.host, requested_scope_string) then
5002
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4674
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: 4674
diff changeset
    27
		end
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4674
diff changeset
    28
	end
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4674
diff changeset
    29
5010
5dadbe0718f1 mod_http_oauth2: Update for new new role API
Matthew Wild <mwild1@gmail.com>
parents: 5002
diff changeset
    30
	return usermanager.get_user_role(username, module.host).name;
4344
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
    31
end
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
    32
4673
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4374
diff changeset
    33
local function code_expires_in(code)
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4374
diff changeset
    34
	return os.difftime(os.time(), code.issued);
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4374
diff changeset
    35
end
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4374
diff changeset
    36
4273
143515d0b212 mod_http_oauth2: Factor out authorization code validity decision
Kim Alvefur <zash@zash.se>
parents: 4269
diff changeset
    37
local function code_expired(code)
4673
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4374
diff changeset
    38
	return code_expires_in(code) > 120;
4273
143515d0b212 mod_http_oauth2: Factor out authorization code validity decision
Kim Alvefur <zash@zash.se>
parents: 4269
diff changeset
    39
end
143515d0b212 mod_http_oauth2: Factor out authorization code validity decision
Kim Alvefur <zash@zash.se>
parents: 4269
diff changeset
    40
4275
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4274
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: 4274
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: 4274
diff changeset
    43
end);
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4274
diff changeset
    44
4276
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
    45
module:add_timer(900, function()
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
    46
	local k, code = codes:tail();
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
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: 4275
diff changeset
    48
		codes:set(k, nil);
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
    49
		k, code = codes:tail();
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
    50
	end
4673
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4374
diff changeset
    51
	return code and code_expires_in(code) + 1 or 900;
4276
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
    52
end)
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
    53
3907
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;
4280
ec33b3b1136c mod_http_oauth2: Fix passing OAuth-specific error details
Kim Alvefur <zash@zash.se>
parents: 4276
diff changeset
    60
		extra = { oauth2_response = { error = err_name, error_description = err_desc } };
3907
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
3922
dea6bea2ddd3 mod_http_oauth2: Refactor re-joining of JID out of token constructor
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
    64
local function new_access_token(token_jid, scope, ttl)
3912
8ac5d9933106 mod_http_oauth2: Implement real tokens using mod_authtokens
Matthew Wild <mwild1@gmail.com>
parents: 3907
diff changeset
    65
	local token = tokens.create_jid_token(token_jid, token_jid, scope, ttl);
3907
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";
3912
8ac5d9933106 mod_http_oauth2: Implement real tokens using mod_authtokens
Matthew Wild <mwild1@gmail.com>
parents: 3907
diff changeset
    68
		access_token = token;
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    69
		expires_in = ttl;
4344
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
    70
		scope = scope;
3907
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 = {};
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
    76
local response_type_handlers = {};
3907
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'"));
3923
8ed261a08a9c mod_http_oauth2: Allow creation of full JID tokens
Kim Alvefur <zash@zash.se>
parents: 3922
diff changeset
    81
	local request_username, request_host, request_resource = jid.prepped_split(request_jid);
4344
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
    82
3912
8ac5d9933106 mod_http_oauth2: Implement real tokens using mod_authtokens
Matthew Wild <mwild1@gmail.com>
parents: 3907
diff changeset
    83
	if not (request_username and request_host) or request_host ~= module.host then
3907
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
4344
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
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: 4280
diff changeset
    87
		return oauth_error("invalid_grant", "incorrect credentials");
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    88
	end
4344
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
    89
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
    90
	local granted_jid = jid.join(request_username, request_host, request_resource);
5002
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4674
diff changeset
    91
	local granted_scopes = filter_scopes(request_username, request_host, params.scope);
4344
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
    92
	return json.encode(new_access_token(granted_jid, granted_scopes, nil));
3907
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
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
    95
function response_type_handlers.code(params, granted_jid)
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
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: 4241
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: 4241
diff changeset
    98
4264
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4263
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: 4263
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: 4263
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: 4263
diff changeset
   102
	end
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4263
diff changeset
   103
	local client, err = clients:get(client_owner, client_id);
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   104
	if err then error(err); end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   105
	if not client then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   106
		return oauth_error("invalid_client", "incorrect credentials");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   107
	end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   108
5002
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4674
diff changeset
   109
	local granted_scopes = filter_scopes(client_owner, client_host, params.scope);
4344
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   110
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   111
	local code = uuid.generate();
4674
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4673
diff changeset
   112
	local ok = codes:set(params.client_id .. "#" .. code, {
4344
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   113
		issued = os.time();
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   114
		granted_jid = granted_jid;
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   115
		granted_scopes = granted_scopes;
4674
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4673
diff changeset
   116
	});
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4673
diff changeset
   117
	if not ok then
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4673
diff changeset
   118
		return {status_code = 429};
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4673
diff changeset
   119
	end
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   120
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   121
	local redirect = url.parse(params.redirect_uri);
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   122
	local query = http.formdecode(redirect.query or "");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   123
	if type(query) ~= "table" then query = {}; end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   124
	table.insert(query, { name = "code", value = code })
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   125
	if params.state then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   126
		table.insert(query, { name = "state", value = params.state });
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   127
	end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   128
	redirect.query = http.formencode(query);
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   129
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   130
	return {
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   131
		status_code = 302;
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   132
		headers = {
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   133
			location = url.build(redirect);
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   134
		};
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   135
	}
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   136
end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   137
4267
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4264
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: 4264
diff changeset
   139
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4264
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: 4264
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: 4264
diff changeset
   142
end
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4264
diff changeset
   143
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   144
function grant_type_handlers.authorization_code(params)
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
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: 4241
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: 4241
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: 4241
diff changeset
   148
	if params.scope and params.scope ~= "" then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   149
		return oauth_error("invalid_scope", "unknown scope requested");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   150
	end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   151
4264
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4263
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: 4263
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: 4263
diff changeset
   154
		module:log("debug", "%q ~= %q", client_host, module.host);
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   155
		return oauth_error("invalid_client", "incorrect credentials");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   156
	end
4264
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4263
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: 4263
diff changeset
   158
	if err then error(err); end
4267
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4264
diff changeset
   159
	if not client or not verify_secret(client.secret_hash, client.salt, client.iteration_count, params.client_secret) then
4264
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4263
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: 4263
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: 4263
diff changeset
   162
	end
4275
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4274
diff changeset
   163
	local code, err = codes:get(params.client_id .. "#" .. params.code);
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   164
	if err then error(err); end
4273
143515d0b212 mod_http_oauth2: Factor out authorization code validity decision
Kim Alvefur <zash@zash.se>
parents: 4269
diff changeset
   165
	if not code or type(code) ~= "table" or code_expired(code) then
4264
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4263
diff changeset
   166
		module:log("debug", "authorization_code invalid or expired: %q", code);
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   167
		return oauth_error("invalid_client", "incorrect credentials");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   168
	end
4264
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4263
diff changeset
   169
	assert(codes:set(client_owner, client_id .. "#" .. params.code, nil));
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   170
4344
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   171
	return json.encode(new_access_token(code.granted_jid, code.granted_scopes, nil));
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   172
end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   173
4374
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   174
local function check_credentials(request, allow_token)
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
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: 4241
diff changeset
   176
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   177
	if auth_type == "Basic" then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   178
		local creds = base64.decode(auth_data);
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   179
		if not creds then return false; end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   180
		local username, password = string.match(creds, "^([^:]+):(.*)$");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   181
		if not username then return false; end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
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: 4241
diff changeset
   183
		if not username then return false; end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
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: 4241
diff changeset
   185
			return false;
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   186
		end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   187
		return username;
4374
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
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: 4344
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: 4344
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: 4344
diff changeset
   191
			return false;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   192
		end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   193
		return token_info.session.username;
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   194
	end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   195
	return nil;
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   196
end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   197
3924
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
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: 3923
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: 3923
diff changeset
   200
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
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: 3923
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: 3923
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: 3923
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: 3923
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: 3923
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: 3923
diff changeset
   207
		end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
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: 3923
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: 3923
diff changeset
   210
		end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
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: 3923
diff changeset
   212
			local granted_jid = jid.join(request_username, request_host, request_resource);
4261
145e8e8a247a mod_http_oauth2: Fix incomplete function arity change in dea6bea2ddd3
Kim Alvefur <zash@zash.se>
parents: 4260
diff changeset
   213
			return json.encode(new_access_token(granted_jid, nil, nil));
3924
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   214
		end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
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: 3923
diff changeset
   216
	end
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   217
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   218
	-- TODO How would this make sense with components?
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   219
	-- Have an admin authenticate maybe?
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   220
	response_type_handlers.code = nil;
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   221
	grant_type_handlers.authorization_code = nil;
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   222
	check_credentials = function () return false end
3924
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   223
end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   224
3907
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)
3938
469408682152 mod_http_oauth2: Set content type on successful repsponses (fixes #1501)
Kim Alvefur <zash@zash.se>
parents: 3924
diff changeset
   226
	event.response.headers.content_type = "application/json";
3907
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
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   239
local function handle_authorization_request(event)
4262
cc712899becd mod_http_oauth2: Unpack event object to improve readability
Kim Alvefur <zash@zash.se>
parents: 4261
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: 4261
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: 4261
diff changeset
   242
		response.headers.www_authenticate = string.format("Basic realm=%q", module.host.."/"..module.name);
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   243
		return 401;
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   244
	end
4262
cc712899becd mod_http_oauth2: Unpack event object to improve readability
Kim Alvefur <zash@zash.se>
parents: 4261
diff changeset
   245
	local user = check_credentials(request);
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   246
	if not user then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   247
		return 401;
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   248
	end
4269
7b4a73364363 mod_http_oauth2: Add TODO
Kim Alvefur <zash@zash.se>
parents: 4267
diff changeset
   249
	-- TODO ask user for consent here
4262
cc712899becd mod_http_oauth2: Unpack event object to improve readability
Kim Alvefur <zash@zash.se>
parents: 4261
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: 4261
diff changeset
   251
		response.headers.content_type = "application/json";
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   252
		return oauth_error("invalid_request");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   253
	end
4262
cc712899becd mod_http_oauth2: Unpack event object to improve readability
Kim Alvefur <zash@zash.se>
parents: 4261
diff changeset
   254
	local params = http.formdecode(request.url.query);
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   255
	if not params then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   256
		return oauth_error("invalid_request");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   257
	end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   258
	local response_type = params.response_type;
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   259
	local response_handler = response_type_handlers[response_type];
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   260
	if not response_handler then
4262
cc712899becd mod_http_oauth2: Unpack event object to improve readability
Kim Alvefur <zash@zash.se>
parents: 4261
diff changeset
   261
		response.headers.content_type = "application/json";
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   262
		return oauth_error("unsupported_response_type");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   263
	end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
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: 4241
diff changeset
   265
end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   266
4374
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
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: 4344
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: 4344
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: 4344
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: 4344
diff changeset
   271
		return 401;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
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: 4344
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: 4344
diff changeset
   274
		return 400;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   275
	end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
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: 4344
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: 4344
diff changeset
   278
		return 401;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   279
	end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   280
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
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: 4344
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: 4344
diff changeset
   283
		return 400;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   284
	end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
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: 4344
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: 4344
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: 4344
diff changeset
   288
		return 500;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   289
	end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   290
	return 200;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   291
end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   292
3907
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;
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   297
		["GET /authorize"] = handle_authorization_request;
4374
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   298
		["POST /revoke"] = handle_revocation_request;
3907
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)
4280
ec33b3b1136c mod_http_oauth2: Fix passing OAuth-specific error details
Kim Alvefur <zash@zash.se>
parents: 4276
diff changeset
   305
	local oauth2_response = event.error and event.error.extra and event.error.extra.oauth2_response;
3907
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);