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 tokens = module:depends("authtokens"); 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) local token_jid = jid.join(username, host); local token = tokens.create_jid_token(token_jid, token_jid, scope, ttl); return { token_type = "bearer"; access_token = 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 request_host ~= module.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);