Changeset

3903:cfeb93b80621

mod_http_oauth2: OAuth2 API (work in progress for developers only)
author Matthew Wild <mwild1@gmail.com>
date Sun, 23 Feb 2020 21:36:53 +0000
parents 3902:341850e8866f
children 3904:d14fc974efbc
files mod_http_oauth2/README.markdown mod_http_oauth2/mod_http_oauth2.lua
diffstat 2 files changed, 96 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_http_oauth2/README.markdown	Sun Feb 23 21:36:53 2020 +0000
@@ -0,0 +1,20 @@
+---
+labels:
+- Stage-Alpha
+summary: 'OAuth2 API'
+...
+
+Introduction
+============
+
+This module is a work-in-progress intended for developers only!
+
+Configuration
+=============
+
+None currently.
+
+Compatibility
+=============
+
+Requires Prosody trunk.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_http_oauth2/mod_http_oauth2.lua	Sun Feb 23 21:36:53 2020 +0000
@@ -0,0 +1,76 @@
+module:set_global();
+
+local http = require "util.http";
+local jid = require "util.jid";
+local json = require "util.json";
+local usermanager = require "core.usermanager";
+local errors = require "util.error";
+
+local function oauth_error(err_name, err_desc)
+	return errors.new({
+		type = "modify";
+		condition = "bad-request";
+		code = err_name == "invalid_client" and 401 or 400;
+		text = err_desc and (err_name..": "..err_desc) or err_name;
+		context = { oauth2_response = { error = err_name, error_description = err_desc } };
+	});
+end
+
+local function new_access_token(username, host, scope, ttl)
+	return {
+		token_type = "bearer";
+		access_token = "test-token";
+		expires_in = ttl;
+		-- TODO: include refresh_token when implemented
+	};
+end
+
+local grant_type_handlers = {};
+
+function grant_type_handlers.password(params)
+	local request_jid = assert(params.username, oauth_error("invalid_request", "missing 'username' (JID)"));
+	local request_password = assert(params.password, oauth_error("invalid_request", "missing 'password'"));
+	local request_username, request_host = jid.prepped_split(request_jid);
+	if params.scope then
+		return oauth_error("invalid_scope", "unknown scope requested");
+	end
+	if not (request_username and request_host) or not (hosts[request_host]) then
+		return oauth_error("invalid_request", "invalid JID");
+	end
+	if usermanager.test_password(request_username, request_host, request_password) then
+		return json.encode(new_access_token(request_username, request_host, nil, nil));
+	end
+	return oauth_error("invalid_grant", "incorrect credentials");
+end
+
+function handle_token_grant(event)
+	local params = http.formdecode(event.request.body);
+	if not params then
+		return oauth_error("invalid_request");
+	end
+	local grant_type = params.grant_type
+	local grant_handler = grant_type_handlers[grant_type];
+	if not grant_handler then
+		return oauth_error("unsupported_grant_type");
+	end
+	return grant_handler(params);
+end
+
+module:depends("http");
+module:provides("http", {
+	route = {
+		["POST /token"] = handle_token_grant;
+	};
+});
+
+local http_server = require "net.http.server";
+
+module:hook_object_event(http_server, "http-error", function (event)
+	local oauth2_response = event.error and event.error.context and event.error.context.oauth2_response;
+	if not oauth2_response then
+		return;
+	end
+	event.response.headers.content_type = "application/json";
+	event.response.status_code = event.error.code or 400;
+	return json.encode(oauth2_response);
+end, 5);