File

util/sasl/oauthbearer.lua @ 12918:ed20555f163a

util.sasl.oauthbearer: Fix traceback on authz in unexpected format E.g. if you were to just pass "username" without @hostname, the split will return nil, "username" and the nil gets passed to saslprep() and it does not like that.
author Kim Alvefur <zash@zash.se>
date Thu, 02 Mar 2023 14:37:46 +0100
parent 12911:ab1164eda011
child 12936:b796e08e6376
line wrap: on
line source

local saslprep = require "util.encodings".stringprep.saslprep;
local nodeprep = require "util.encodings".stringprep.nodeprep;
local jid = require "util.jid";
local json = require "util.json";
local log = require "util.logger".init("sasl");
local _ENV = nil;


local function oauthbearer(self, message)
	if not message then
		return "failure", "malformed-request";
	end

	if message == "\001" then
		return "failure", "not-authorized";
	end

	local gs2_authzid, kvpairs = message:match("n,a=([^,]+),(.+)$");
	if not gs2_authzid then
		return "failure", "malformed-request";
	end

	local auth_header;
	for k, v in kvpairs:gmatch("([a-zA-Z]+)=([\033-\126 \009\r\n]*)\001") do
		if k == "auth" then
			auth_header = v;
			break;
		end
	end

	if not auth_header then
		return "failure", "malformed-request";
	end

	local username = jid.prepped_split(gs2_authzid);

	if not username or username == "" then
		return "failure", "malformed-request", "Expected authorization identity in the username@hostname format";
	end

	-- SASLprep username
	username = saslprep(username);

	if not username or username == "" then
		log("debug", "Username violates SASLprep.");
		return "failure", "malformed-request", "Invalid username.";
	end

	local _nodeprep = self.profile.nodeprep;
	if _nodeprep ~= false then
		username = (_nodeprep or nodeprep)(username);
		if not username or username == "" then
			return "failure", "malformed-request", "Invalid username or password."
		end
	end

	self.username = username;

	local token = auth_header:match("^Bearer (.+)$");

	local correct, state, token_info = self.profile.oauthbearer(self, username, token, self.realm);

	if state == false then
		return "failure", "account-disabled";
	elseif state == nil or not correct then
		-- For token-level errors, RFC 7628 demands use of a JSON-encoded
		-- challenge response upon failure. We relay additional info from
		-- the auth backend if available.
		return "challenge", json.encode({
			status = token_info and token_info.status or "invalid_token";
			scope = token_info and token_info.scope or nil;
			["openid-configuration"] = token_info and token_info.oidc_discovery_url or nil;
		});
	end

	self.resource = token_info.resource;
	self.role = token_info.role;
	return "success";
end

local function init(registerMechanism)
	registerMechanism("OAUTHBEARER", {"oauthbearer"}, oauthbearer);
end

return {
	init = init;
}